YAML Configurations Support (GSoC - 2020) (#543)

* Add ServiceDesciptorYaml.cs

* Add YamlConfigurations.cs
YmlDotNet library added

* Update YamlConfigurations.cs

* Add download configuration to YamlConfiguration

* Revert "Add download configuration to YamlConfiguration"

This reverts commit f150de13b0.

* Add  ServiceDescriptrYaml unit test
Update YamlConfigurations.

* Confid files seperated
Download class refactored
Unit test updated

* Change nullable attributes.

* Refactor IWinSWConfiguration and Logging settings propagation

* Update YamlConfigurations.cs

* Update configuration FailureActions

* Update Yaml Confifurations

* Update YAML Configuration

* Update yaml configurations

* Update Yaml Configurations

* Yaml Configuration refactored

* Refactored YamlConfigurations

* Update serviceaccount and log configs

* YAML_SC_ACTION method name channged

* Refacored Download class. Field names changed to PascalCase readonly

* Add seperate download class to YamlConfigurations and create and return List<Download>

* Created DefaultWinSWSettings singleton

* Refactor variable name

* Update StopExecutable

* Nullable references updated

* Null references updated

* Add sanity checks for yaml deserializing.

* Implement Log Defaults

* Call logdefaults in YAMLConfigurations

* Update defaults value of ServiceAccout

If serviceaccoutn is not specified default ServiceAccount object will be provided from the Defautls.

* Merge build.yml with master

* Update YamlSeriviceDescriptor

Remove invalid Name field from ServiceAccout
Add BaseName logics to defults
merge build.yml with master

* Update IWinSWConfiguration Support

Now can use any IWinSWConfiguration type instead of ServiceDescriptor. We can use both ServiceDescriptor or ServiceDescriptorYaml.

* Update LogMode unit test

* ServiceAccount configurations refactored

Get all ServiceAccount configuration into a single ServiceAccout class.

* Update default BasePath

* Resolve Merge conflicts

* Resolve Merge Conflicts

* Update YamlDownload configs

* Fix null reference issue in arguments

* Update ServiceAccount configs in XML ServiceDescriptor

* remove BOM header

* Update environment variable configurations

Co-authored-by: Oleg Nenashev <o.v.nenashev@gmail.com>
pull/590/head
Buddhika Chathuranga 2020-07-21 22:27:27 +05:30 committed by GitHub
parent ae10abe857
commit 10d3a6113f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2213 additions and 1230 deletions

View File

@ -18,9 +18,9 @@ using log4net.Appender;
using log4net.Config; using log4net.Config;
using log4net.Core; using log4net.Core;
using log4net.Layout; using log4net.Layout;
using WinSW.Configuration;
using WinSW.Logging; using WinSW.Logging;
using WinSW.Native; using WinSW.Native;
using WinSW.Util;
using WMI; using WMI;
using ServiceType = WMI.ServiceType; using ServiceType = WMI.ServiceType;
@ -59,7 +59,7 @@ namespace WinSW
} }
} }
public static void Run(string[] argsArray, ServiceDescriptor? descriptor = null) public static void Run(string[] argsArray, IWinSWConfiguration? descriptor = null)
{ {
bool inConsoleMode = argsArray.Length > 0; bool inConsoleMode = argsArray.Length > 0;
@ -181,7 +181,9 @@ namespace WinSW
default: default:
Console.WriteLine("Unknown command: " + args[0]); Console.WriteLine("Unknown command: " + args[0]);
PrintAvailableCommands(); PrintAvailableCommands();
#pragma warning disable S112 // General exceptions should never be thrown
throw new Exception("Unknown command: " + args[0]); throw new Exception("Unknown command: " + args[0]);
#pragma warning restore S112 // General exceptions should never be thrown
} }
void Install() void Install()
@ -199,7 +201,9 @@ namespace WinSW
{ {
Console.WriteLine("Service with id '" + descriptor.Id + "' already exists"); Console.WriteLine("Service with id '" + descriptor.Id + "' already exists");
Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file"); Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
#pragma warning disable S112 // General exceptions should never be thrown
throw new Exception("Installation failure: Service with id '" + descriptor.Id + "' already exists"); throw new Exception("Installation failure: Service with id '" + descriptor.Id + "' already exists");
#pragma warning restore S112 // General exceptions should never be thrown
} }
string? username = null; string? username = null;
@ -222,17 +226,17 @@ namespace WinSW
} }
else else
{ {
if (descriptor.HasServiceAccount()) if (descriptor.ServiceAccount.HasServiceAccount())
{ {
username = descriptor.ServiceAccountUser; username = descriptor.ServiceAccount.ServiceAccountUser;
password = descriptor.ServiceAccountPassword; password = descriptor.ServiceAccount.ServiceAccountPassword;
allowServiceLogonRight = descriptor.AllowServiceAcountLogonRight; allowServiceLogonRight = descriptor.ServiceAccount.AllowServiceAcountLogonRight;
} }
} }
if (allowServiceLogonRight) if (allowServiceLogonRight)
{ {
Security.AddServiceLogonRight(descriptor.ServiceAccountDomain!, descriptor.ServiceAccountName!); Security.AddServiceLogonRight(descriptor.ServiceAccount.ServiceAccountDomain!, descriptor.ServiceAccount.ServiceAccountName!);
} }
svcs.Create( svcs.Create(
@ -319,7 +323,7 @@ namespace WinSW
Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'"); Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'");
} }
throw e; throw;
} }
} }
@ -455,14 +459,18 @@ namespace WinSW
bool result = ProcessApis.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _); bool result = ProcessApis.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, ProcessApis.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _);
if (!result) if (!result)
{ {
#pragma warning disable S112 // General exceptions should never be thrown
throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error()); throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error());
#pragma warning restore S112 // General exceptions should never be thrown
} }
} }
void Status() void Status()
{ {
Log.Debug("User requested the status of the process with id '" + descriptor.Id + "'"); Log.Debug("User requested the status of the process with id '" + descriptor.Id + "'");
#pragma warning disable S3358 // Ternary operators should not be nested
Console.WriteLine(svc is null ? "NonExistent" : svc.Started ? "Started" : "Stopped"); Console.WriteLine(svc is null ? "NonExistent" : svc.Started ? "Started" : "Stopped");
#pragma warning restore S3358 // Ternary operators should not be nested
} }
void Test() void Test()
@ -532,7 +540,7 @@ namespace WinSW
[DoesNotReturn] [DoesNotReturn]
private static void ThrowNoSuchService() => throw new WmiException(ReturnValue.NoSuchService); private static void ThrowNoSuchService() => throw new WmiException(ReturnValue.NoSuchService);
private static void InitLoggers(ServiceDescriptor descriptor, bool enableConsoleLogging) private static void InitLoggers(IWinSWConfiguration descriptor, bool enableConsoleLogging)
{ {
// TODO: Make logging levels configurable // TODO: Make logging levels configurable
Level fileLogLevel = Level.Debug; Level fileLogLevel = Level.Debug;

View File

@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
#endif #endif
using log4net; using log4net;
using WinSW.Configuration;
using WinSW.Extensions; using WinSW.Extensions;
using WinSW.Logging; using WinSW.Logging;
using WinSW.Native; using WinSW.Native;
@ -20,9 +21,11 @@ namespace WinSW
public class WrapperService : ServiceBase, IEventLogger public class WrapperService : ServiceBase, IEventLogger
{ {
private ServiceApis.SERVICE_STATUS wrapperServiceStatus; private ServiceApis.SERVICE_STATUS wrapperServiceStatus;
private readonly Process process = new Process(); private readonly Process process = new Process();
private readonly ServiceDescriptor descriptor;
private readonly IWinSWConfiguration descriptor;
private Dictionary<string, string>? envs; private Dictionary<string, string>? envs;
internal WinSWExtensionManager ExtensionManager { get; private set; } internal WinSWExtensionManager ExtensionManager { get; private set; }
@ -55,7 +58,7 @@ namespace WinSW
/// </summary> /// </summary>
public bool IsShuttingDown => this.systemShuttingdown; public bool IsShuttingDown => this.systemShuttingdown;
public WrapperService(ServiceDescriptor descriptor) public WrapperService(IWinSWConfiguration descriptor)
{ {
this.descriptor = descriptor; this.descriptor = descriptor;
this.ServiceName = this.descriptor.Id; this.ServiceName = this.descriptor.Id;
@ -131,14 +134,15 @@ namespace WinSW
/// <returns>Log Handler, which should be used for the spawned process</returns> /// <returns>Log Handler, which should be used for the spawned process</returns>
private LogHandler CreateExecutableLogHandler() private LogHandler CreateExecutableLogHandler()
{ {
string logDirectory = this.descriptor.LogDirectory; string? logDirectory = this.descriptor.LogDirectory;
if (!Directory.Exists(logDirectory)) if (!Directory.Exists(logDirectory))
{ {
Directory.CreateDirectory(logDirectory); Directory.CreateDirectory(logDirectory);
} }
LogHandler logAppender = this.descriptor.LogHandler; LogHandler logAppender = this.descriptor.Log.CreateLogHandler();
logAppender.EventLogger = this; logAppender.EventLogger = this;
return logAppender; return logAppender;
} }

View File

@ -12,6 +12,8 @@ namespace WinSW.Configuration
/// </summary> /// </summary>
public sealed class DefaultWinSWSettings : IWinSWConfiguration public sealed class DefaultWinSWSettings : IWinSWConfiguration
{ {
public static LogDefaults DefaultLogSettings { get; } = new LogDefaults();
public string Id => throw new InvalidOperationException(nameof(this.Id) + " must be specified."); public string Id => throw new InvalidOperationException(nameof(this.Id) + " must be specified.");
public string Caption => throw new InvalidOperationException(nameof(this.Caption) + " must be specified."); public string Caption => throw new InvalidOperationException(nameof(this.Caption) + " must be specified.");
@ -25,12 +27,6 @@ namespace WinSW.Configuration
public string ExecutablePath => Process.GetCurrentProcess().MainModule.FileName; public string ExecutablePath => Process.GetCurrentProcess().MainModule.FileName;
// Installation // Installation
public bool AllowServiceAcountLogonRight => false;
public string? ServiceAccountPassword => null;
public string? ServiceAccountUser => null;
public Native.SC_ACTION[] FailureActions => new Native.SC_ACTION[0]; public Native.SC_ACTION[] FailureActions => new Native.SC_ACTION[0];
public TimeSpan ResetFailureAfter => TimeSpan.FromDays(1); public TimeSpan ResetFailureAfter => TimeSpan.FromDays(1);
@ -66,17 +62,67 @@ namespace WinSW.Configuration
public bool Interactive => false; public bool Interactive => false;
// Logging // Logging
public string LogDirectory => Path.GetDirectoryName(this.ExecutablePath)!; public Log Log { get => new LogDefaults(); }
public string LogMode => "append"; public string LogDirectory => DefaultLogSettings.Directory;
public bool OutFileDisabled => false; public string LogMode => DefaultLogSettings.Mode;
public bool ErrFileDisabled => false; public bool OutFileDisabled => this.Log.OutFileDisabled;
public string OutFilePattern => ".out.log"; public bool ErrFileDisabled => this.Log.ErrFileDisabled;
public string ErrFilePattern => ".err.log"; public string OutFilePattern => this.Log.OutFilePattern;
public string ErrFilePattern => this.Log.ErrFilePattern;
public ServiceAccount ServiceAccount => new ServiceAccount()
{
ServiceAccountName = null,
ServiceAccountDomain = null,
ServiceAccountPassword = null,
AllowServiceAcountLogonRight = false
};
public class LogDefaults : Log
{
private readonly DefaultWinSWSettings defaults;
public LogDefaults()
{
this.defaults = new DefaultWinSWSettings();
}
public override string Mode => "append";
public override string Name => this.defaults.BaseName;
public override string Directory => Path.GetDirectoryName(this.defaults.ExecutablePath)!;
public override int? SizeThreshold => 1024 * 10 * RollingSizeTimeLogAppender.BytesPerKB;
public override int? KeepFiles => SizeBasedRollingLogAppender.DefaultFilesToKeep;
public override string Pattern =>
throw new InvalidDataException("Time Based rolling policy is specified but no pattern can be found in configuration XML.");
public override int? Period => 1;
public override bool OutFileDisabled { get => false; }
public override bool ErrFileDisabled { get => false; }
public override string OutFilePattern { get => ".out.log"; }
public override string ErrFilePattern { get => ".err.log"; }
public override string? AutoRollAtTime => null;
public override int? ZipOlderThanNumDays =>
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but zipOlderThanNumDays does not match the int format found in configuration XML.");
public override string? ZipDateFormat => null;
}
// Environment // Environment
public List<Download> Downloads => new List<Download>(0); public List<Download> Downloads => new List<Download>(0);
@ -88,5 +134,32 @@ namespace WinSW.Configuration
// Extensions // Extensions
public XmlNode? ExtensionsConfiguration => null; public XmlNode? ExtensionsConfiguration => null;
public string BaseName
{
get
{
string baseName = Path.GetFileNameWithoutExtension(this.ExecutablePath);
if (baseName.EndsWith(".vshost"))
{
baseName = baseName.Substring(0, baseName.Length - 7);
}
return baseName;
}
}
public string BasePath
{
get
{
var d = new DirectoryInfo(Path.GetDirectoryName(this.ExecutablePath));
return Path.Combine(d.FullName, this.BaseName);
}
}
public List<string> ExtensionIds => new List<string>(0);
public string? SecurityDescriptor => null;
} }
} }

View File

@ -22,12 +22,6 @@ namespace WinSW.Configuration
bool HideWindow { get; } bool HideWindow { get; }
// Installation // Installation
bool AllowServiceAcountLogonRight { get; }
string? ServiceAccountPassword { get; }
string? ServiceAccountUser { get; }
Native.SC_ACTION[] FailureActions { get; } Native.SC_ACTION[] FailureActions { get; }
TimeSpan ResetFailureAfter { get; } TimeSpan ResetFailureAfter { get; }
@ -60,12 +54,17 @@ namespace WinSW.Configuration
bool Interactive { get; } bool Interactive { get; }
// Logging /// <summary>
/// Destination for logging.
/// If undefined, a default one should be used.
/// </summary>
string LogDirectory { get; } string LogDirectory { get; }
// TODO: replace by enum // TODO: replace by enum
string LogMode { get; } string LogMode { get; }
Log Log { get; }
// Environment // Environment
List<Download> Downloads { get; } List<Download> Downloads { get; }
@ -76,5 +75,19 @@ namespace WinSW.Configuration
// Extensions // Extensions
XmlNode? ExtensionsConfiguration { get; } XmlNode? ExtensionsConfiguration { get; }
// IWinSWConfiguration Support
List<string> ExtensionIds { get; }
// Service Account
ServiceAccount ServiceAccount { get; }
string BaseName { get; }
string BasePath { get; }
bool DelayedAutoStart { get; }
string? SecurityDescriptor { get; }
} }
} }

View File

@ -0,0 +1,107 @@
using System;
using System.IO;
namespace WinSW.Configuration
{
public abstract class Log
{
public abstract string? Mode { get; }
public abstract string Name { get; }
public abstract string Directory { get; }
public abstract int? SizeThreshold { get; }
public abstract int? KeepFiles { get; }
public abstract string Pattern { get; }
public abstract int? Period { get; }
// Filters
public abstract bool OutFileDisabled { get; }
public abstract bool ErrFileDisabled { get; }
public abstract string OutFilePattern { get; }
public abstract string ErrFilePattern { get; }
// Zip options
public abstract string? AutoRollAtTime { get; }
public abstract int? ZipOlderThanNumDays { get; }
public abstract string? ZipDateFormat { get; }
public LogHandler CreateLogHandler()
{
switch (this.Mode)
{
case "rotate":
return new SizeBasedRollingLogAppender(this.Directory, this.Name, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
case "none":
return new IgnoreLogAppender();
case "reset":
return new ResetLogAppender(this.Directory, this.Name, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
case "roll":
return new RollingLogAppender(this.Directory, this.Name, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
case "roll-by-time":
return new TimeBasedRollingLogAppender(this.Directory, this.Name, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, this.Pattern, this.Period.GetValueOrDefault(1));
case "roll-by-size":
return new SizeBasedRollingLogAppender(
this.Directory,
this.Name,
this.OutFileDisabled,
this.ErrFileDisabled,
this.OutFilePattern,
this.ErrFilePattern,
this.SizeThreshold.GetValueOrDefault(10 * 1024) * SizeBasedRollingLogAppender.BytesPerKB,
this.KeepFiles.GetValueOrDefault(SizeBasedRollingLogAppender.DefaultFilesToKeep));
case "append":
return new DefaultLogAppender(this.Directory, this.Name, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
case "roll-by-size-time":
if (this.Pattern is null)
{
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but no pattern can be found in configuration");
}
TimeSpan? autoRollAtTime = null;
if (this.AutoRollAtTime != null)
{
// validate it
if (!TimeSpan.TryParse(this.AutoRollAtTime, out TimeSpan autoRollAtTimeValue))
{
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but autoRollAtTime does not match the TimeSpan format HH:mm:ss found in configuration XML.");
}
autoRollAtTime = autoRollAtTimeValue;
}
return new RollingSizeTimeLogAppender(
this.Directory,
this.Name,
this.OutFileDisabled,
this.ErrFileDisabled,
this.OutFilePattern,
this.ErrFilePattern,
this.SizeThreshold.GetValueOrDefault(10 * 1024) * SizeBasedRollingLogAppender.BytesPerKB,
this.Pattern,
autoRollAtTime,
this.ZipOlderThanNumDays,
this.ZipDateFormat != null ? this.ZipDateFormat : "yyyyMM");
default:
throw new InvalidDataException("Undefined logging mode: " + this.Mode);
}
}
}
}

View File

@ -0,0 +1,29 @@
using YamlDotNet.Serialization;
namespace WinSW.Configuration
{
public class ServiceAccount
{
[YamlMember(Alias = "user")]
public string? ServiceAccountName { get; set; }
[YamlMember(Alias = "domain")]
public string? ServiceAccountDomain { get; set; }
[YamlMember(Alias = "Password")]
public string? ServiceAccountPassword { get; set; }
[YamlMember(Alias = "allowservicelogon")]
public bool AllowServiceAcountLogonRight { get; set; }
public string? ServiceAccountUser
{
get => this.ServiceAccountName is null ? null : (this.ServiceAccountDomain ?? ".") + "\\" + this.ServiceAccountName;
}
public bool HasServiceAccount()
{
return !string.IsNullOrEmpty(this.ServiceAccountName);
}
}
}

View File

@ -0,0 +1,514 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
using WinSW.Native;
using WMI;
using YamlDotNet.Serialization;
using static WinSW.Download;
namespace WinSW.Configuration
{
public class YamlConfiguration : IWinSWConfiguration
{
public DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings();
[YamlMember(Alias = "id")]
public string? IdYaml { get; set; }
[YamlMember(Alias = "name")]
public string? NameYaml { get; set; }
[YamlMember(Alias = "description")]
public string? DescriptionYaml { get; set; }
[YamlMember(Alias = "executable")]
public string? ExecutableYaml { get; set; }
[YamlMember(Alias = "executablePath")]
public string? ExecutablePathYaml { get; set; }
[YamlMember(Alias = "caption")]
public string? CaptionYaml { get; set; }
[YamlMember(Alias = "hideWindow")]
public bool? HideWindowYaml { get; set; }
[YamlMember(Alias = "workingdirectory")]
public string? WorkingDirectoryYaml { get; set; }
[YamlMember(Alias = "serviceaccount")]
public ServiceAccount? ServiceAccountYaml { get; set; }
[YamlMember(Alias = "log")]
public YamlLog? YAMLLog { get; set; }
[YamlMember(Alias = "download")]
public List<YamlDownload>? DownloadsYaml { get; set; }
[YamlMember(Alias = "arguments")]
public string? ArgumentsYaml { get; set; }
[YamlMember(Alias = "startArguments")]
public string? StartArgumentsYaml { get; set; }
[YamlMember(Alias = "stopArguments")]
public string? StopArgumentsYaml { get; set; }
[YamlMember(Alias = "stopExecutable")]
public string? StopExecutableYaml { get; set; }
[YamlMember(Alias = "stopParentProcessFirst")]
public bool? StopParentProcessFirstYaml { get; set; }
[YamlMember(Alias = "resetFailureAfter")]
public TimeSpan? ResetFailureAfterYaml { get; set; }
[YamlMember(Alias = "stopTimeout")]
public TimeSpan? StopTimeoutYaml { get; set; }
[YamlMember(Alias = "startMode")]
public StartMode? StartModeYaml { get; set; }
[YamlMember(Alias = "serviceDependencies")]
public string[]? ServiceDependenciesYaml { get; set; }
[YamlMember(Alias = "waitHint")]
public TimeSpan? WaitHintYaml { get; set; }
[YamlMember(Alias = "sleepTime")]
public TimeSpan? SleepTimeYaml { get; set; }
[YamlMember(Alias = "interactive")]
public bool? InteractiveYaml { get; set; }
[YamlMember(Alias = "priority")]
public ProcessPriorityClass? PriorityYaml { get; set; }
[YamlMember(Alias = "beepOnShutdown")]
public bool BeepOnShutdown { get; set; }
[YamlMember(Alias = "env")]
public Dictionary<string, string>? EnvironmentVariablesYaml { get; set; }
[YamlMember(Alias = "failureActions")]
public List<YamlFailureAction>? YamlFailureActions { get; set; }
[YamlMember(Alias = "delayedAutoStart")]
public bool DelayedAutoStart { get; set; }
[YamlMember(Alias = "securityDescriptor")]
public string? SecurityDescriptorYaml { get; set; }
public class YamlLog : Log
{
private readonly YamlConfiguration configs;
public YamlLog()
{
this.configs = new YamlConfiguration();
}
[YamlMember(Alias = "mode")]
public string? ModeYamlLog { get; set; }
[YamlMember(Alias = "name")]
public string? NameYamlLog { get; set; }
[YamlMember(Alias = "sizeThreshold")]
public int? SizeThresholdYamlLog { get; set; }
[YamlMember(Alias = "keepFiles")]
public int? KeepFilesYamlLog { get; set; }
[YamlMember(Alias = "pattern")]
public string? PatternYamlLog { get; set; }
[YamlMember(Alias = "period")]
public int? PeriodYamlLog { get; set; }
[YamlMember(Alias = "logpath")]
public string? LogPathYamlLog { get; set; }
// Filters
[YamlMember(Alias = "outFileDisabled")]
public bool? OutFileDisabledYamlLog { get; set; }
[YamlMember(Alias = "errFileDisabled")]
public bool? ErrFileDisabledYamlLog { get; set; }
[YamlMember(Alias = "outFilePattern")]
public string? OutFilePatternYamlLog { get; set; }
[YamlMember(Alias = "errFilePattern")]
public string? ErrFilePatternYamlLog { get; set; }
// Zip options
[YamlMember(Alias = "autoRollAtTime")]
public string? AutoRollAtTimeYamlLog { get; set; }
[YamlMember(Alias = "zipOlderThanNumDays")]
public int? ZipOlderThanNumDaysYamlLog { get; set; }
[YamlMember(Alias = "zipDateFormat")]
public string? ZipDateFormatYamlLog { get; set; }
public override string Mode => this.ModeYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.Mode :
this.ModeYamlLog;
public override string Name
{
get
{
return this.NameYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.Name :
Environment.ExpandEnvironmentVariables(this.NameYamlLog);
}
}
public override string Directory
{
get
{
return this.LogPathYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.Directory :
Environment.ExpandEnvironmentVariables(this.LogPathYamlLog);
}
}
public override int? SizeThreshold
{
get
{
return this.SizeThresholdYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.SizeThreshold :
this.SizeThresholdYamlLog * RollingSizeTimeLogAppender.BytesPerKB;
}
}
public override int? KeepFiles
{
get
{
return this.KeepFilesYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.KeepFiles :
this.KeepFilesYamlLog;
}
}
public override string Pattern
{
get
{
if (this.PatternYamlLog != null)
{
return this.PatternYamlLog;
}
return DefaultWinSWSettings.DefaultLogSettings.Pattern;
}
}
public override int? Period => this.PeriodYamlLog is null ? 1 : this.PeriodYamlLog;
public override bool OutFileDisabled
{
get
{
return this.OutFileDisabledYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.OutFileDisabled :
(bool)this.OutFileDisabledYamlLog;
}
}
public override bool ErrFileDisabled
{
get
{
return this.ErrFileDisabledYamlLog is null ?
this.configs.Defaults.ErrFileDisabled :
(bool)this.ErrFileDisabledYamlLog;
}
}
public override string OutFilePattern
{
get
{
return this.OutFilePatternYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.OutFilePattern :
Environment.ExpandEnvironmentVariables(this.OutFilePatternYamlLog);
}
}
public override string ErrFilePattern
{
get
{
return this.ErrFilePatternYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.ErrFilePattern :
Environment.ExpandEnvironmentVariables(this.ErrFilePatternYamlLog);
}
}
public override string? AutoRollAtTime
{
get
{
return this.AutoRollAtTimeYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.AutoRollAtTime :
this.AutoRollAtTimeYamlLog;
}
}
public override int? ZipOlderThanNumDays
{
get
{
if (this.ZipOlderThanNumDaysYamlLog != null)
{
return this.ZipOlderThanNumDaysYamlLog;
}
return DefaultWinSWSettings.DefaultLogSettings.ZipOlderThanNumDays;
}
}
public override string? ZipDateFormat
{
get
{
return this.ZipDateFormatYamlLog is null ?
DefaultWinSWSettings.DefaultLogSettings.ZipDateFormat :
this.ZipDateFormatYamlLog;
}
}
}
public class YamlDownload
{
[YamlMember(Alias = "from")]
public string FromYamlDownload { get; set; } = string.Empty;
[YamlMember(Alias = "to")]
public string ToYamlDownload { get; set; } = string.Empty;
[YamlMember(Alias = "auth")]
public AuthType AuthYamlDownload { get; set; }
[YamlMember(Alias = "username")]
public string? UsernameYamlDownload { get; set; }
[YamlMember(Alias = "password")]
public string? PasswordYamlDownload { get; set; }
[YamlMember(Alias = "unsecureAuth")]
public bool UnsecureAuthYamlDownload { get; set; }
[YamlMember(Alias = "failOnError")]
public bool FailOnErrorYamlDownload { get; set; }
[YamlMember(Alias = "proxy")]
public string? ProxyYamlDownload { get; set; }
}
public class YamlFailureAction
{
[YamlMember(Alias = "type")]
private SC_ACTION_TYPE type;
[YamlMember(Alias = "delay")]
private TimeSpan delay;
public SC_ACTION_TYPE Type { get => this.type; set => this.type = value; }
public TimeSpan Delay { get => this.delay; set => this.delay = value; }
}
private string? GetArguments(string? args, ArgType type)
{
if (args is null)
{
switch (type)
{
case ArgType.Arg:
return this.Defaults.Arguments;
case ArgType.Startarg:
return this.Defaults.StartArguments;
case ArgType.Stoparg:
return this.Defaults.StopArguments;
default:
return string.Empty;
}
}
return Environment.ExpandEnvironmentVariables(args);
}
private enum ArgType
{
Arg = 0,
Startarg = 1,
Stoparg = 2
}
private List<Download> GetDownloads(List<YamlDownload>? downloads)
{
if (downloads is null)
{
return this.Defaults.Downloads;
}
var result = new List<Download>(downloads.Count);
foreach (var item in downloads)
{
result.Add(new Download(
item.FromYamlDownload,
item.ToYamlDownload,
item.FailOnErrorYamlDownload,
item.AuthYamlDownload,
item.UsernameYamlDownload,
item.PasswordYamlDownload,
item.UnsecureAuthYamlDownload,
item.ProxyYamlDownload));
}
return result;
}
public string Id => this.IdYaml is null ? this.Defaults.Id : this.IdYaml;
public string Description => this.DescriptionYaml is null ? this.Defaults.Description : this.DescriptionYaml;
public string Executable => this.ExecutableYaml is null ? this.Defaults.Executable : this.ExecutableYaml;
public string ExecutablePath => this.ExecutablePathYaml is null ? this.Defaults.ExecutablePath : this.ExecutablePathYaml;
public string Caption => this.CaptionYaml is null ? this.Defaults.Caption : this.CaptionYaml;
public bool HideWindow => this.HideWindowYaml is null ? this.Defaults.HideWindow : (bool)this.HideWindowYaml;
public bool StopParentProcessFirst
{
get
{
return this.StopParentProcessFirstYaml is null ?
this.Defaults.StopParentProcessFirst :
(bool)this.StopParentProcessFirstYaml;
}
}
public StartMode StartMode => this.StartModeYaml is null ? this.Defaults.StartMode : (StartMode)this.StartModeYaml;
public string Arguments
{
get
{
var args = this.GetArguments(this.ArgumentsYaml, ArgType.Arg);
return args is null ? this.Defaults.Arguments : args;
}
}
public string? StartArguments => this.GetArguments(this.StartArgumentsYaml, ArgType.Startarg);
public string? StopArguments => this.GetArguments(this.StopArgumentsYaml, ArgType.Stoparg);
public string? StopExecutable
{
get
{
return this.StopExecutableYaml is null ?
this.Defaults.StopExecutable :
null;
}
}
public SC_ACTION[] FailureActions
{
get
{
if (this.YamlFailureActions is null)
{
return new SC_ACTION[0];
}
var arr = new List<SC_ACTION>();
foreach (var item in this.YamlFailureActions)
{
arr.Add(new SC_ACTION(item.Type, item.Delay));
}
return arr.ToArray();
}
}
public TimeSpan ResetFailureAfter => this.ResetFailureAfterYaml is null ?
this.Defaults.ResetFailureAfter :
(TimeSpan)this.ResetFailureAfterYaml;
public string WorkingDirectory => this.WorkingDirectoryYaml is null ?
this.Defaults.WorkingDirectory :
this.WorkingDirectoryYaml;
public ProcessPriorityClass Priority => this.PriorityYaml is null ? this.Defaults.Priority : (ProcessPriorityClass)this.PriorityYaml;
public TimeSpan StopTimeout => this.StopTimeoutYaml is null ? this.Defaults.StopTimeout : (TimeSpan)this.StopTimeoutYaml;
public string[] ServiceDependencies => this.ServiceDependenciesYaml is null ?
this.Defaults.ServiceDependencies :
this.ServiceDependenciesYaml;
public TimeSpan WaitHint => this.WaitHintYaml is null ? this.Defaults.WaitHint : (TimeSpan)this.WaitHintYaml;
public TimeSpan SleepTime => this.SleepTimeYaml is null ? this.Defaults.SleepTime : (TimeSpan)this.SleepTimeYaml;
public bool Interactive => this.InteractiveYaml is null ? this.Defaults.Interactive : (bool)this.InteractiveYaml;
public List<Download> Downloads => this.GetDownloads(this.DownloadsYaml);
public Dictionary<string, string> EnvironmentVariables { get; set; } = new Dictionary<string, string>();
public void LoadEnvironmentVariables()
{
if (this.EnvironmentVariablesYaml is null)
{
this.EnvironmentVariables = this.Defaults.EnvironmentVariables;
}
else
{
foreach (var item in this.EnvironmentVariablesYaml)
{
var value = Environment.ExpandEnvironmentVariables(item.Value);
this.EnvironmentVariables[item.Key] = value;
Environment.SetEnvironmentVariable(item.Key, value);
}
}
}
public ServiceAccount ServiceAccount => this.ServiceAccountYaml is null ? this.Defaults.ServiceAccount : this.ServiceAccountYaml;
public Log Log => this.YAMLLog is null ? this.Defaults.Log : this.YAMLLog;
public string LogDirectory => this.Log.Directory;
public string LogMode => this.Log.Mode is null ? this.Defaults.LogMode : this.Log.Mode;
// TODO
XmlNode? IWinSWConfiguration.ExtensionsConfiguration => throw new NotImplementedException();
public List<string> ExtensionIds => throw new NotImplementedException();
public string BaseName => throw new NotImplementedException();
public string BasePath => throw new NotImplementedException();
public string? ServiceAccountDomain => throw new NotImplementedException();
public string? ServiceAccountName => throw new NotImplementedException();
public string? SecurityDescriptor => throw new NotImplementedException();
}
}

View File

@ -1,4 +1,5 @@
using System.Xml; using System.Xml;
using WinSW.Configuration;
namespace WinSW.Extensions namespace WinSW.Extensions
{ {
@ -10,7 +11,7 @@ namespace WinSW.Extensions
public WinSWExtensionDescriptor Descriptor { get; set; } public WinSWExtensionDescriptor Descriptor { get; set; }
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
public virtual void Configure(ServiceDescriptor descriptor, XmlNode node) public virtual void Configure(IWinSWConfiguration descriptor, XmlNode node)
{ {
// Do nothing // Do nothing
} }

View File

@ -1,4 +1,5 @@
using System.Xml; using System.Xml;
using WinSW.Configuration;
namespace WinSW.Extensions namespace WinSW.Extensions
{ {
@ -27,7 +28,7 @@ namespace WinSW.Extensions
/// </summary> /// </summary>
/// <param name="descriptor">Service descriptor</param> /// <param name="descriptor">Service descriptor</param>
/// <param name="node">Configuration node</param> /// <param name="node">Configuration node</param>
void Configure(ServiceDescriptor descriptor, XmlNode node); void Configure(IWinSWConfiguration descriptor, XmlNode node);
/// <summary> /// <summary>
/// Start handler. Called during startup of the service before the child process. /// Start handler. Called during startup of the service before the child process.

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml; using System.Xml;
using log4net; using log4net;
using WinSW.Configuration;
namespace WinSW.Extensions namespace WinSW.Extensions
{ {
@ -9,11 +10,11 @@ namespace WinSW.Extensions
{ {
public Dictionary<string, IWinSWExtension> Extensions { get; private set; } public Dictionary<string, IWinSWExtension> Extensions { get; private set; }
public ServiceDescriptor ServiceDescriptor { get; private set; } public IWinSWConfiguration ServiceDescriptor { get; private set; }
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager)); private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
public WinSWExtensionManager(ServiceDescriptor serviceDescriptor) public WinSWExtensionManager(IWinSWConfiguration serviceDescriptor)
{ {
this.ServiceDescriptor = serviceDescriptor; this.ServiceDescriptor = serviceDescriptor;
this.Extensions = new Dictionary<string, IWinSWExtension>(); this.Extensions = new Dictionary<string, IWinSWExtension>();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
using System;
using System.IO;
using WinSW.Configuration;
using YamlDotNet.Serialization;
namespace WinSW
{
public class ServiceDescriptorYaml
{
public readonly YamlConfiguration Configurations = new YamlConfiguration();
public static DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings();
public string BasePath { get; set; }
public virtual string ExecutablePath => Defaults.ExecutablePath;
public ServiceDescriptorYaml()
{
string p = this.ExecutablePath;
string baseName = Path.GetFileNameWithoutExtension(p);
if (baseName.EndsWith(".vshost"))
{
baseName = baseName.Substring(0, baseName.Length - 7);
}
DirectoryInfo d = new DirectoryInfo(Path.GetDirectoryName(p));
while (true)
{
if (File.Exists(Path.Combine(d.FullName, baseName + ".yml")))
{
break;
}
if (d.Parent is null)
{
throw new FileNotFoundException("Unable to locate " + baseName + ".yml file within executable directory or any parents");
}
d = d.Parent;
}
this.BasePath = Path.Combine(d.FullName, baseName);
using (var reader = new StreamReader(this.BasePath + ".yml"))
{
var file = reader.ReadToEnd();
var deserializer = new DeserializerBuilder().Build();
this.Configurations = deserializer.Deserialize<YamlConfiguration>(file);
}
Environment.SetEnvironmentVariable("BASE", d.FullName);
// ditto for ID
Environment.SetEnvironmentVariable("SERVICE_ID", this.Configurations.Id);
// New name
Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameExecutablePath, this.ExecutablePath);
// Also inject system environment variables
Environment.SetEnvironmentVariable(WinSWSystem.EnvVarNameServiceId, this.Configurations.Id);
this.Configurations.LoadEnvironmentVariables();
}
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
public ServiceDescriptorYaml(YamlConfiguration configs)
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
{
this.Configurations = configs;
this.Configurations.LoadEnvironmentVariables();
}
public static ServiceDescriptorYaml FromYaml(string yaml)
{
var deserializer = new DeserializerBuilder().Build();
var configs = deserializer.Deserialize<YamlConfiguration>(yaml);
return new ServiceDescriptorYaml(configs);
}
}
}

View File

@ -11,6 +11,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="log4net" Version="2.0.8" /> <PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="YamlDotNet" Version="8.1.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.*"> <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.*">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

7
src/NuGet.Config Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using log4net; using log4net;
using WinSW.Configuration;
using WinSW.Extensions; using WinSW.Extensions;
using WinSW.Util; using WinSW.Util;
using static WinSW.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension.NativeMethods; using static WinSW.Plugins.RunawayProcessKiller.RunawayProcessKillerExtension.NativeMethods;
@ -179,7 +180,7 @@ namespace WinSW.Plugins.RunawayProcessKiller
return parameters.Environment; return parameters.Environment;
} }
public override void Configure(ServiceDescriptor descriptor, XmlNode node) public override void Configure(IWinSWConfiguration descriptor, XmlNode node)
{ {
// We expect the upper logic to process any errors // We expect the upper logic to process any errors
// TODO: a better parser API for types would be useful // TODO: a better parser API for types would be useful

View File

@ -1,9 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml; using System.Xml;
using log4net; using log4net;
using WinSW.Configuration;
using WinSW.Extensions; using WinSW.Extensions;
using WinSW.Util; using WinSW.Util;
namespace WinSW.Plugins.SharedDirectoryMapper namespace WinSW.Plugins.SharedDirectoryMapper
{ {
public class SharedDirectoryMapper : AbstractWinSWExtension public class SharedDirectoryMapper : AbstractWinSWExtension
@ -25,7 +27,7 @@ namespace WinSW.Plugins.SharedDirectoryMapper
this._entries.Add(config); this._entries.Add(config);
} }
public override void Configure(ServiceDescriptor descriptor, XmlNode node) public override void Configure(IWinSWConfiguration descriptor, XmlNode node)
{ {
XmlNodeList? mapNodes = XmlHelper.SingleNode(node, "mapping", false)!.SelectNodes("map"); XmlNodeList? mapNodes = XmlHelper.SingleNode(node, "mapping", false)!.SelectNodes("map");
if (mapNodes != null) if (mapNodes != null)

View File

@ -1,445 +1,445 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using NUnit.Framework; using NUnit.Framework;
using WinSW; using WinSW;
using winswTests.Util; using winswTests.Util;
using WMI; using WMI;
namespace winswTests namespace winswTests
{ {
[TestFixture] [TestFixture]
public class ServiceDescriptorTests public class ServiceDescriptorTests
{ {
private ServiceDescriptor _extendedServiceDescriptor; private ServiceDescriptor _extendedServiceDescriptor;
private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath"; private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath";
private const string Username = "User"; private const string Username = "User";
private const string Password = "Password"; private const string Password = "Password";
private const string Domain = "Domain"; private const string Domain = "Domain";
private const string AllowServiceAccountLogonRight = "true"; private const string AllowServiceAccountLogonRight = "true";
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
string seedXml = string seedXml =
$@"<service> $@"<service>
<id>service.exe</id> <id>service.exe</id>
<name>Service</name> <name>Service</name>
<description>The service.</description> <description>The service.</description>
<executable>node.exe</executable> <executable>node.exe</executable>
<arguments>My Arguments</arguments> <arguments>My Arguments</arguments>
<log mode=""roll""></log> <log mode=""roll""></log>
<serviceaccount> <serviceaccount>
<domain>{Domain}</domain> <domain>{Domain}</domain>
<user>{Username}</user> <user>{Username}</user>
<password>{Password}</password> <password>{Password}</password>
<allowservicelogon>{AllowServiceAccountLogonRight}</allowservicelogon> <allowservicelogon>{AllowServiceAccountLogonRight}</allowservicelogon>
</serviceaccount> </serviceaccount>
<workingdirectory>{ExpectedWorkingDirectory}</workingdirectory> <workingdirectory>{ExpectedWorkingDirectory}</workingdirectory>
<logpath>C:\logs</logpath> <logpath>C:\logs</logpath>
</service>"; </service>";
this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml); this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
} }
[Test] [Test]
public void DefaultStartMode() public void DefaultStartMode()
{ {
Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Automatic)); Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Automatic));
} }
[Test] [Test]
public void IncorrectStartMode() public void IncorrectStartMode()
{ {
string seedXml = string seedXml =
$@"<service> $@"<service>
<id>service.exe</id> <id>service.exe</id>
<name>Service</name> <name>Service</name>
<description>The service.</description> <description>The service.</description>
<executable>node.exe</executable> <executable>node.exe</executable>
<arguments>My Arguments</arguments> <arguments>My Arguments</arguments>
<startmode>roll</startmode> <startmode>roll</startmode>
<log mode=""roll""></log> <log mode=""roll""></log>
<serviceaccount> <serviceaccount>
<domain>{Domain}</domain> <domain>{Domain}</domain>
<user>{Username}</user> <user>{Username}</user>
<password>{Password}</password> <password>{Password}</password>
<allowservicelogon>{AllowServiceAccountLogonRight}</allowservicelogon> <allowservicelogon>{AllowServiceAccountLogonRight}</allowservicelogon>
</serviceaccount> </serviceaccount>
<workingdirectory>{ExpectedWorkingDirectory}</workingdirectory> <workingdirectory>{ExpectedWorkingDirectory}</workingdirectory>
<logpath>C:\logs</logpath> <logpath>C:\logs</logpath>
</service>"; </service>";
this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml); this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(() => this._extendedServiceDescriptor.StartMode, Throws.ArgumentException); Assert.That(() => this._extendedServiceDescriptor.StartMode, Throws.ArgumentException);
} }
[Test] [Test]
public void ChangedStartMode() public void ChangedStartMode()
{ {
string seedXml = string seedXml =
$@"<service> $@"<service>
<id>service.exe</id> <id>service.exe</id>
<name>Service</name> <name>Service</name>
<description>The service.</description> <description>The service.</description>
<executable>node.exe</executable> <executable>node.exe</executable>
<arguments>My Arguments</arguments> <arguments>My Arguments</arguments>
<startmode>manual</startmode> <startmode>manual</startmode>
<log mode=""roll""></log> <log mode=""roll""></log>
<serviceaccount> <serviceaccount>
<domain>{Domain}</domain> <domain>{Domain}</domain>
<user>{Username}</user> <user>{Username}</user>
<password>{Password}</password> <password>{Password}</password>
<allowservicelogon>{AllowServiceAccountLogonRight}</allowservicelogon> <allowservicelogon>{AllowServiceAccountLogonRight}</allowservicelogon>
</serviceaccount> </serviceaccount>
<workingdirectory>{ExpectedWorkingDirectory}</workingdirectory> <workingdirectory>{ExpectedWorkingDirectory}</workingdirectory>
<logpath>C:\logs</logpath> <logpath>C:\logs</logpath>
</service>"; </service>";
this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml); this._extendedServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Manual)); Assert.That(this._extendedServiceDescriptor.StartMode, Is.EqualTo(StartMode.Manual));
} }
[Test] [Test]
public void VerifyWorkingDirectory() public void VerifyWorkingDirectory()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory); Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory);
Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory)); Assert.That(this._extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory));
} }
[Test] [Test]
public void VerifyServiceLogonRight() public void VerifyServiceLogonRight()
{ {
Assert.That(this._extendedServiceDescriptor.AllowServiceAcountLogonRight, Is.True); Assert.That(_extendedServiceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.True);
} }
[Test] [Test]
public void VerifyUsername() public void VerifyUsername()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory); Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory);
Assert.That(this._extendedServiceDescriptor.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username)); Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username));
} }
[Test] [Test]
public void VerifyPassword() public void VerifyPassword()
{ {
Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + this._extendedServiceDescriptor.WorkingDirectory); Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + _extendedServiceDescriptor.WorkingDirectory);
Assert.That(this._extendedServiceDescriptor.ServiceAccountPassword, Is.EqualTo(Password)); Assert.That(_extendedServiceDescriptor.ServiceAccount.ServiceAccountPassword, Is.EqualTo(Password));
} }
[Test] [Test]
public void Priority() public void Priority()
{ {
var sd = ServiceDescriptor.FromXML("<service><id>test</id><priority>normal</priority></service>"); var sd = ServiceDescriptor.FromXML("<service><id>test</id><priority>normal</priority></service>");
Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal)); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal));
sd = ServiceDescriptor.FromXML("<service><id>test</id><priority>idle</priority></service>"); sd = ServiceDescriptor.FromXML("<service><id>test</id><priority>idle</priority></service>");
Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Idle)); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Idle));
sd = ServiceDescriptor.FromXML("<service><id>test</id></service>"); sd = ServiceDescriptor.FromXML("<service><id>test</id></service>");
Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal)); Assert.That(sd.Priority, Is.EqualTo(ProcessPriorityClass.Normal));
} }
[Test] [Test]
public void StopParentProcessFirstIsFalseByDefault() public void StopParentProcessFirstIsFalseByDefault()
{ {
Assert.That(this._extendedServiceDescriptor.StopParentProcessFirst, Is.False); Assert.That(this._extendedServiceDescriptor.StopParentProcessFirst, Is.False);
} }
[Test] [Test]
public void CanParseStopParentProcessFirst() public void CanParseStopParentProcessFirst()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<stopparentprocessfirst>true</stopparentprocessfirst>" + "<stopparentprocessfirst>true</stopparentprocessfirst>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.StopParentProcessFirst, Is.True); Assert.That(serviceDescriptor.StopParentProcessFirst, Is.True);
} }
[Test] [Test]
public void CanParseStopTimeout() public void CanParseStopTimeout()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<stoptimeout>60sec</stoptimeout>" + "<stoptimeout>60sec</stoptimeout>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(60))); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(60)));
} }
[Test] [Test]
public void CanParseStopTimeoutFromMinutes() public void CanParseStopTimeoutFromMinutes()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<stoptimeout>10min</stoptimeout>" + "<stoptimeout>10min</stoptimeout>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10))); Assert.That(serviceDescriptor.StopTimeout, Is.EqualTo(TimeSpan.FromMinutes(10)));
} }
[Test] [Test]
public void CanParseLogname() public void CanParseLogname()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<logname>MyTestApp</logname>" + "<logname>MyTestApp</logname>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.LogName, Is.EqualTo("MyTestApp")); Assert.That(serviceDescriptor.LogName, Is.EqualTo("MyTestApp"));
} }
[Test] [Test]
public void CanParseOutfileDisabled() public void CanParseOutfileDisabled()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<outfiledisabled>true</outfiledisabled>" + "<outfiledisabled>true</outfiledisabled>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.OutFileDisabled, Is.True); Assert.That(serviceDescriptor.Log.OutFileDisabled, Is.True);
} }
[Test] [Test]
public void CanParseErrfileDisabled() public void CanParseErrfileDisabled()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<errfiledisabled>true</errfiledisabled>" + "<errfiledisabled>true</errfiledisabled>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.ErrFileDisabled, Is.True); Assert.That(serviceDescriptor.Log.ErrFileDisabled, Is.True);
} }
[Test] [Test]
public void CanParseOutfilePattern() public void CanParseOutfilePattern()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<outfilepattern>.out.test.log</outfilepattern>" + "<outfilepattern>.out.test.log</outfilepattern>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.OutFilePattern, Is.EqualTo(".out.test.log")); Assert.That(serviceDescriptor.Log.OutFilePattern, Is.EqualTo(".out.test.log"));
} }
[Test] [Test]
public void CanParseErrfilePattern() public void CanParseErrfilePattern()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<errfilepattern>.err.test.log</errfilepattern>" + "<errfilepattern>.err.test.log</errfilepattern>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.ErrFilePattern, Is.EqualTo(".err.test.log")); Assert.That(serviceDescriptor.Log.ErrFilePattern, Is.EqualTo(".err.test.log"));
} }
[Test] [Test]
public void LogModeRollBySize() public void LogModeRollBySize()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<logpath>c:\\</logpath>" + "<logpath>c:\\</logpath>"
+ "<log mode=\"roll-by-size\">" + "<log mode=\"roll-by-size\">"
+ "<sizeThreshold>112</sizeThreshold>" + "<sizeThreshold>112</sizeThreshold>"
+ "<keepFiles>113</keepFiles>" + "<keepFiles>113</keepFiles>"
+ "</log>" + "</log>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
serviceDescriptor.BaseName = "service"; serviceDescriptor.BaseName = "service";
var logHandler = serviceDescriptor.LogHandler as SizeBasedRollingLogAppender; var logHandler = serviceDescriptor.Log.CreateLogHandler() as SizeBasedRollingLogAppender;
Assert.That(logHandler, Is.Not.Null); Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(112 * 1024)); Assert.That(logHandler.SizeTheshold, Is.EqualTo(112 * 1024));
Assert.That(logHandler.FilesToKeep, Is.EqualTo(113)); Assert.That(logHandler.FilesToKeep, Is.EqualTo(113));
} }
[Test] [Test]
public void LogModeRollByTime() public void LogModeRollByTime()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<logpath>c:\\</logpath>" + "<logpath>c:\\</logpath>"
+ "<log mode=\"roll-by-time\">" + "<log mode=\"roll-by-time\">"
+ "<period>7</period>" + "<period>7</period>"
+ "<pattern>log pattern</pattern>" + "<pattern>log pattern</pattern>"
+ "</log>" + "</log>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
serviceDescriptor.BaseName = "service"; serviceDescriptor.BaseName = "service";
var logHandler = serviceDescriptor.LogHandler as TimeBasedRollingLogAppender; var logHandler = serviceDescriptor.Log.CreateLogHandler() as TimeBasedRollingLogAppender;
Assert.That(logHandler, Is.Not.Null); Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.Period, Is.EqualTo(7)); Assert.That(logHandler.Period, Is.EqualTo(7));
Assert.That(logHandler.Pattern, Is.EqualTo("log pattern")); Assert.That(logHandler.Pattern, Is.EqualTo("log pattern"));
} }
[Test] [Test]
public void LogModeRollBySizeTime() public void LogModeRollBySizeTime()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<logpath>c:\\</logpath>" + "<logpath>c:\\</logpath>"
+ "<log mode=\"roll-by-size-time\">" + "<log mode=\"roll-by-size-time\">"
+ "<sizeThreshold>10240</sizeThreshold>" + "<sizeThreshold>10240</sizeThreshold>"
+ "<pattern>yyyy-MM-dd</pattern>" + "<pattern>yyyy-MM-dd</pattern>"
+ "<autoRollAtTime>00:00:00</autoRollAtTime>" + "<autoRollAtTime>00:00:00</autoRollAtTime>"
+ "</log>" + "</log>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
serviceDescriptor.BaseName = "service"; serviceDescriptor.BaseName = "service";
var logHandler = serviceDescriptor.LogHandler as RollingSizeTimeLogAppender; var logHandler = serviceDescriptor.Log.CreateLogHandler() as RollingSizeTimeLogAppender;
Assert.That(logHandler, Is.Not.Null); Assert.That(logHandler, Is.Not.Null);
Assert.That(logHandler.SizeTheshold, Is.EqualTo(10240 * 1024)); Assert.That(logHandler.SizeTheshold, Is.EqualTo(10240 * 1024));
Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd")); Assert.That(logHandler.FilePattern, Is.EqualTo("yyyy-MM-dd"));
Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0, 0, 0))); Assert.That(logHandler.AutoRollAtTime, Is.EqualTo((TimeSpan?)new TimeSpan(0, 0, 0)));
} }
[Test] [Test]
public void VerifyServiceLogonRightGraceful() public void VerifyServiceLogonRightGraceful()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<serviceaccount>" + "<serviceaccount>"
+ "<domain>" + Domain + "</domain>" + "<domain>" + Domain + "</domain>"
+ "<user>" + Username + "</user>" + "<user>" + Username + "</user>"
+ "<password>" + Password + "</password>" + "<password>" + Password + "</password>"
+ "<allowservicelogon>true1</allowservicelogon>" + "<allowservicelogon>true1</allowservicelogon>"
+ "</serviceaccount>" + "</serviceaccount>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.AllowServiceAcountLogonRight, Is.False); Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False);
} }
[Test] [Test]
public void VerifyServiceLogonRightOmitted() public void VerifyServiceLogonRightOmitted()
{ {
const string seedXml = "<service>" const string seedXml = "<service>"
+ "<serviceaccount>" + "<serviceaccount>"
+ "<domain>" + Domain + "</domain>" + "<domain>" + Domain + "</domain>"
+ "<user>" + Username + "</user>" + "<user>" + Username + "</user>"
+ "<password>" + Password + "</password>" + "<password>" + Password + "</password>"
+ "</serviceaccount>" + "</serviceaccount>"
+ "</service>"; + "</service>";
var serviceDescriptor = ServiceDescriptor.FromXML(seedXml); var serviceDescriptor = ServiceDescriptor.FromXML(seedXml);
Assert.That(serviceDescriptor.AllowServiceAcountLogonRight, Is.False); Assert.That(serviceDescriptor.ServiceAccount.AllowServiceAcountLogonRight, Is.False);
} }
[Test] [Test]
public void VerifyWaitHint_FullXML() public void VerifyWaitHint_FullXML()
{ {
var sd = ConfigXmlBuilder.create() var sd = ConfigXmlBuilder.create()
.WithTag("waithint", "20 min") .WithTag("waithint", "20 min")
.ToServiceDescriptor(true); .ToServiceDescriptor(true);
Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(20))); Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(20)));
} }
/// <summary> /// <summary>
/// Test for https://github.com/kohsuke/winsw/issues/159 /// Test for https://github.com/kohsuke/winsw/issues/159
/// </summary> /// </summary>
[Test] [Test]
public void VerifyWaitHint_XMLWithoutVersion() public void VerifyWaitHint_XMLWithoutVersion()
{ {
var sd = ConfigXmlBuilder.create(printXMLVersion: false) var sd = ConfigXmlBuilder.create(printXMLVersion: false)
.WithTag("waithint", "21 min") .WithTag("waithint", "21 min")
.ToServiceDescriptor(true); .ToServiceDescriptor(true);
Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(21))); Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(21)));
} }
[Test] [Test]
public void VerifyWaitHint_XMLWithoutComment() public void VerifyWaitHint_XMLWithoutComment()
{ {
var sd = ConfigXmlBuilder.create(xmlComment: null) var sd = ConfigXmlBuilder.create(xmlComment: null)
.WithTag("waithint", "22 min") .WithTag("waithint", "22 min")
.ToServiceDescriptor(true); .ToServiceDescriptor(true);
Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(22))); Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(22)));
} }
[Test] [Test]
public void VerifyWaitHint_XMLWithoutVersionAndComment() public void VerifyWaitHint_XMLWithoutVersionAndComment()
{ {
var sd = ConfigXmlBuilder.create(xmlComment: null, printXMLVersion: false) var sd = ConfigXmlBuilder.create(xmlComment: null, printXMLVersion: false)
.WithTag("waithint", "23 min") .WithTag("waithint", "23 min")
.ToServiceDescriptor(true); .ToServiceDescriptor(true);
Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(23))); Assert.That(sd.WaitHint, Is.EqualTo(TimeSpan.FromMinutes(23)));
} }
[Test] [Test]
public void VerifySleepTime() public void VerifySleepTime()
{ {
var sd = ConfigXmlBuilder.create().WithTag("sleeptime", "3 hrs").ToServiceDescriptor(true); var sd = ConfigXmlBuilder.create().WithTag("sleeptime", "3 hrs").ToServiceDescriptor(true);
Assert.That(sd.SleepTime, Is.EqualTo(TimeSpan.FromHours(3))); Assert.That(sd.SleepTime, Is.EqualTo(TimeSpan.FromHours(3)));
} }
[Test] [Test]
public void VerifyResetFailureAfter() public void VerifyResetFailureAfter()
{ {
var sd = ConfigXmlBuilder.create().WithTag("resetfailure", "75 sec").ToServiceDescriptor(true); var sd = ConfigXmlBuilder.create().WithTag("resetfailure", "75 sec").ToServiceDescriptor(true);
Assert.That(sd.ResetFailureAfter, Is.EqualTo(TimeSpan.FromSeconds(75))); Assert.That(sd.ResetFailureAfter, Is.EqualTo(TimeSpan.FromSeconds(75)));
} }
[Test] [Test]
public void VerifyStopTimeout() public void VerifyStopTimeout()
{ {
var sd = ConfigXmlBuilder.create().WithTag("stoptimeout", "35 secs").ToServiceDescriptor(true); var sd = ConfigXmlBuilder.create().WithTag("stoptimeout", "35 secs").ToServiceDescriptor(true);
Assert.That(sd.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(35))); Assert.That(sd.StopTimeout, Is.EqualTo(TimeSpan.FromSeconds(35)));
} }
/// <summary> /// <summary>
/// https://github.com/kohsuke/winsw/issues/178 /// https://github.com/kohsuke/winsw/issues/178
/// </summary> /// </summary>
[Test] [Test]
public void Arguments_LegacyParam() public void Arguments_LegacyParam()
{ {
var sd = ConfigXmlBuilder.create().WithTag("arguments", "arg").ToServiceDescriptor(true); var sd = ConfigXmlBuilder.create().WithTag("arguments", "arg").ToServiceDescriptor(true);
Assert.That(sd.Arguments, Is.EqualTo("arg")); Assert.That(sd.Arguments, Is.EqualTo("arg"));
} }
[Test] [Test]
public void Arguments_NewParam_Single() public void Arguments_NewParam_Single()
{ {
var sd = ConfigXmlBuilder.create() var sd = ConfigXmlBuilder.create()
.WithTag("argument", "--arg1=2") .WithTag("argument", "--arg1=2")
.ToServiceDescriptor(true); .ToServiceDescriptor(true);
Assert.That(sd.Arguments, Is.EqualTo(" --arg1=2")); Assert.That(sd.Arguments, Is.EqualTo(" --arg1=2"));
} }
[Test] [Test]
public void Arguments_NewParam_MultipleArgs() public void Arguments_NewParam_MultipleArgs()
{ {
var sd = ConfigXmlBuilder.create() var sd = ConfigXmlBuilder.create()
.WithTag("argument", "--arg1=2") .WithTag("argument", "--arg1=2")
.WithTag("argument", "--arg2=123") .WithTag("argument", "--arg2=123")
.WithTag("argument", "--arg3=null") .WithTag("argument", "--arg3=null")
.ToServiceDescriptor(true); .ToServiceDescriptor(true);
Assert.That(sd.Arguments, Is.EqualTo(" --arg1=2 --arg2=123 --arg3=null")); Assert.That(sd.Arguments, Is.EqualTo(" --arg1=2 --arg2=123 --arg3=null"));
} }
/// <summary> /// <summary>
/// Ensures that the new single-argument field has a higher priority. /// Ensures that the new single-argument field has a higher priority.
/// </summary> /// </summary>
[Test] [Test]
public void Arguments_Bothparam_Priorities() public void Arguments_Bothparam_Priorities()
{ {
var sd = ConfigXmlBuilder.create() var sd = ConfigXmlBuilder.create()
.WithTag("arguments", "--arg1=2 --arg2=3") .WithTag("arguments", "--arg1=2 --arg2=3")
.WithTag("argument", "--arg2=123") .WithTag("argument", "--arg2=123")
.WithTag("argument", "--arg3=null") .WithTag("argument", "--arg3=null")
.ToServiceDescriptor(true); .ToServiceDescriptor(true);
Assert.That(sd.Arguments, Is.EqualTo(" --arg2=123 --arg3=null")); Assert.That(sd.Arguments, Is.EqualTo(" --arg2=123 --arg3=null"));
} }
[TestCase(true)] [TestCase(true)]
[TestCase(false)] [TestCase(false)]
public void DelayedStart_RoundTrip(bool enabled) public void DelayedStart_RoundTrip(bool enabled)
{ {
var bldr = ConfigXmlBuilder.create(); var bldr = ConfigXmlBuilder.create();
if (enabled) if (enabled)
{ {
bldr = bldr.WithDelayedAutoStart(); bldr = bldr.WithDelayedAutoStart();
} }
var sd = bldr.ToServiceDescriptor(); var sd = bldr.ToServiceDescriptor();
Assert.That(sd.DelayedAutoStart, Is.EqualTo(enabled)); Assert.That(sd.DelayedAutoStart, Is.EqualTo(enabled));
} }
} }
} }

View File

@ -0,0 +1,119 @@
using System;
using NUnit.Framework;
using WinSW;
namespace winswTests
{
class ServiceDescriptorYamlTest
{
private string MinimalYaml = @"id: myapp
caption: This is a test
executable: 'C:\Program Files\Java\jdk1.8.0_241\bin\java.exe'
description: This is test winsw";
[Test]
public void Simple_yaml_parsing_test()
{
var configs = ServiceDescriptorYaml.FromYaml(MinimalYaml).Configurations;
Assert.AreEqual("myapp", configs.Id);
Assert.AreEqual("This is a test", configs.Caption);
Assert.AreEqual("C:\\Program Files\\Java\\jdk1.8.0_241\\bin\\java.exe", configs.Executable);
Assert.AreEqual("This is test winsw", configs.Description);
}
[Test]
public void Must_implemented_value_test()
{
string yml = @"caption: This is a test
executable: 'C:\Program Files\Java\jdk1.8.0_241\bin\java.exe'
description: This is test winsw";
void getId()
{
var id = ServiceDescriptorYaml.FromYaml(yml).Configurations.Id;
}
Assert.That(() => getId(), Throws.TypeOf<InvalidOperationException>());
}
[Test]
public void Default_value_map_test()
{
var executablePath = ServiceDescriptorYaml.FromYaml(MinimalYaml).Configurations.ExecutablePath;
Assert.IsNotNull(executablePath);
}
[Test]
public void Simple_download_parsing_test()
{
var yml = @"download:
-
from: www.sample.com
to: c://tmp
-
from: www.sample2.com
to: d://tmp
-
from: www.sample3.com
to: d://temp";
var configs = ServiceDescriptorYaml.FromYaml(yml).Configurations;
Assert.AreEqual(3, configs.Downloads.Count);
}
[Test]
public void Download_not_specified_test()
{
var yml = @"id: jenkins
name: No Service Account
";
var configs = ServiceDescriptorYaml.FromYaml(yml).Configurations;
Assert.DoesNotThrow(() =>
{
var dowloads = configs.Downloads;
});
}
[Test]
public void Service_account_not_specified_test()
{
var yml = @"id: jenkins
name: No Service Account
";
var configs = ServiceDescriptorYaml.FromYaml(yml).Configurations;
Assert.DoesNotThrow(() =>
{
var serviceAccount = configs.ServiceAccount.AllowServiceAcountLogonRight;
});
}
[Test]
public void Service_account_specified_but_fields_not_specified()
{
var yml = @"id: jenkins
name: No Service Account
serviceaccount:
user: testuser
";
var configs = ServiceDescriptorYaml.FromYaml(yml).Configurations;
Assert.DoesNotThrow(() =>
{
var user = configs.ServiceAccount.ServiceAccountUser;
var password = configs.ServiceAccount.ServiceAccountPassword;
var allowLogon = configs.ServiceAccount.AllowServiceAcountLogonRight;
var hasAccount = configs.ServiceAccount.HasServiceAccount();
});
}
}
}

View File

@ -59,6 +59,10 @@ namespace winswTests.Util
properties.Remove("Caption"); properties.Remove("Caption");
properties.Remove("Description"); properties.Remove("Description");
properties.Remove("Executable"); properties.Remove("Executable");
properties.Remove("BaseName");
properties.Remove("BasePath");
properties.Remove("Log");
properties.Remove("ServiceAccount");
return properties; return properties;
} }
} }