mirror of https://github.com/winsw/winsw
Upate extension yaml support
Remove old object query class and add methods to extension which can take yaml objects as aruments. Add unit test for runawayprocesskiller for yaml supportpull/638/head
parent
42df5eeff9
commit
8013ab3d9d
|
@ -2,6 +2,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
using WMI;
|
using WMI;
|
||||||
|
|
||||||
namespace WinSW.Configuration
|
namespace WinSW.Configuration
|
||||||
|
@ -132,7 +133,9 @@ namespace WinSW.Configuration
|
||||||
public bool BeepOnShutdown => false;
|
public bool BeepOnShutdown => false;
|
||||||
|
|
||||||
// Extensions
|
// Extensions
|
||||||
public object? ExtensionsConfiguration => null;
|
public XmlNode? ExtensionsConfiguration => null;
|
||||||
|
|
||||||
|
public object? YamlExtensionsConfiguration => null;
|
||||||
|
|
||||||
public string BaseName
|
public string BaseName
|
||||||
{
|
{
|
||||||
|
|
|
@ -74,7 +74,9 @@ namespace WinSW.Configuration
|
||||||
bool BeepOnShutdown { get; }
|
bool BeepOnShutdown { get; }
|
||||||
|
|
||||||
// Extensions
|
// Extensions
|
||||||
object? ExtensionsConfiguration { get; }
|
XmlNode? ExtensionsConfiguration { get; }
|
||||||
|
|
||||||
|
object? YamlExtensionsConfiguration { get; }
|
||||||
|
|
||||||
List<string> ExtensionIds { get; }
|
List<string> ExtensionIds { get; }
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
using WinSW.Native;
|
using WinSW.Native;
|
||||||
using WinSW.Util;
|
using WinSW.Util;
|
||||||
using WMI;
|
using WMI;
|
||||||
|
@ -98,9 +99,6 @@ namespace WinSW.Configuration
|
||||||
[YamlMember(Alias = "securityDescriptor")]
|
[YamlMember(Alias = "securityDescriptor")]
|
||||||
public string? SecurityDescriptorYaml { get; set; }
|
public string? SecurityDescriptorYaml { get; set; }
|
||||||
|
|
||||||
[YamlMember(Alias = "extensions")]
|
|
||||||
public object? YamlExtensions { get; set; }
|
|
||||||
|
|
||||||
public class YamlEnv
|
public class YamlEnv
|
||||||
{
|
{
|
||||||
[YamlMember(Alias = "name")]
|
[YamlMember(Alias = "name")]
|
||||||
|
@ -640,26 +638,32 @@ namespace WinSW.Configuration
|
||||||
|
|
||||||
public string LogMode => this.Log.Mode is null ? this.Defaults.LogMode : this.Log.Mode;
|
public string LogMode => this.Log.Mode is null ? this.Defaults.LogMode : this.Log.Mode;
|
||||||
|
|
||||||
public object? ExtensionsConfiguration => this.YamlExtensions;
|
// TODO - Extensions
|
||||||
|
public XmlNode? ExtensionsConfiguration => null;
|
||||||
|
|
||||||
|
// YAML Extension
|
||||||
|
[YamlMember(Alias = "extensions")]
|
||||||
|
public object? YamlExtensionsConfiguration { get; set; }
|
||||||
|
|
||||||
public List<string> ExtensionIds
|
public List<string> ExtensionIds
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var result = new List<string>(0);
|
var result = new List<string>();
|
||||||
var extensions = this.ExtensionsConfiguration;
|
|
||||||
|
|
||||||
if (extensions is null)
|
if (!(this.YamlExtensionsConfiguration is List<object> extensions))
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var extensionConfigListObject = new ObjectQuery(extensions).AsList<object>();
|
foreach (var item in extensions)
|
||||||
|
|
||||||
foreach (var item in extensionConfigListObject)
|
|
||||||
{
|
{
|
||||||
var configObject = new ObjectQuery(item);
|
if (!(item is Dictionary<object, object> dict))
|
||||||
var id = configObject.On("id").AsString();
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var id = (string)dict["id"];
|
||||||
result.Add(id);
|
result.Add(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using WinSW.Configuration;
|
using System.Xml;
|
||||||
using WinSW.Util;
|
using WinSW.Configuration;
|
||||||
|
|
||||||
namespace WinSW.Extensions
|
namespace WinSW.Extensions
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,12 @@ 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(IWinSWConfiguration descriptor, ObjectQuery settings)
|
public virtual void Configure(IWinSWConfiguration descriptor, XmlNode node)
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Configure(IWinSWConfiguration descriptor, object yamlObject)
|
||||||
{
|
{
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using WinSW.Configuration;
|
|
||||||
using WinSW.Util;
|
|
||||||
|
|
||||||
namespace WinSW.Extensions
|
|
||||||
{
|
|
||||||
public class ExtensionConfigurationProvider
|
|
||||||
{
|
|
||||||
private readonly IWinSWConfiguration serviceDescriptor;
|
|
||||||
|
|
||||||
private readonly List<WinSWExtensionConfiguration> extensionConfigList;
|
|
||||||
|
|
||||||
public ExtensionConfigurationProvider(IWinSWConfiguration serviceConfigs)
|
|
||||||
{
|
|
||||||
this.serviceDescriptor = serviceConfigs;
|
|
||||||
this.extensionConfigList = this.CreateExtensionConfigList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public WinSWExtensionConfiguration? GetExtenstionConfiguration(string id)
|
|
||||||
{
|
|
||||||
foreach (var item in this.extensionConfigList)
|
|
||||||
{
|
|
||||||
if (item.Id.Equals(id))
|
|
||||||
{
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<WinSWExtensionConfiguration> CreateExtensionConfigList()
|
|
||||||
{
|
|
||||||
var result = new List<WinSWExtensionConfiguration>(0);
|
|
||||||
|
|
||||||
var extensions = this.serviceDescriptor.ExtensionsConfiguration;
|
|
||||||
|
|
||||||
if (extensions is null)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
var extensionNodes = new ObjectQuery(extensions).AsList<object>();
|
|
||||||
|
|
||||||
foreach (var extension in extensionNodes)
|
|
||||||
{
|
|
||||||
var query = new ObjectQuery(extension);
|
|
||||||
|
|
||||||
var id = query.On("id").AsString();
|
|
||||||
var enabled = query.On("enabled").AsBool();
|
|
||||||
var className = query.On("classname").AsString();
|
|
||||||
var settings = query.On("settings").AsParent();
|
|
||||||
|
|
||||||
var extensionConfig = new WinSWExtensionConfiguration(id, enabled, className, settings);
|
|
||||||
result.Add(extensionConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
using WinSW.Configuration;
|
using System.Xml;
|
||||||
using WinSW.Util;
|
using WinSW.Configuration;
|
||||||
|
|
||||||
namespace WinSW.Extensions
|
namespace WinSW.Extensions
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,15 @@ namespace WinSW.Extensions
|
||||||
/// Init handler. Extension should load it's config during that step
|
/// Init handler. Extension should load it's config during that step
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="descriptor">Service descriptor</param>
|
/// <param name="descriptor">Service descriptor</param>
|
||||||
void Configure(IWinSWConfiguration descriptor, ObjectQuery settings);
|
/// <param name="node">Configuration node</param>
|
||||||
|
void Configure(IWinSWConfiguration descriptor, XmlNode node);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configure the extension from Yaml configuration
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor">Yaml Service Descptor</param>
|
||||||
|
/// <param name="yamlObject">Configuration Node</param>
|
||||||
|
void Configure(IWinSWConfiguration descriptor, object yamlObject);
|
||||||
|
|
||||||
/// <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.
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
using WinSW.Util;
|
|
||||||
|
|
||||||
namespace WinSW.Extensions
|
|
||||||
{
|
|
||||||
public class WinSWExtensionConfiguration
|
|
||||||
{
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
public bool Enabled { get; set; }
|
|
||||||
|
|
||||||
public string ClassName { get; set; }
|
|
||||||
|
|
||||||
public ObjectQuery Settings { get; set; }
|
|
||||||
|
|
||||||
public WinSWExtensionConfiguration(string id, bool enabled, string className, ObjectQuery settings)
|
|
||||||
{
|
|
||||||
this.Id = id;
|
|
||||||
this.Enabled = enabled;
|
|
||||||
this.ClassName = className;
|
|
||||||
this.Settings = settings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,7 @@
|
||||||
using System.Xml;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
using WinSW.Util;
|
using WinSW.Util;
|
||||||
|
|
||||||
namespace WinSW.Extensions
|
namespace WinSW.Extensions
|
||||||
|
@ -26,7 +29,7 @@ namespace WinSW.Extensions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ClassName { get; private set; }
|
public string ClassName { get; private set; }
|
||||||
|
|
||||||
public WinSWExtensionDescriptor(string id, string className, bool enabled)
|
private WinSWExtensionDescriptor(string id, string className, bool enabled)
|
||||||
{
|
{
|
||||||
this.Id = id;
|
this.Id = id;
|
||||||
this.Enabled = enabled;
|
this.Enabled = enabled;
|
||||||
|
@ -40,5 +43,19 @@ namespace WinSW.Extensions
|
||||||
string id = XmlHelper.SingleAttribute<string>(node, "id");
|
string id = XmlHelper.SingleAttribute<string>(node, "id");
|
||||||
return new WinSWExtensionDescriptor(id, className, enabled);
|
return new WinSWExtensionDescriptor(id, className, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static WinSWExtensionDescriptor FromYaml(object node)
|
||||||
|
{
|
||||||
|
if (!(node is Dictionary<object, object> config))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Cannot get the configuration entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enabled = ConfigHelper.YamlBoolParse((string)config["enabled"]);
|
||||||
|
string className = (string)config["classname"];
|
||||||
|
string id = (string)config["id"];
|
||||||
|
|
||||||
|
return new WinSWExtensionDescriptor(id, className, enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
using log4net;
|
using log4net;
|
||||||
using WinSW.Configuration;
|
using WinSW.Configuration;
|
||||||
|
|
||||||
|
@ -11,15 +13,12 @@ namespace WinSW.Extensions
|
||||||
|
|
||||||
public IWinSWConfiguration ServiceDescriptor { get; private set; }
|
public IWinSWConfiguration ServiceDescriptor { get; private set; }
|
||||||
|
|
||||||
public ExtensionConfigurationProvider ConfigProvider { get; private set; }
|
|
||||||
|
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
|
private static readonly ILog Log = LogManager.GetLogger(typeof(WinSWExtensionManager));
|
||||||
|
|
||||||
public WinSWExtensionManager(IWinSWConfiguration serviceDescriptor)
|
public WinSWExtensionManager(IWinSWConfiguration serviceDescriptor)
|
||||||
{
|
{
|
||||||
this.ServiceDescriptor = serviceDescriptor;
|
this.ServiceDescriptor = serviceDescriptor;
|
||||||
this.Extensions = new Dictionary<string, IWinSWExtension>();
|
this.Extensions = new Dictionary<string, IWinSWExtension>();
|
||||||
this.ConfigProvider = new ExtensionConfigurationProvider(this.ServiceDescriptor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -129,21 +128,34 @@ namespace WinSW.Extensions
|
||||||
throw new ExtensionException(id, "Extension has been already loaded");
|
throw new ExtensionException(id, "Extension has been already loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
var configNode = this.ConfigProvider.GetExtenstionConfiguration(id);
|
if (this.ServiceDescriptor.GetType() == typeof(ServiceDescriptor))
|
||||||
|
{
|
||||||
|
this.LoadExtensionFromXml(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.LoadExtensionFromYaml(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadExtensionFromXml(string id)
|
||||||
|
{
|
||||||
|
XmlNode? extensionsConfig = this.ServiceDescriptor.ExtensionsConfiguration;
|
||||||
|
XmlElement? configNode = extensionsConfig is null ? null : extensionsConfig.SelectSingleNode("extension[@id='" + id + "'][1]") as XmlElement;
|
||||||
if (configNode is null)
|
if (configNode is null)
|
||||||
{
|
{
|
||||||
throw new ExtensionException(id, "Cannot get the configuration entry");
|
throw new ExtensionException(id, "Cannot get the configuration entry");
|
||||||
}
|
}
|
||||||
|
|
||||||
var descriptor = new WinSWExtensionDescriptor(configNode.Id, configNode.ClassName, configNode.Enabled);
|
var descriptor = WinSWExtensionDescriptor.FromXml(configNode);
|
||||||
|
|
||||||
if (descriptor.Enabled)
|
if (descriptor.Enabled)
|
||||||
{
|
{
|
||||||
IWinSWExtension extension = this.CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
IWinSWExtension extension = this.CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
||||||
extension.Descriptor = descriptor;
|
extension.Descriptor = descriptor;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
extension.Configure(this.ServiceDescriptor, configNode.Settings);
|
extension.Configure(this.ServiceDescriptor, configNode);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{ // Consider any unexpected exception as fatal
|
{ // Consider any unexpected exception as fatal
|
||||||
|
@ -160,6 +172,67 @@ namespace WinSW.Extensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LoadExtensionFromYaml(string id)
|
||||||
|
{
|
||||||
|
var extensionConfigList = this.ServiceDescriptor.YamlExtensionsConfiguration as List<object>;
|
||||||
|
|
||||||
|
if (extensionConfigList is null)
|
||||||
|
{
|
||||||
|
throw new ExtensionException(id, "Cannot get the configuration entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
object? configNode = GetYamlonfigById(extensionConfigList, id);
|
||||||
|
|
||||||
|
if (configNode is null)
|
||||||
|
{
|
||||||
|
throw new ExtensionException(id, "Cannot get the configuration entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptor = WinSWExtensionDescriptor.FromYaml(configNode);
|
||||||
|
|
||||||
|
if (descriptor.Enabled)
|
||||||
|
{
|
||||||
|
IWinSWExtension extension = this.CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
|
||||||
|
extension.Descriptor = descriptor;
|
||||||
|
|
||||||
|
if (!(configNode is Dictionary<object, object> dict))
|
||||||
|
{
|
||||||
|
// TODO : Replace with exntension exception
|
||||||
|
throw new InvalidDataException("Error in config node");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
extension.Configure(this.ServiceDescriptor, dict["settings"]);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{ // Consider any unexpected exception as fatal
|
||||||
|
Log.Fatal("Failed to configure the extension " + id, ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Extensions.Add(id, extension);
|
||||||
|
Log.Info("Extension loaded: " + id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Warn("Extension is disabled: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
object? GetYamlonfigById(List<object> configs, string id)
|
||||||
|
{
|
||||||
|
foreach (var item in configs)
|
||||||
|
{
|
||||||
|
if (item is Dictionary<object, object> config && config["id"].Equals(id))
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IWinSWExtension CreateExtensionInstance(string id, string className)
|
private IWinSWExtension CreateExtensionInstance(string id, string className)
|
||||||
{
|
{
|
||||||
object created;
|
object created;
|
||||||
|
|
|
@ -208,7 +208,7 @@ namespace WinSW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*public List<string> ExtensionIds
|
public List<string> ExtensionIds
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -227,18 +227,9 @@ namespace WinSW
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
public List<string> ExtensionIds
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new List<string>(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// public XmlNode? ExtensionsConfiguration => this.dom.SelectSingleNode("//extensions");
|
public XmlNode? ExtensionsConfiguration => this.dom.SelectSingleNode("//extensions");
|
||||||
public object? ExtensionsConfiguration { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Combines the contents of all the elements of the given name,
|
/// Combines the contents of all the elements of the given name,
|
||||||
|
@ -712,5 +703,7 @@ namespace WinSW
|
||||||
|
|
||||||
return environment;
|
return environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object? YamlExtensionsConfiguration => null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,5 +33,17 @@ namespace WinSW.Util
|
||||||
{ "day", 1000L * 60L * 60L * 24L },
|
{ "day", 1000L * 60L * 60L * 24L },
|
||||||
{ "days", 1000L * 60L * 60L * 24L }
|
{ "days", 1000L * 60L * 60L * 24L }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static bool YamlBoolParse(string value)
|
||||||
|
{
|
||||||
|
value = value.ToLower();
|
||||||
|
|
||||||
|
if (value.Equals("true") || value.Equals("yes") || value.Equals("on"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace WinSW.Util
|
|
||||||
{
|
|
||||||
public class ObjectQuery
|
|
||||||
{
|
|
||||||
private readonly object configObject;
|
|
||||||
private object? current;
|
|
||||||
private string? key;
|
|
||||||
|
|
||||||
public ObjectQuery(object? config)
|
|
||||||
{
|
|
||||||
if (config is null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Query object is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.configObject = config;
|
|
||||||
this.current = this.configObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectQuery On(string key)
|
|
||||||
{
|
|
||||||
this.key = key;
|
|
||||||
this.current = this.Query(this.configObject, key);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectQuery Get(string key)
|
|
||||||
{
|
|
||||||
if (this.current == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("The key <" + key + "> is not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.key = key;
|
|
||||||
this.current = this.Query(this.current, key);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string AsString()
|
|
||||||
{
|
|
||||||
if (this.current == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("The key <" + this.key + "> is not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = this.current as string;
|
|
||||||
|
|
||||||
if (result == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException(this.key + " can't converto to a string");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> AsList<T>()
|
|
||||||
{
|
|
||||||
if (this.current == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("The key <" + this.key + "> is not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = this.current as List<object>;
|
|
||||||
|
|
||||||
if (list == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException(this.key + " can't converto to List<" + typeof(T) + ">");
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new List<T>(0);
|
|
||||||
foreach (var item in list)
|
|
||||||
{
|
|
||||||
result.Add((T)item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AsBool()
|
|
||||||
{
|
|
||||||
if (this.current == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("The key <" + this.key + "> is not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
var value = this.current as string;
|
|
||||||
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException(this.key + " can't convert into bool");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == "true" || value == "yes" || value == "on")
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (value == "false" || value == "no" || value == "off")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidDataException(value + " cannot convert into bool");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectQuery At(int index)
|
|
||||||
{
|
|
||||||
if (this.current == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("The key <" + this.key + "> is not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = this.current as List<object>;
|
|
||||||
|
|
||||||
if (list == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Can't execute At(index) on " + this.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var result = list[index];
|
|
||||||
this.current = result;
|
|
||||||
}
|
|
||||||
catch (IndexOutOfRangeException)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Index " + index + " not in range");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object? Query(object? dic, string key)
|
|
||||||
{
|
|
||||||
if (dic == null)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException(key + " is not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
var dict = dic as IDictionary<object, object>;
|
|
||||||
if (dict != null)
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<object, object> kvp in dict)
|
|
||||||
{
|
|
||||||
if (kvp.Key as string == key)
|
|
||||||
{
|
|
||||||
return kvp.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectQuery AsParent()
|
|
||||||
{
|
|
||||||
return new ObjectQuery(this.current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Xml;
|
||||||
using log4net;
|
using log4net;
|
||||||
using WinSW.Configuration;
|
using WinSW.Configuration;
|
||||||
using WinSW.Extensions;
|
using WinSW.Extensions;
|
||||||
|
@ -179,20 +181,39 @@ namespace WinSW.Plugins.RunawayProcessKiller
|
||||||
return parameters.Environment;
|
return parameters.Environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Configure(IWinSWConfiguration descriptor, ObjectQuery settings)
|
public override void Configure(IWinSWConfiguration descriptor, XmlNode node)
|
||||||
{
|
{
|
||||||
this.Pidfile = settings.On("pidfile").AsString();
|
// We expect the upper logic to process any errors
|
||||||
this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse(settings.On("stopTimeOut").AsString()));
|
// TODO: a better parser API for types would be useful
|
||||||
this.StopParentProcessFirst = settings.On("StopParentFirst").AsBool();
|
this.Pidfile = XmlHelper.SingleElement(node, "pidfile", false)!;
|
||||||
|
this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse(XmlHelper.SingleElement(node, "stopTimeout", false)!));
|
||||||
|
this.StopParentProcessFirst = bool.Parse(XmlHelper.SingleElement(node, "stopParentFirst", false)!);
|
||||||
|
this.ServiceId = descriptor.Id;
|
||||||
|
// TODO: Consider making it documented
|
||||||
|
var checkWinSWEnvironmentVariable = XmlHelper.SingleElement(node, "checkWinSWEnvironmentVariable", true);
|
||||||
|
this.CheckWinSWEnvironmentVariable = checkWinSWEnvironmentVariable is null ? true : bool.Parse(checkWinSWEnvironmentVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Configure(IWinSWConfiguration descriptor, object yamlObject)
|
||||||
|
{
|
||||||
|
if (!(yamlObject is Dictionary<object, object> dict))
|
||||||
|
{
|
||||||
|
// TODO : throw ExtensionException
|
||||||
|
throw new InvalidDataException("Cann't configure");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Pidfile = (string)dict["pidfile"];
|
||||||
|
this.StopTimeout = TimeSpan.FromMilliseconds(int.Parse((string)dict["stopTimeOut"]));
|
||||||
|
this.StopParentProcessFirst = bool.Parse((string)dict["StopParentFirst"]);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.CheckWinSWEnvironmentVariable = settings.Get("checkWinSWEnvironmentVariable").AsBool();
|
this.CheckWinSWEnvironmentVariable = bool.Parse((string)dict["checkWinSWEnvironmentVariable"]);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
this.CheckWinSWEnvironmentVariable = true;
|
this.CheckWinSWEnvironmentVariable = true;
|
||||||
}
|
}
|
||||||
this.ServiceId = descriptor.Id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
using log4net;
|
using log4net;
|
||||||
using WinSW.Configuration;
|
using WinSW.Configuration;
|
||||||
using WinSW.Extensions;
|
using WinSW.Extensions;
|
||||||
|
@ -26,18 +28,41 @@ namespace WinSW.Plugins.SharedDirectoryMapper
|
||||||
this._entries.Add(config);
|
this._entries.Add(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Configure(IWinSWConfiguration descriptor, ObjectQuery settings)
|
public override void Configure(IWinSWConfiguration descriptor, XmlNode node)
|
||||||
{
|
{
|
||||||
var maps = settings.On("mapping").AsList<object>();
|
XmlNodeList? mapNodes = XmlHelper.SingleNode(node, "mapping", false)!.SelectNodes("map");
|
||||||
|
if (mapNodes != null)
|
||||||
foreach (var map in maps)
|
|
||||||
{
|
{
|
||||||
var mapObject = new ObjectQuery(map);
|
for (int i = 0; i < mapNodes.Count; i++)
|
||||||
var enable = mapObject.On("enabled").AsBool();
|
{
|
||||||
var label = mapObject.On("label").AsString();
|
if (mapNodes[i] is XmlElement mapElement)
|
||||||
var uncpath = mapObject.On("uncpath").AsString();
|
{
|
||||||
|
var config = SharedDirectoryMapperConfig.FromXml(mapElement);
|
||||||
|
this._entries.Add(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var config = new SharedDirectoryMapperConfig(enable, label, uncpath);
|
public override void Configure(IWinSWConfiguration descriptor, object yamlObject)
|
||||||
|
{
|
||||||
|
if (!(yamlObject is Dictionary<object, object> dict))
|
||||||
|
{
|
||||||
|
// TODO : throw ExtensionException
|
||||||
|
throw new InvalidDataException("Conn't configure");
|
||||||
|
}
|
||||||
|
|
||||||
|
var mappingNode = dict["mapping"];
|
||||||
|
|
||||||
|
if (!(mappingNode is List<object> mappings))
|
||||||
|
{
|
||||||
|
// TODO : throw ExtensionException
|
||||||
|
throw new InvalidDataException("Conn't configure");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var map in mappings)
|
||||||
|
{
|
||||||
|
var config = SharedDirectoryMapperConfig.FromYaml(map);
|
||||||
this._entries.Add(config);
|
this._entries.Add(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Xml;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
using WinSW.Util;
|
using WinSW.Util;
|
||||||
|
|
||||||
namespace WinSW.Plugins.SharedDirectoryMapper
|
namespace WinSW.Plugins.SharedDirectoryMapper
|
||||||
|
@ -26,5 +28,20 @@ namespace WinSW.Plugins.SharedDirectoryMapper
|
||||||
string uncPath = XmlHelper.SingleAttribute<string>(node, "uncpath");
|
string uncPath = XmlHelper.SingleAttribute<string>(node, "uncpath");
|
||||||
return new SharedDirectoryMapperConfig(enableMapping, label, uncPath);
|
return new SharedDirectoryMapperConfig(enableMapping, label, uncPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SharedDirectoryMapperConfig FromYaml(object yamlObject)
|
||||||
|
{
|
||||||
|
if (!(yamlObject is Dictionary<object, object> dict))
|
||||||
|
{
|
||||||
|
// TODO : throw ExtensionExeption
|
||||||
|
throw new InvalidDataException("SharedDirectoryMapperConfig config error");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enableMapping = ConfigHelper.YamlBoolParse((string)dict["enabled"]);
|
||||||
|
string label = (string)dict["label"];
|
||||||
|
string uncPath = (string)dict["uncpath"];
|
||||||
|
|
||||||
|
return new SharedDirectoryMapperConfig(enableMapping, label, uncPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using WinSW;
|
using WinSW;
|
||||||
|
using WinSW.Configuration;
|
||||||
using WinSW.Extensions;
|
using WinSW.Extensions;
|
||||||
using WinSW.Plugins.RunawayProcessKiller;
|
using WinSW.Plugins.RunawayProcessKiller;
|
||||||
using WinSW.Util;
|
using WinSW.Util;
|
||||||
|
@ -14,7 +15,8 @@ namespace winswTests.Extensions
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
class RunawayProcessKillerExtensionTest : ExtensionTestBase
|
class RunawayProcessKillerExtensionTest : ExtensionTestBase
|
||||||
{
|
{
|
||||||
ServiceDescriptor _testServiceDescriptor;
|
IWinSWConfiguration _testServiceDescriptor;
|
||||||
|
IWinSWConfiguration _testServiceDescriptorYaml;
|
||||||
|
|
||||||
readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
||||||
|
|
||||||
|
@ -38,6 +40,29 @@ $@"<service>
|
||||||
</extensions>
|
</extensions>
|
||||||
</service>";
|
</service>";
|
||||||
this._testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
this._testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
|
||||||
|
|
||||||
|
string seedYaml = $@"---
|
||||||
|
id: jenkins
|
||||||
|
name: Jenkins
|
||||||
|
description: This service runs Jenkins automation server.
|
||||||
|
env:
|
||||||
|
-
|
||||||
|
name: JENKINS_HOME
|
||||||
|
value: '%LocalAppData%\Jenkins.jenkins'
|
||||||
|
executable: java
|
||||||
|
arguments: >-
|
||||||
|
-Xrs -Xmx256m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle
|
||||||
|
-jar E:\Winsw Test\yml6\jenkins.war --httpPort=8081
|
||||||
|
extensions:
|
||||||
|
- id: killRunawayProcess
|
||||||
|
enabled: yes
|
||||||
|
classname: ""{this.testExtension}""
|
||||||
|
settings:
|
||||||
|
pidfile: 'foo/bar/pid.txt'
|
||||||
|
stopTimeOut: 5000
|
||||||
|
StopParentFirst: true";
|
||||||
|
|
||||||
|
this._testServiceDescriptorYaml = ServiceDescriptorYaml.FromYaml(seedYaml).Configurations;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -55,6 +80,21 @@ $@"<service>
|
||||||
Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one");
|
Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void LoadExtensionsYaml()
|
||||||
|
{
|
||||||
|
WinSWExtensionManager manager = new WinSWExtensionManager(this._testServiceDescriptorYaml);
|
||||||
|
manager.LoadExtensions();
|
||||||
|
Assert.AreEqual(1, manager.Extensions.Count, "One extension should be loaded");
|
||||||
|
|
||||||
|
// Check the file is correct
|
||||||
|
var extension = manager.Extensions["killRunawayProcess"] as RunawayProcessKillerExtension;
|
||||||
|
Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded");
|
||||||
|
Assert.AreEqual("foo/bar/pid.txt", extension.Pidfile, "Loaded PID file path is not equal to the expected one");
|
||||||
|
Assert.AreEqual(5000, extension.StopTimeout.TotalMilliseconds, "Loaded Stop Timeout is not equal to the expected one");
|
||||||
|
Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one");
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void StartStopExtension()
|
public void StartStopExtension()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
using NUnit.Framework;
|
|
||||||
using WinSW;
|
|
||||||
using WinSW.Configuration;
|
|
||||||
using WinSW.Extensions;
|
|
||||||
using WinSW.Plugins.RunawayProcessKiller;
|
|
||||||
using WinSW.Util;
|
|
||||||
using winswTests.Extensions;
|
|
||||||
|
|
||||||
namespace winswTests
|
|
||||||
{
|
|
||||||
public class YamlExtensionTest : ExtensionTestBase
|
|
||||||
{
|
|
||||||
private IWinSWConfiguration _testServiceDescriptor { get; set; }
|
|
||||||
|
|
||||||
readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(RunawayProcessKillerExtension));
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void SetUp()
|
|
||||||
{
|
|
||||||
|
|
||||||
string yamlDoc = $@"id: SERVICE_NAME
|
|
||||||
name: Jenkins Slave
|
|
||||||
description: This service runs Jenkins automation server.
|
|
||||||
executable: java
|
|
||||||
arguments: -Xrs -jar \""%BASE%\slave.jar\"" -jnlpUrl ...
|
|
||||||
extensions:
|
|
||||||
- id: killOnStartup
|
|
||||||
enabled: yes
|
|
||||||
classname: ""{this.testExtension}""
|
|
||||||
settings:
|
|
||||||
pidfile: pid.txt
|
|
||||||
stopTimeOut: 5000
|
|
||||||
StopParentFirst: true";
|
|
||||||
|
|
||||||
this._testServiceDescriptor = ServiceDescriptorYaml.FromYaml(yamlDoc).Configurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void LoadExtensions()
|
|
||||||
{
|
|
||||||
|
|
||||||
WinSWExtensionManager manager = new WinSWExtensionManager(this._testServiceDescriptor);
|
|
||||||
manager.LoadExtensions();
|
|
||||||
Assert.AreEqual(1, manager.Extensions.Count, "One extension should be loaded");
|
|
||||||
|
|
||||||
// Check the file is correct
|
|
||||||
var extension = manager.Extensions["killRunawayProcess"] as RunawayProcessKillerExtension;
|
|
||||||
Assert.IsNotNull(extension, "RunawayProcessKillerExtension should be loaded");
|
|
||||||
Assert.AreEqual("foo/bar/pid.txt", extension.Pidfile, "Loaded PID file path is not equal to the expected one");
|
|
||||||
Assert.AreEqual(5000, extension.StopTimeout.TotalMilliseconds, "Loaded Stop Timeout is not equal to the expected one");
|
|
||||||
Assert.AreEqual(true, extension.StopParentProcessFirst, "Loaded StopParentFirst is not equal to the expected one");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Sample_test()
|
|
||||||
{
|
|
||||||
ExtensionConfigurationProvider provider = new ExtensionConfigurationProvider(this._testServiceDescriptor);
|
|
||||||
var config = provider.GetExtenstionConfiguration("killOnStartup");
|
|
||||||
|
|
||||||
var pid = config.Settings.On("pidfile").AsString();
|
|
||||||
var stopTimeOut = config.Settings.On("stopTimeOut").AsString();
|
|
||||||
var StopParentFirst = config.Settings.On("StopParentFirst").AsString();
|
|
||||||
|
|
||||||
System.Console.WriteLine(pid);
|
|
||||||
System.Console.WriteLine(stopTimeOut);
|
|
||||||
System.Console.WriteLine(StopParentFirst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue