mirror of https://github.com/winsw/winsw
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
parent
ae10abe857
commit
10d3a6113f
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -22,7 +23,9 @@ namespace WinSW
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>();
|
||||||
|
|
|
@ -16,7 +16,9 @@ namespace WinSW
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ServiceDescriptor : IWinSWConfiguration
|
public class ServiceDescriptor : IWinSWConfiguration
|
||||||
{
|
{
|
||||||
|
#pragma warning disable S2755 // XML parsers should not be vulnerable to XXE attacks
|
||||||
protected readonly XmlDocument dom = new XmlDocument();
|
protected readonly XmlDocument dom = new XmlDocument();
|
||||||
|
#pragma warning restore S2755 // XML parsers should not be vulnerable to XXE attacks
|
||||||
|
|
||||||
private readonly Dictionary<string, string> environmentVariables;
|
private readonly Dictionary<string, string> environmentVariables;
|
||||||
|
|
||||||
|
@ -108,9 +110,11 @@ namespace WinSW
|
||||||
|
|
||||||
public static ServiceDescriptor FromXML(string xml)
|
public static ServiceDescriptor FromXML(string xml)
|
||||||
{
|
{
|
||||||
var dom = new XmlDocument();
|
#pragma warning disable S2755 // XML parsers should not be vulnerable to XXE attacks
|
||||||
dom.LoadXml(xml);
|
var xmlDom = new XmlDocument();
|
||||||
return new ServiceDescriptor(dom);
|
#pragma warning restore S2755 // XML parsers should not be vulnerable to XXE attacks
|
||||||
|
xmlDom.LoadXml(xml);
|
||||||
|
return new ServiceDescriptor(xmlDom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string SingleElement(string tagName)
|
private string SingleElement(string tagName)
|
||||||
|
@ -143,7 +147,7 @@ namespace WinSW
|
||||||
return e is null ? defaultValue : int.Parse(e.InnerText);
|
return e is null ? defaultValue : int.Parse(e.InnerText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TimeSpan SingleTimeSpanElement(XmlNode parent, string tagName, TimeSpan defaultValue)
|
private TimeSpan SingleTimeSpanElement(string tagName, TimeSpan defaultValue)
|
||||||
{
|
{
|
||||||
string? value = this.SingleElement(tagName, true);
|
string? value = this.SingleElement(tagName, true);
|
||||||
return value is null ? defaultValue : this.ParseTimeSpan(value);
|
return value is null ? defaultValue : this.ParseTimeSpan(value);
|
||||||
|
@ -327,17 +331,7 @@ namespace WinSW
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// LogDirectory is the service wrapper executable directory or the optionally specified logpath element.
|
/// LogDirectory is the service wrapper executable directory or the optionally specified logpath element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LogDirectory
|
public string LogDirectory { get => this.Log.Directory; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
XmlNode? loggingNode = this.dom.SelectSingleNode("//logpath");
|
|
||||||
|
|
||||||
return loggingNode is null
|
|
||||||
? Defaults.LogDirectory
|
|
||||||
: Environment.ExpandEnvironmentVariables(loggingNode.InnerText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LogMode
|
public string LogMode
|
||||||
{
|
{
|
||||||
|
@ -375,114 +369,135 @@ namespace WinSW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OutFileDisabled => this.SingleBoolElement("outfiledisabled", Defaults.OutFileDisabled);
|
public Log Log
|
||||||
|
|
||||||
public bool ErrFileDisabled => this.SingleBoolElement("errfiledisabled", Defaults.ErrFileDisabled);
|
|
||||||
|
|
||||||
public string OutFilePattern
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNode? loggingName = this.dom.SelectSingleNode("//outfilepattern");
|
return new XmlLogSettings(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class XmlLogSettings : Log
|
||||||
|
{
|
||||||
|
private readonly ServiceDescriptor d;
|
||||||
|
|
||||||
|
public XmlLogSettings(ServiceDescriptor d)
|
||||||
|
{
|
||||||
|
this.d = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XmlElement E
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
XmlElement? e = (XmlElement?)this.d.dom.SelectSingleNode("//logmode");
|
||||||
|
|
||||||
|
// this is more modern way, to support nested elements as configuration
|
||||||
|
e ??= (XmlElement?)this.d.dom.SelectSingleNode("//log")!; // WARNING: NRE
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string? Mode { get => this.d.LogMode; }
|
||||||
|
|
||||||
|
public override string Name { get => this.d.LogName; }
|
||||||
|
|
||||||
|
public override string Directory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
XmlNode? loggingNode = this.d.dom.SelectSingleNode("//logpath");
|
||||||
|
|
||||||
|
return loggingNode is null
|
||||||
|
? Defaults.LogDirectory
|
||||||
|
: Environment.ExpandEnvironmentVariables(loggingNode.InnerText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int? SizeThreshold { get => this.d.SingleIntElement(this.E, "sizeThreshold", 10 * 1024); }
|
||||||
|
|
||||||
|
public override int? KeepFiles { get => this.d.SingleIntElement(this.E, "keepFiles", SizeBasedRollingLogAppender.DefaultFilesToKeep); }
|
||||||
|
|
||||||
|
public override int? Period { get => this.d.SingleIntElement(this.E, "period", 1); }
|
||||||
|
|
||||||
|
public override string Pattern
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
XmlNode? patternNode = this.E.SelectSingleNode("pattern");
|
||||||
|
if (patternNode is null)
|
||||||
|
{
|
||||||
|
#pragma warning disable S2372 // Exceptions should not be thrown from property getters
|
||||||
|
throw new InvalidDataException("Time Based rolling policy is specified but no pattern can be found in configuration XML.");
|
||||||
|
#pragma warning restore S2372 // Exceptions should not be thrown from property getters
|
||||||
|
}
|
||||||
|
|
||||||
|
return patternNode.InnerText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OutFileDisabled => this.d.SingleBoolElement("outfiledisabled", Defaults.OutFileDisabled);
|
||||||
|
|
||||||
|
public override bool ErrFileDisabled => this.d.SingleBoolElement("errfiledisabled", Defaults.ErrFileDisabled);
|
||||||
|
|
||||||
|
public override string OutFilePattern
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
XmlNode? loggingName = this.d.dom.SelectSingleNode("//outfilepattern");
|
||||||
|
|
||||||
return loggingName is null ? Defaults.OutFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
return loggingName is null ? Defaults.OutFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ErrFilePattern
|
public override string ErrFilePattern
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlNode? loggingName = this.dom.SelectSingleNode("//errfilepattern");
|
XmlNode? loggingName = this.d.dom.SelectSingleNode("//errfilepattern");
|
||||||
|
|
||||||
return loggingName is null ? Defaults.ErrFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
return loggingName is null ? Defaults.ErrFilePattern : Environment.ExpandEnvironmentVariables(loggingName.InnerText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LogHandler LogHandler
|
public override string? AutoRollAtTime
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
XmlElement? e = (XmlElement?)this.dom.SelectSingleNode("//logmode");
|
XmlNode? autoRollAtTimeNode = this.E.SelectSingleNode("autoRollAtTime");
|
||||||
|
return autoRollAtTimeNode?.InnerText;
|
||||||
// this is more modern way, to support nested elements as configuration
|
}
|
||||||
e ??= (XmlElement?)this.dom.SelectSingleNode("//log")!; // WARNING: NRE
|
|
||||||
|
|
||||||
int sizeThreshold;
|
|
||||||
switch (this.LogMode)
|
|
||||||
{
|
|
||||||
case "rotate":
|
|
||||||
return new SizeBasedRollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
|
|
||||||
|
|
||||||
case "none":
|
|
||||||
return new IgnoreLogAppender();
|
|
||||||
|
|
||||||
case "reset":
|
|
||||||
return new ResetLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
|
|
||||||
|
|
||||||
case "roll":
|
|
||||||
return new RollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
|
|
||||||
|
|
||||||
case "roll-by-time":
|
|
||||||
XmlNode? patternNode = e.SelectSingleNode("pattern");
|
|
||||||
if (patternNode is null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Time Based rolling policy is specified but no pattern can be found in configuration XML.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var pattern = patternNode.InnerText;
|
public override int? ZipOlderThanNumDays
|
||||||
int period = this.SingleIntElement(e, "period", 1);
|
|
||||||
return new TimeBasedRollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, pattern, period);
|
|
||||||
|
|
||||||
case "roll-by-size":
|
|
||||||
sizeThreshold = this.SingleIntElement(e, "sizeThreshold", 10 * 1024) * SizeBasedRollingLogAppender.BytesPerKB;
|
|
||||||
int keepFiles = this.SingleIntElement(e, "keepFiles", SizeBasedRollingLogAppender.DefaultFilesToKeep);
|
|
||||||
return new SizeBasedRollingLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, sizeThreshold, keepFiles);
|
|
||||||
|
|
||||||
case "append":
|
|
||||||
return new DefaultLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern);
|
|
||||||
|
|
||||||
case "roll-by-size-time":
|
|
||||||
sizeThreshold = this.SingleIntElement(e, "sizeThreshold", 10 * 1024) * RollingSizeTimeLogAppender.BytesPerKB;
|
|
||||||
XmlNode? filePatternNode = e.SelectSingleNode("pattern");
|
|
||||||
if (filePatternNode is null)
|
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but no pattern can be found in configuration XML.");
|
get
|
||||||
}
|
|
||||||
|
|
||||||
XmlNode? autoRollAtTimeNode = e.SelectSingleNode("autoRollAtTime");
|
|
||||||
TimeSpan? autoRollAtTime = null;
|
|
||||||
if (autoRollAtTimeNode != null)
|
|
||||||
{
|
{
|
||||||
// validate it
|
XmlNode? zipolderthannumdaysNode = this.E.SelectSingleNode("zipOlderThanNumDays");
|
||||||
if (!TimeSpan.TryParse(autoRollAtTimeNode.InnerText, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
XmlNode? zipolderthannumdaysNode = e.SelectSingleNode("zipOlderThanNumDays");
|
|
||||||
int? zipolderthannumdays = null;
|
int? zipolderthannumdays = null;
|
||||||
if (zipolderthannumdaysNode != null)
|
if (zipolderthannumdaysNode != null)
|
||||||
{
|
{
|
||||||
// validate it
|
// validate it
|
||||||
if (!int.TryParse(zipolderthannumdaysNode.InnerText, out int zipolderthannumdaysValue))
|
if (!int.TryParse(zipolderthannumdaysNode.InnerText, out int zipolderthannumdaysValue))
|
||||||
{
|
{
|
||||||
|
#pragma warning disable S2372 // Exceptions should not be thrown from property getters
|
||||||
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but zipOlderThanNumDays does not match the int format found in configuration XML.");
|
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but zipOlderThanNumDays does not match the int format found in configuration XML.");
|
||||||
|
#pragma warning restore S2372 // Exceptions should not be thrown from property getters
|
||||||
}
|
}
|
||||||
|
|
||||||
zipolderthannumdays = zipolderthannumdaysValue;
|
zipolderthannumdays = zipolderthannumdaysValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlNode? zipdateformatNode = e.SelectSingleNode("zipDateFormat");
|
return zipolderthannumdays;
|
||||||
string zipdateformat = zipdateformatNode is null ? "yyyyMM" : zipdateformatNode.InnerText;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new RollingSizeTimeLogAppender(this.LogDirectory, this.LogName, this.OutFileDisabled, this.ErrFileDisabled, this.OutFilePattern, this.ErrFilePattern, sizeThreshold, filePatternNode.InnerText, autoRollAtTime, zipolderthannumdays, zipdateformat);
|
public override string? ZipDateFormat
|
||||||
|
{
|
||||||
default:
|
get
|
||||||
throw new InvalidDataException("Undefined logging mode: " + this.LogMode);
|
{
|
||||||
|
XmlNode? zipdateformatNode = this.E.SelectSingleNode("zipDateFormat");
|
||||||
|
return zipdateformatNode is null ? null : zipdateformatNode.InnerText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,14 +578,14 @@ namespace WinSW
|
||||||
/// Before the specified amount of time has elapsed, the service should make its next call to the SetServiceStatus function
|
/// Before the specified amount of time has elapsed, the service should make its next call to the SetServiceStatus function
|
||||||
/// with either an incremented checkPoint value or a change in currentState. (see http://msdn.microsoft.com/en-us/library/ms685996.aspx)
|
/// with either an incremented checkPoint value or a change in currentState. (see http://msdn.microsoft.com/en-us/library/ms685996.aspx)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan WaitHint => this.SingleTimeSpanElement(this.dom, "waithint", Defaults.WaitHint);
|
public TimeSpan WaitHint => this.SingleTimeSpanElement("waithint", Defaults.WaitHint);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time before the service should make its next call to the SetServiceStatus function
|
/// The time before the service should make its next call to the SetServiceStatus function
|
||||||
/// with an incremented checkPoint value (default 1 sec).
|
/// with an incremented checkPoint value (default 1 sec).
|
||||||
/// Do not wait longer than the wait hint. A good interval is one-tenth of the wait hint but not less than 1 second and not more than 10 seconds.
|
/// Do not wait longer than the wait hint. A good interval is one-tenth of the wait hint but not less than 1 second and not more than 10 seconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan SleepTime => this.SingleTimeSpanElement(this.dom, "sleeptime", Defaults.SleepTime);
|
public TimeSpan SleepTime => this.SingleTimeSpanElement("sleeptime", Defaults.SleepTime);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the service can interact with the desktop.
|
/// True if the service can interact with the desktop.
|
||||||
|
@ -639,59 +654,60 @@ namespace WinSW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimeSpan ResetFailureAfter => this.SingleTimeSpanElement(this.dom, "resetfailure", Defaults.ResetFailureAfter);
|
public TimeSpan ResetFailureAfter => this.SingleTimeSpanElement("resetfailure", Defaults.ResetFailureAfter);
|
||||||
|
|
||||||
protected string? GetServiceAccountPart(string subNodeName)
|
protected string? GetServiceAccountPart(XmlNode node, string subNodeName)
|
||||||
{
|
|
||||||
XmlNode? node = this.dom.SelectSingleNode("//serviceaccount");
|
|
||||||
|
|
||||||
if (node != null)
|
|
||||||
{
|
{
|
||||||
XmlNode? subNode = node.SelectSingleNode(subNodeName);
|
XmlNode? subNode = node.SelectSingleNode(subNodeName);
|
||||||
if (subNode != null)
|
if (subNode != null)
|
||||||
{
|
{
|
||||||
return subNode.InnerText;
|
return subNode.InnerText;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected string? AllowServiceLogon => this.GetServiceAccountPart("allowservicelogon");
|
private bool ParseAllowServiceAcountLogonRight(string? logonRight)
|
||||||
|
|
||||||
protected internal string? ServiceAccountDomain => this.GetServiceAccountPart("domain");
|
|
||||||
|
|
||||||
protected internal string? ServiceAccountName => this.GetServiceAccountPart("user");
|
|
||||||
|
|
||||||
public string? ServiceAccountPassword => this.GetServiceAccountPart("password");
|
|
||||||
|
|
||||||
public string? ServiceAccountUser => this.ServiceAccountName is null ? null : (this.ServiceAccountDomain ?? ".") + "\\" + this.ServiceAccountName;
|
|
||||||
|
|
||||||
public bool HasServiceAccount()
|
|
||||||
{
|
{
|
||||||
return !string.IsNullOrEmpty(this.ServiceAccountName);
|
if (logonRight != null && bool.TryParse(logonRight, out bool parsedvalue))
|
||||||
}
|
|
||||||
|
|
||||||
public bool AllowServiceAcountLogonRight
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (this.AllowServiceLogon != null)
|
|
||||||
{
|
|
||||||
if (bool.TryParse(this.AllowServiceLogon, out bool parsedvalue))
|
|
||||||
{
|
{
|
||||||
return parsedvalue;
|
return parsedvalue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceAccount ServiceAccount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
XmlNode? node = this.dom.SelectSingleNode("//serviceaccount");
|
||||||
|
|
||||||
|
if (node is null)
|
||||||
|
{
|
||||||
|
return Defaults.ServiceAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceAccount = Defaults.ServiceAccount;
|
||||||
|
|
||||||
|
serviceAccount.ServiceAccountDomain = this.GetServiceAccountPart(node, "domain");
|
||||||
|
|
||||||
|
serviceAccount.ServiceAccountName = this.GetServiceAccountPart(node, "user");
|
||||||
|
|
||||||
|
serviceAccount.ServiceAccountPassword = this.GetServiceAccountPart(node, "password");
|
||||||
|
|
||||||
|
var loginRight = this.GetServiceAccountPart(node, "allowservicelogon");
|
||||||
|
|
||||||
|
serviceAccount.AllowServiceAcountLogonRight = this.ParseAllowServiceAcountLogonRight(loginRight);
|
||||||
|
|
||||||
|
return serviceAccount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time to wait for the service to gracefully shutdown the executable before we forcibly kill it
|
/// Time to wait for the service to gracefully shutdown the executable before we forcibly kill it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan StopTimeout => this.SingleTimeSpanElement(this.dom, "stoptimeout", Defaults.StopTimeout);
|
public TimeSpan StopTimeout => this.SingleTimeSpanElement("stoptimeout", Defaults.StopTimeout);
|
||||||
|
|
||||||
public bool StopParentProcessFirst
|
public bool StopParentProcessFirst
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using WinSW;
|
using WinSW;
|
||||||
|
@ -109,21 +109,21 @@ $@"<service>
|
||||||
[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]
|
||||||
|
@ -197,7 +197,7 @@ $@"<service>
|
||||||
+ "</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]
|
||||||
|
@ -208,7 +208,7 @@ $@"<service>
|
||||||
+ "</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]
|
||||||
|
@ -219,7 +219,7 @@ $@"<service>
|
||||||
+ "</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]
|
||||||
|
@ -230,7 +230,7 @@ $@"<service>
|
||||||
+ "</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]
|
||||||
|
@ -247,7 +247,7 @@ $@"<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));
|
||||||
|
@ -267,7 +267,7 @@ $@"<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"));
|
||||||
|
@ -288,7 +288,7 @@ $@"<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"));
|
||||||
|
@ -307,7 +307,7 @@ $@"<service>
|
||||||
+ "</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]
|
||||||
|
@ -321,7 +321,7 @@ $@"<service>
|
||||||
+ "</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]
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue