diff --git a/.gitignore b/.gitignore index 4f5e66d..9eb5712 100644 --- a/.gitignore +++ b/.gitignore @@ -221,7 +221,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -317,7 +317,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -326,10 +326,8 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ Elfo.Wardein.Core/Assets/WardeinConfig.json Elfo.Wardein.Core/Assets/MailConfig.json -Elfo.Wardein.Integrations.Tests/appsettings.json -/Elfo.Wardein.Integrations/appsettings.json -/Elfo.Wardein.Core.Tests/appsettings.json +*/appsettings.json diff --git a/Elfo.Wardein.APIs/Abstractions/IAmRouteImplementation.cs b/Elfo.Wardein.APIs/Abstractions/IAmRouteImplementation.cs index 8b46de5..dad6891 100644 --- a/Elfo.Wardein.APIs/Abstractions/IAmRouteImplementation.cs +++ b/Elfo.Wardein.APIs/Abstractions/IAmRouteImplementation.cs @@ -10,7 +10,7 @@ public abstract class IAmRouteImplementation { public IAmRouteImplementation(bool blockActionIfInMaintenanceMode) { - if (blockActionIfInMaintenanceMode && ServicesContainer.WardeinConfigurationManager(Const.WARDEIN_CONFIG_PATH).IsInMaintenanceMode) + if (blockActionIfInMaintenanceMode && ServicesContainer.WardeinConfigurationManager().IsInMaintenanceMode) { throw new InvalidOperationException("Wardein is in maintenance mode. Please try again later"); } diff --git a/Elfo.Wardein.APIs/RouteImplementations/ConfigsImplementation.cs b/Elfo.Wardein.APIs/RouteImplementations/ConfigsImplementation.cs index 306d801..4be758c 100644 --- a/Elfo.Wardein.APIs/RouteImplementations/ConfigsImplementation.cs +++ b/Elfo.Wardein.APIs/RouteImplementations/ConfigsImplementation.cs @@ -15,8 +15,8 @@ public ConfigsImplementation() : base(blockActionIfInMaintenanceMode: false) { } public async Task Invalidate(HttpContext context) { - ServicesContainer.MailConfigurationManager(Const.MAIL_CONFIG_PATH).InvalidateCache(); - ServicesContainer.WardeinConfigurationManager(Const.WARDEIN_CONFIG_PATH).InvalidateCache(); + ServicesContainer.MailConfigurationManager().InvalidateCache(); + ServicesContainer.WardeinConfigurationManager().InvalidateCache(); await new PollingImplementation().Restart(context); await context.Response.WriteAsync($"Cached configs invalidated"); } diff --git a/Elfo.Wardein.APIs/RouteImplementations/MaintenanceImplementation.cs b/Elfo.Wardein.APIs/RouteImplementations/MaintenanceImplementation.cs index b0f4a09..dec74fa 100644 --- a/Elfo.Wardein.APIs/RouteImplementations/MaintenanceImplementation.cs +++ b/Elfo.Wardein.APIs/RouteImplementations/MaintenanceImplementation.cs @@ -21,20 +21,20 @@ public Task StartMaintenanceMode(HttpContext context) if (!double.TryParse(context.GetRouteData().Values["durationInSeconds"]?.ToString(), out double durationInSecond)) durationInSecond = TimeSpan.FromMinutes(5).TotalSeconds; // Default value - ServicesContainer.WardeinConfigurationManager(Const.WARDEIN_CONFIG_PATH).StartMaintenanceMode(durationInSecond); + ServicesContainer.WardeinConfigurationManager().StartMaintenanceMode(durationInSecond); return context.Response.WriteAsync($"Maintenance Mode Started"); } public Task StopMaintenanceMode(HttpContext context) { - ServicesContainer.WardeinConfigurationManager(Const.WARDEIN_CONFIG_PATH).StopMaintenaceMode(); + ServicesContainer.WardeinConfigurationManager().StopMaintenaceMode(); return context.Response.WriteAsync($"Maintenance Mode Stopped"); } public Task GetMaintenanceModeStatus(HttpContext context) { var result = "Wardein is not in maintenance mode"; - if (ServicesContainer.WardeinConfigurationManager(Const.WARDEIN_CONFIG_PATH).IsInMaintenanceMode) + if (ServicesContainer.WardeinConfigurationManager().IsInMaintenanceMode) result = "Wardein is in maintenance mode"; return context.Response.WriteAsync(result); } diff --git a/Elfo.Wardein.APIs/RouteImplementations/ServiceManagerImplementation.cs b/Elfo.Wardein.APIs/RouteImplementations/ServiceManagerImplementation.cs index cbf3857..7fa5948 100644 --- a/Elfo.Wardein.APIs/RouteImplementations/ServiceManagerImplementation.cs +++ b/Elfo.Wardein.APIs/RouteImplementations/ServiceManagerImplementation.cs @@ -31,12 +31,12 @@ public Task StopService(HttpContext context) return context.Response.WriteAsync($"Service {serviceName} stopped"); } - public Task GetServiceStatus(HttpContext context) + public async Task GetServiceStatus(HttpContext context) { string serviceName = context.GetRouteValue("name").ToString(); - var status = GetServiceStatus(serviceName); - return context.Response.WriteAsync(status); + var status = await GetServiceStatus(serviceName); + return status; // TODO: print on webpage } public Task StartService(HttpContext context) @@ -72,12 +72,12 @@ public Task StopPool(HttpContext context) return context.Response.WriteAsync($"ApplicationPool {applicationPoolName} stopped"); } - public Task GetPoolStatus(HttpContext context) + public async Task GetPoolStatus(HttpContext context) { string applicationPoolName = context.GetRouteValue("name").ToString(); - var status = GetIISPoolStatus(applicationPoolName); - return context.Response.WriteAsync(status); + var status = await GetIISPoolStatus(applicationPoolName); + return status; // TODO: print in webpage } #endregion @@ -98,14 +98,14 @@ public Task RestartServiceAndPool(HttpContext context) #region Local Functions - void RestartService(string serviceName) => new WindowsServiceManager(serviceName).Restart(); - void StopService(string serviceName) => new WindowsServiceManager(serviceName).Stop(); - void StartService(string serviceName) => new WindowsServiceManager(serviceName).Start(); - string GetServiceStatus(string serviceName) => new WindowsServiceManager(serviceName).GetStatus(); - void RefreshIISPool(string iisPoolName) => new IISPoolManager(iisPoolName).Restart(); - void StopIISPool(string issPoolName) => new IISPoolManager(issPoolName).Stop(); - string GetIISPoolStatus(string iisPoolName) => new IISPoolManager(iisPoolName).GetStatus(); - void StartIISPool(string iisPoolName) => new IISPoolManager(iisPoolName).Start(); + async Task RestartService(string serviceName) => await new WindowsServiceManager(serviceName).Restart(); + async Task StopService(string serviceName) => await new WindowsServiceManager(serviceName).Stop(); + async Task StartService(string serviceName) => await new WindowsServiceManager(serviceName).Start(); + async Task GetServiceStatus(string serviceName) => await new WindowsServiceManager(serviceName).GetStatus(); + async Task RefreshIISPool(string iisPoolName) => await new IISPoolManager(iisPoolName).Restart(); + async Task StopIISPool(string issPoolName) => await new IISPoolManager(issPoolName).Stop(); + async Task GetIISPoolStatus(string iisPoolName) => await new IISPoolManager(iisPoolName).GetStatus(); + async Task StartIISPool(string iisPoolName) => await new IISPoolManager(iisPoolName).Start(); #endregion } diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/GenericServiceModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/GenericServiceModel.cs deleted file mode 100644 index bea8e9e..0000000 --- a/Elfo.Wardein.Abstractions/Configuration/Models/GenericServiceModel.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Elfo.Firmenich.Wardein.Abstractions.Configuration.Models -{ - public class GenericServiceModel - { - public class ObservarableGenericServiceWatcher - { - [JsonProperty(PropertyName = "serviceName")] - public string ServiceName { get; set; } - - [JsonProperty(PropertyName = "maxRetryCount")] - public int MaxRetryCount { get; set; } - - [JsonProperty(PropertyName = "failMessage")] - public string FailMessage { get; set; } - - [JsonProperty(PropertyName = "restoredMessage")] - public string RestoredMessage { get; set; } - - [JsonProperty(PropertyName = "recipientAddress")] - public string RecipientAddress { get; set; } - - [JsonProperty(PropertyName = "notificationType")] - public string NotificationType { get; set; } = "Mail"; - - [JsonProperty(PropertyName = "sendRepeatedNotificationAfterSeconds")] - public double? SendRepeatedNotificationAfterSeconds { get; set; } - - [JsonProperty(PropertyName = "numberOfNotificationsWithoutRateLimitation")] - public int? NumberOfNotificationsWithoutRateLimitation { get; set; } - - [JsonProperty(PropertyName = "serviceManagerType")] - public ServiceManagerType ServiceManagerType { get; set; } - } - } -} diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/ServiceManagerType.cs b/Elfo.Wardein.Abstractions/Configuration/Models/ServiceManagerType.cs index d8940c9..fadf7a1 100644 --- a/Elfo.Wardein.Abstractions/Configuration/Models/ServiceManagerType.cs +++ b/Elfo.Wardein.Abstractions/Configuration/Models/ServiceManagerType.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Elfo.Firmenich.Wardein.Abstractions.Configuration.Models +namespace Elfo.Wardein.Abstractions.Configuration.Models { public enum ServiceManagerType { diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WardeinConfig.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WardeinConfig.cs index 68549bc..24ceac5 100644 --- a/Elfo.Wardein.Abstractions/Configuration/Models/WardeinConfig.cs +++ b/Elfo.Wardein.Abstractions/Configuration/Models/WardeinConfig.cs @@ -1,4 +1,4 @@ -using Elfo.Firmenich.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Abstractions.Configuration.Models; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -11,6 +11,9 @@ public class WardeinConfig [JsonProperty(PropertyName = "timeSpanFromSeconds")] public double TimeSpanFromSeconds { get; set; } + [JsonProperty(PropertyName = "connectionString")] + public string ConnectionString { get; set; } + [JsonProperty(PropertyName = "sendRepeatedNotificationAfterSeconds")] public double SendRepeatedNotificationAfterSeconds { get; set; } = 3600; // Default values @@ -28,13 +31,19 @@ public class WardeinConfig MaintenanceModeStartDateInUTC = DateTime.UtcNow }; // Default values + [JsonProperty(PropertyName = "heartbeat")] + public HeartbeatConfigurationModel Heartbeat { get; set; } = new HeartbeatConfigurationModel(); + [JsonProperty(PropertyName = "services")] - public IEnumerable Services { get; set; } + public IEnumerable Services { get; set; } [JsonProperty(PropertyName = "iisPools")] - public IEnumerable IISPools { get; set; } + public IEnumerable IISPools { get; set; } [JsonProperty(PropertyName = "urls")] - public IEnumerable Urls { get; set; } + public IEnumerable Urls { get; set; } + + [JsonProperty(PropertyName = "cleanUps")] + public IEnumerable CleanUps { get; set; } } } diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherConfigurationModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherConfigurationModel.cs deleted file mode 100644 index 0c4a589..0000000 --- a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherConfigurationModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Elfo.Firmenich.Wardein.Abstractions.Configuration.Models -{ - class WatcherConfigurationModel - { - } -} diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/Abstractions/IAmBaseConfigurationModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/Abstractions/IAmBaseConfigurationModel.cs new file mode 100644 index 0000000..05539c8 --- /dev/null +++ b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/Abstractions/IAmBaseConfigurationModel.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Elfo.Wardein.Abstractions.Configuration.Models +{ + public interface IAmBaseConfigurationModel + { + [JsonProperty(PropertyName = "isInMaintenanceMode")] + bool IsInMaintenanceMode { get; } + [JsonProperty(PropertyName = "timeSpanFromSeconds")] + double TimeSpanFromSeconds { get; } + [JsonProperty(PropertyName = "watcherConfigurationId")] + public int WatcherConfigurationId { get; } + [JsonProperty(PropertyName = "applicationId")] + public int ApplicationId { get; } + [JsonProperty(PropertyName = "applicationHostname")] + public string ApplicationHostname { get; } + } +} diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/Abstractions/IAmConfigurationModelWithResolution.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/Abstractions/IAmConfigurationModelWithResolution.cs new file mode 100644 index 0000000..7337c60 --- /dev/null +++ b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/Abstractions/IAmConfigurationModelWithResolution.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Elfo.Wardein.Abstractions.Configuration.Models +{ + public interface IAmConfigurationModelWithResolution : IAmBaseConfigurationModel + { + [JsonProperty(PropertyName = "notificationtype")] + public NotificationType NotificationType { get; set; } + + [JsonProperty(PropertyName = "recipientAddresses")] + public string RecipientAddresses { get; set; } + + [JsonProperty(PropertyName = "failureMessage")] + public string FailureMessage { get; set; } + + [JsonProperty(PropertyName = "restoredMessage")] + public string RestoredMessage { get; set; } + + [JsonProperty(PropertyName = "maxRetryCount")] + public int MaxRetryCount { get; set; } + + [JsonProperty(PropertyName = "sendReminderEmailAfterRetryCount")] + public int SendReminderEmailAfterRetryCount { get; set; } + } +} diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/FileSystemConfigurationModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/FileSystemConfigurationModel.cs new file mode 100644 index 0000000..3c99980 --- /dev/null +++ b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/FileSystemConfigurationModel.cs @@ -0,0 +1,69 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Elfo.Wardein.Abstractions.Configuration.Models +{ + public class FileSystemConfigurationModel : IAmBaseConfigurationModel + { + /// + /// Property defines if watcher has to be running in maintainance mode + /// Default value false + /// + [JsonProperty(PropertyName = "isInMaintenanceMode")] + public bool IsInMaintenanceMode { get; set; } = false; + + /// + /// Property that defines frequency of watcher polling + /// Default value 10 seconds + /// + [JsonProperty(PropertyName = "timeSpanFromSeconds")] + public double TimeSpanFromSeconds { get; set; } = 300; + + /// + /// List of folder configurations that has to be monitored and cleaned by criteria + /// + [JsonProperty(PropertyName = "cleanUps")] + public IEnumerable CleanUps { get; set; } = new List(); + + public int WatcherConfigurationId { get; set; } + + public int ApplicationId { get; set; } + + public string ApplicationHostname => HostHelper.GetName(); + } + + public class FileSystemCleanUpConfig + { + [JsonProperty(PropertyName = "filePath")] + public string FilePath { get; set; } + + [JsonProperty(PropertyName = "timeSpanFromSeconds")] + public double TimeSpanFromSeconds { get; set; } = 10; // Default values + + [JsonProperty(PropertyName = "cleanUpOptions")] + public FileSystemCleanUpOptions CleanUpOptions { get; set; } = new FileSystemCleanUpOptions(); + } + + public class FileSystemCleanUpOptions + { + [JsonProperty(PropertyName = "thresholdInSeconds")] + public int ThresholdInSeconds { get; set; } + + [JsonProperty(PropertyName = "thresholdInDays")] + public int ThresholdInDays { get; set; } + + [JsonProperty(PropertyName = "displayOnly")] + public bool DisplayOnly { get; set; } = false; + + [JsonProperty(PropertyName = "useRecycleBin")] + public bool UseRecycleBin { get; set; } = false; + + [JsonProperty(PropertyName = "removeEmptyFolders")] + public bool RemoveEmptyFolders { get; set; } = false; + + [JsonProperty(PropertyName = "recursive")] + public bool Recursive { get; set; } = false; + } +} diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/GenericServiceConfigurationModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/GenericServiceConfigurationModel.cs new file mode 100644 index 0000000..3eaf19e --- /dev/null +++ b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/GenericServiceConfigurationModel.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Elfo.Wardein.Abstractions.Configuration.Models +{ + public class GenericServiceConfigurationModel : IAmConfigurationModelWithResolution + { + [JsonProperty(PropertyName = "serviceName")] + public string ServiceName { get; set; } + [JsonProperty(PropertyName = "serviceManagerType")] + public ServiceManagerType ServiceManagerType { get; set; } + public bool IsInMaintenanceMode { get; set; } + public double TimeSpanFromSeconds { get; set; } = 60; + public int WatcherConfigurationId { get; set; } + public int ApplicationId { get; set; } + public string ApplicationHostname => HostHelper.GetName(); + public string RecipientAddresses { get; set; } + public string FailureMessage { get; set; } + public int SendReminderEmailAfterRetryCount { get; set; } = 120; + public NotificationType NotificationType { get; set; } = NotificationType.Mail; + public string RestoredMessage { get; set; } + public int MaxRetryCount { get; set; } = 2; + } +} diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/HeartbeatConfigurationModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/HeartbeatConfigurationModel.cs new file mode 100644 index 0000000..8c52144 --- /dev/null +++ b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/HeartbeatConfigurationModel.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Elfo.Wardein.Abstractions.Configuration.Models +{ + public class HeartbeatConfigurationModel : IAmBaseConfigurationModel + { + public string ApplicationHostname => HostHelper.GetName(); + + public bool IsInMaintenanceMode => false; + + public double TimeSpanFromSeconds => 60; + + public int WatcherConfigurationId { get; set; } + + public int ApplicationId { get; set; } + } +} diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/WebWatcherConfigurationModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/WebWatcherConfigurationModel.cs new file mode 100644 index 0000000..ad6f832 --- /dev/null +++ b/Elfo.Wardein.Abstractions/Configuration/Models/WatcherModels/WebWatcherConfigurationModel.cs @@ -0,0 +1,47 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Elfo.Wardein.Abstractions.Configuration.Models +{ + public class WebWatcherConfigurationModel : IAmConfigurationModelWithResolution + { + [JsonProperty(PropertyName = "associatedIISPool")] + public string AssociatedIISPool { get; set; } + + [JsonProperty(PropertyName = "url")] + public Uri Url { get; set; } + + [JsonProperty(PropertyName = "urlAlias")] + public string UrlAlias { get; set; } + + [JsonProperty(PropertyName = "assertWithRegex")] + public string AssertWithRegex { get; set; } + + [JsonProperty(PropertyName = "assertWithStatusCode")] + public bool AssertWithStatusCode { get; set; } = true; + + [JsonProperty(PropertyName = "isInMaintenanceMode")] + public bool IsInMaintenanceMode { get; set; } = false; + + [JsonProperty(PropertyName = "timeSpanFromSeconds")] + public double TimeSpanFromSeconds { get; set; } = 60; + + [JsonProperty(PropertyName = "useAuthentication")] + public bool UseAuthentication { get; set; } + + [JsonProperty(PropertyName = "authCredentials")] + public string AuthCredentials { get; set; } + + public int WatcherConfigurationId { get; set; } + public int ApplicationId { get; set; } + public string ApplicationHostname => HostHelper.GetName(); + public NotificationType NotificationType { get; set; } = NotificationType.Mail; + public string RecipientAddresses { get; set; } + public string FailureMessage { get; set; } + public string RestoredMessage { get; set; } + public int MaxRetryCount { get; set; } = 2; + public int SendReminderEmailAfterRetryCount { get; set; } = 120; + } +} diff --git a/Elfo.Wardein.Abstractions/Configuration/Models/WebWatcherUrlModel.cs b/Elfo.Wardein.Abstractions/Configuration/Models/WebWatcherUrlModel.cs deleted file mode 100644 index 5f7f8c4..0000000 --- a/Elfo.Wardein.Abstractions/Configuration/Models/WebWatcherUrlModel.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Elfo.Firmenich.Wardein.Abstractions.Configuration.Models -{ - public class WebWatcherUrlModel - { - [JsonProperty(PropertyName = "isInMaintenanceMode")] - public bool IsInMaintenanceMode { get; protected set; } = false; - - [JsonProperty(PropertyName = "timeSpanFromSeconds")] - public double TimeSpanFromSeconds { get; protected set; } = 10; - - [JsonProperty(PropertyName = "useAuthentication")] - public bool UseAuthentication { get; protected set; } - - [JsonProperty(PropertyName = "authCredentials")] - public string AuthCredentials { get; protected set; } - - [JsonProperty(PropertyName = "assertWithRegex")] - public string AssertWithRegex { get; protected set; } - - [JsonProperty(PropertyName = "assertWithStatusCode")] - public bool AssertWithStatusCode { get; protected set; } - - [JsonProperty(PropertyName = "maxRetryCount")] - public int MaxRetryCount { get; protected set; } - - [JsonProperty(PropertyName = "sendReminderEmailAfterRetryCount")] - public int SendReminderEmailAfterRetryCount { get; protected set; } - - [JsonProperty(PropertyName = "associatedIISPool")] - public string AssociatedIISPool { get; protected set; } - - [JsonProperty(PropertyName = "recipientAddresses")] - public string RecipientAddresses { get; protected set; } - - [JsonProperty(PropertyName = "failureMessage")] - public string FailureMessage { get; protected set; } - - [JsonProperty(PropertyName = "restoredMessage")] - public string RestoredMessage { get; protected set; } - - [JsonProperty(PropertyName = "connectionString")] - public string ConnectionString { get; protected set; } - - [JsonProperty(PropertyName = "watcherConfigurationId")] - public int WatcherConfigurationId { get; protected set; } - [JsonProperty(PropertyName = "applicationId")] - public int ApplicationId { get; protected set; } - [JsonProperty(PropertyName = "applicationHostname")] - public string ApplicationHostname { get; protected set; } - - [JsonProperty(PropertyName = "url")] - public Uri Url { get; protected set; } - - [JsonProperty(PropertyName = "urlAlias")] - public string UrlAlias { get; protected set; } - } -} diff --git a/Elfo.Wardein.Abstractions/Elfo.Wardein.Abstractions.csproj b/Elfo.Wardein.Abstractions/Elfo.Wardein.Abstractions.csproj index 7cd486c..6dc4194 100644 --- a/Elfo.Wardein.Abstractions/Elfo.Wardein.Abstractions.csproj +++ b/Elfo.Wardein.Abstractions/Elfo.Wardein.Abstractions.csproj @@ -3,8 +3,8 @@ netstandard2.1 latest - Elfo.Firmenich.Wardein.Abstractions - Elfo.Firmenich.Wardein.Abstractions + Elfo.Wardein.Abstractions + Elfo.Wardein.Abstractions diff --git a/Elfo.Wardein.Abstractions/HeartBeat/IAmWardeinHeartBeatPersistanceService.cs b/Elfo.Wardein.Abstractions/HeartBeat/IAmWardeinHeartBeatPersistanceService.cs index edf04b1..e98dd5b 100644 --- a/Elfo.Wardein.Abstractions/HeartBeat/IAmWardeinHeartBeatPersistanceService.cs +++ b/Elfo.Wardein.Abstractions/HeartBeat/IAmWardeinHeartBeatPersistanceService.cs @@ -3,7 +3,7 @@ using System.Text; using System.Threading.Tasks; -namespace Elfo.Firmenich.Wardein.Abstractions.HeartBeat +namespace Elfo.Wardein.Abstractions.HeartBeat { public interface IAmWardeinHeartBeatPersistanceService { diff --git a/Elfo.Wardein.Abstractions/Helpers/HostHelper.cs b/Elfo.Wardein.Abstractions/Helpers/HostHelper.cs new file mode 100644 index 0000000..dfd714a --- /dev/null +++ b/Elfo.Wardein.Abstractions/Helpers/HostHelper.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Elfo.Wardein.Abstractions +{ + public static class HostHelper + { + public static string GetName() + { +#if DEBUG + return "SRVWEB07"; +#else + return Dns.GetHostName()?.ToUpperInvariant(); +#endif + } + } +} diff --git a/Elfo.Wardein.Abstractions/Services/IAmServiceManager.cs b/Elfo.Wardein.Abstractions/Services/IAmServiceManager.cs index bf6ed42..83364e5 100644 --- a/Elfo.Wardein.Abstractions/Services/IAmServiceManager.cs +++ b/Elfo.Wardein.Abstractions/Services/IAmServiceManager.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; namespace Elfo.Wardein.Abstractions.Services { @@ -16,11 +17,11 @@ public IAmServiceManager(string serviceName) this.serviceName = serviceName; } - public abstract bool IsStillAlive { get; } - public abstract void ForceKill(); - public abstract void Restart(); - public abstract void Start(); - public abstract void Stop(); - public abstract string GetStatus(); + public abstract Task IsStillAlive(); + public abstract Task ForceKill(); + public abstract Task Restart(); + public abstract Task Start(); + public abstract Task Stop(); + public abstract Task GetStatus(); } } \ No newline at end of file diff --git a/Elfo.Wardein.Core/NotificationService/NotificationType.cs b/Elfo.Wardein.Abstractions/Services/Models/NotificationType.cs similarity index 83% rename from Elfo.Wardein.Core/NotificationService/NotificationType.cs rename to Elfo.Wardein.Abstractions/Services/Models/NotificationType.cs index e9a63f0..92d38b2 100644 --- a/Elfo.Wardein.Core/NotificationService/NotificationType.cs +++ b/Elfo.Wardein.Abstractions/Services/Models/NotificationType.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using System.Text; -namespace Elfo.Wardein.Core.NotificationService +namespace Elfo.Wardein.Abstractions { public enum NotificationType { diff --git a/Elfo.Wardein.Abstractions/Watchers/IAmWatcherPersistenceService.cs b/Elfo.Wardein.Abstractions/Watchers/IAmWatcherPersistenceService.cs index 0186190..5dd49af 100644 --- a/Elfo.Wardein.Abstractions/Watchers/IAmWatcherPersistenceService.cs +++ b/Elfo.Wardein.Abstractions/Watchers/IAmWatcherPersistenceService.cs @@ -3,7 +3,7 @@ using System.Text; using System.Threading.Tasks; -namespace Elfo.Firmenich.Wardein.Abstractions.Watchers +namespace Elfo.Wardein.Abstractions.Watchers { public interface IAmWatcherPersistenceService { diff --git a/Elfo.Wardein.Abstractions/Watchers/Models/WatcherStatusResult.cs b/Elfo.Wardein.Abstractions/Watchers/Models/WatcherStatusResult.cs index ff783a5..96d1e73 100644 --- a/Elfo.Wardein.Abstractions/Watchers/Models/WatcherStatusResult.cs +++ b/Elfo.Wardein.Abstractions/Watchers/Models/WatcherStatusResult.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Elfo.Firmenich.Wardein.Abstractions.Watchers +namespace Elfo.Wardein.Abstractions.Watchers { public class WatcherStatusResult { diff --git a/Elfo.Wardein.Abstractions/WebWatcher/IAmUrlResponseManager.cs b/Elfo.Wardein.Abstractions/WebWatcher/IAmUrlResponseManager.cs index 896d2a4..068baff 100644 --- a/Elfo.Wardein.Abstractions/WebWatcher/IAmUrlResponseManager.cs +++ b/Elfo.Wardein.Abstractions/WebWatcher/IAmUrlResponseManager.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; -namespace Elfo.Firmenich.Wardein.Abstractions.WebWatcher +namespace Elfo.Wardein.Abstractions.WebWatcher { public interface IAmUrlResponseManager { diff --git a/Elfo.Wardein.Core.Tests/Elfo.Wardein.Core.Tests.csproj b/Elfo.Wardein.Core.Tests/Elfo.Wardein.Core.Tests.csproj index fed634b..d684acd 100644 --- a/Elfo.Wardein.Core.Tests/Elfo.Wardein.Core.Tests.csproj +++ b/Elfo.Wardein.Core.Tests/Elfo.Wardein.Core.Tests.csproj @@ -21,6 +21,7 @@ + diff --git a/Elfo.Wardein.Core.Tests/OracleWardeinConfigurationManagerTest.cs b/Elfo.Wardein.Core.Tests/OracleWardeinConfigurationManagerTest.cs index 9cbb64e..977b4ce 100644 --- a/Elfo.Wardein.Core.Tests/OracleWardeinConfigurationManagerTest.cs +++ b/Elfo.Wardein.Core.Tests/OracleWardeinConfigurationManagerTest.cs @@ -1,9 +1,12 @@ -using Elfo.Firmenich.Wardein.Core.ConfigurationManagers; +using Elfo.Wardein.Core.ConfigurationManagers; +using Elfo.Wardein.Abstractions.Configuration.Models; using Elfo.Wardein.Core.Helpers; using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace Elfo.Wardein.Core.Tests @@ -11,34 +14,21 @@ namespace Elfo.Wardein.Core.Tests [TestClass] public class OracleWardeinConfigurationManagerTest { - private string connectionString = string.Empty; - private IConfiguration configuration; - private OracleConnectionConfiguration config; - - [TestInitialize] - public void Initialize() - { - this.configuration = new ConfigurationBuilder() - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .Build(); - - connectionString = configuration["ConnectionStrings:Db"]; - - this.config = new OracleConnectionConfiguration.Builder(this.connectionString) - .WithClientId(configuration["Oracle:ClientId"]) - .WithClientInfo(configuration["Oracle:ClientInfo"]) - .WithModuleName(configuration["Oracle:ModuleName"]) - .WithDateLanguage(configuration["Oracle:DateLanguage"]) - .Build(); - } [TestMethod] - [TestCategory("ManualTest")] public void CanReadConfigurations() { - var manager = new OracleWardeinConfigurationManager(config, "SRVWEB07"); + var oracleHelper = Substitute.For(); + oracleHelper.Query(Arg.Any(), Arg.Any>()).Returns(new List() + { + new WardeinConfigurationModel() { WatcherConfigurationId = 1, ApplicationHostname = "SRVWEB07", ApplicationId = 1, WardeinConfig = "{\"timeSpanFromSeconds\":60}", WatcherTypeJsonConfig = "{}", WatcherJsonConfig = "{\"services\":[{\"serviceName\":\"MSMQ\",\"maxRetryCount\":2,\"serviceManagerType\":0}]}" }, + new WardeinConfigurationModel() { WatcherConfigurationId = 2, ApplicationHostname = "SRVWEB07", ApplicationId = 1, WardeinConfig = "{\"timeSpanFromSeconds\":60}", WatcherTypeJsonConfig = "{}", WatcherJsonConfig = "{\"iisPools\":[{\"serviceName\":\"Elfo.Wardein\",\"maxRetryCount\":2,\"serviceManagerType\":1}]}" } + }); + var manager = new OracleWardeinConfigurationManager(oracleHelper, "SRVWEB07"); var configs = manager.GetConfiguration(); - Assert.IsNotNull(configs); + Assert.AreEqual(60, configs.TimeSpanFromSeconds); + Assert.AreEqual(1, configs.IISPools.Count()); + Assert.AreEqual(1, configs.Services.Count()); } } } diff --git a/Elfo.Wardein.Core.Tests/OracleWatcherPersistanceServiceTest.cs b/Elfo.Wardein.Core.Tests/OracleWatcherPersistanceServiceTest.cs index 4f375a2..7a8ae40 100644 --- a/Elfo.Wardein.Core.Tests/OracleWatcherPersistanceServiceTest.cs +++ b/Elfo.Wardein.Core.Tests/OracleWatcherPersistanceServiceTest.cs @@ -1,6 +1,6 @@ -using Elfo.Firmenich.Wardein.Abstractions.Watchers; -using Elfo.Firmenich.Wardein.Abstractions.WebWatcher; -using Elfo.Firmenich.Wardein.Core.ServiceManager; +using Elfo.Wardein.Abstractions.Watchers; +using Elfo.Wardein.Abstractions.WebWatcher; +using Elfo.Wardein.Core.ServiceManager; using Elfo.Wardein.Core.Helpers; using Elfo.Wardein.Integrations.Oracle.Integration; using Elfo.Wardein.Watchers.WebWatcher; @@ -38,7 +38,7 @@ public void Initialize() .Build(); oracleIntegration = new OracleIntegration(OracleConnectionConfiguration); - watcherPersistenceService = ServicesContainer.WatcherPersistenceService(connectionString); + watcherPersistenceService = ServicesContainer.WatcherPersistenceService(); } [TestMethod] diff --git a/Elfo.Wardein.Core/ConfigurationManagers/MailConfigurationManagerFromJSON.cs b/Elfo.Wardein.Core/ConfigurationManagers/MailConfigurationManagerFromJSON.cs index 2f66c30..a66096f 100644 --- a/Elfo.Wardein.Core/ConfigurationManagers/MailConfigurationManagerFromJSON.cs +++ b/Elfo.Wardein.Core/ConfigurationManagers/MailConfigurationManagerFromJSON.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Text; -namespace Elfo.Wardein.Core.ConfigurationReader +namespace Elfo.Wardein.Core.ConfigurationManagers { public class MailConfigurationManagerFromJSON : IAmMailConfigurationManager { diff --git a/Elfo.Wardein.Core/ConfigurationManagers/OracleWardeinConfigurationManager.cs b/Elfo.Wardein.Core/ConfigurationManagers/OracleWardeinConfigurationManager.cs index f9778c8..05b9c36 100644 --- a/Elfo.Wardein.Core/ConfigurationManagers/OracleWardeinConfigurationManager.cs +++ b/Elfo.Wardein.Core/ConfigurationManagers/OracleWardeinConfigurationManager.cs @@ -1,27 +1,24 @@ using Elfo.Wardein.Abstractions.Configuration; using Elfo.Wardein.Abstractions.Configuration.Models; using Elfo.Wardein.Core.Helpers; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Oracle.ManagedDataAccess.Client; using System; using System.Collections.Generic; using System.Linq; -using System.Text; -namespace Elfo.Firmenich.Wardein.Core.ConfigurationManagers +namespace Elfo.Wardein.Core.ConfigurationManagers { public class OracleWardeinConfigurationManager : IAmWardeinConfigurationManager { private readonly OracleConnectionConfiguration configuration; + private readonly IOracleHelper oracleHelper; private readonly string hostname; - private readonly OracleHelper oracleHelper; - public OracleWardeinConfigurationManager(OracleConnectionConfiguration configuration, string hostname) + public OracleWardeinConfigurationManager(IOracleHelper oracleHelper, string hostname) { - this.configuration = configuration; + this.oracleHelper = oracleHelper; this.hostname = hostname; - this.oracleHelper = new OracleHelper(configuration); } public bool IsInMaintenanceMode => throw new NotImplementedException(); @@ -34,23 +31,21 @@ public WardeinConfig GetConfiguration() ["APPL_HOSTNAME"] = new OracleParameter("APPL_HOSTNAME", OracleDbType.Varchar2).Value = hostname }; var query = @"SELECT * FROM V_WRD_WATCHERS WHERE ""ApplicationHostname"" = :APPL_HOSTNAME"; - var result = this.oracleHelper.Query(query, parameters); + var waredinWatcherConfigs = this.oracleHelper.Query(query, parameters); // TODO: Test to see if it works.. code will probably need refactoring - //var json = JObject.Parse(result.FirstOrDefault().WardeinConfig); - //foreach (var watcherConfig in result) - //{ - // var watcherTypeConfigTest = JObject.Parse(watcherConfig.WatcherTypeJsonConfig); - // var watcherConfigTest = JObject.Parse(watcherConfig.WatcherJsonConfig); - // watcherTypeConfigTest.Merge(watcherConfigTest); - // json.Merge(watcherTypeConfigTest); - //} + var wardeinConfig = JObject.Parse(waredinWatcherConfigs.FirstOrDefault().WardeinConfig); + foreach (var wardeinWatcherConfig in waredinWatcherConfigs) + { + var watcherTypeConfig = JObject.Parse((string)wardeinWatcherConfig.WatcherTypeJsonConfig); + var watcherConfig = JObject.Parse((string)wardeinWatcherConfig.WatcherJsonConfig); + watcherConfig.AddDefaultProps(wardeinWatcherConfig.WatcherType, wardeinWatcherConfig.WatcherConfigurationId, wardeinWatcherConfig.ApplicationId); + watcherTypeConfig.Merge(watcherConfig); + wardeinConfig.Merge(watcherTypeConfig); + } // TODO: Cahe the config - var rawJson = "{\"timeSpanFromSeconds\":60,\"serviceManagerType\":1,\"services\":[{\"serviceName\":\"MSMQ\",\"maxRetryCount\":2,\"serviceManagerType\":0}],\"iisPools\":[{\"serviceName\":\"Elfo.Firmenich.CoreDbDocGen\",\"maxRetryCount\":2,\"serviceManagerType\":1}],\"urls\":[{\"url\":\"www.google.com\",\"urlAlias\":\"Google\",\"assertWithStatusCode\":true,\"assertWithRegex\":\"\\d*\"}]}"; - var json = JObject.Parse(rawJson); - var tests = JsonConvert.DeserializeObject(json.ToString()); - return json.ToObject(); + return wardeinConfig.ToObject(); } public void InvalidateCache() @@ -74,8 +69,58 @@ public class WardeinConfigurationModel public int WatcherConfigurationId { get; set; } public int ApplicationId { get; set; } public string ApplicationHostname { get; set; } + public WardeinWatcherType WatcherType { get; set; } public string WatcherJsonConfig { get; set; } public string WatcherTypeJsonConfig { get; set; } public string WardeinConfig { get; set; } } + + public enum WardeinWatcherType + { + Unknown, + WindowsService, + CleanUp, + IISPool, + Web, + WardeinHeartbeat, + HealthAPI + } + + internal static class JObjectExtensions + { + internal static void AddDefaultProps(this JObject config, WardeinWatcherType watcherType, int watcherConfigurationId, int applicationId) + { + JToken tokens; + switch (watcherType) + { + case WardeinWatcherType.WindowsService: + if (config.TryGetValue("services", out tokens)) + foreach (var token in tokens.AsJEnumerable()) + token.AddDefaultProps(watcherConfigurationId, applicationId); + break; + case WardeinWatcherType.IISPool: + if (config.TryGetValue("iisPools", out tokens)) + foreach (var token in tokens.AsJEnumerable()) + token.AddDefaultProps(watcherConfigurationId, applicationId); + break; + case WardeinWatcherType.Web: + if (config.TryGetValue("urls", out tokens)) + foreach (var token in tokens.AsJEnumerable()) + token.AddDefaultProps(watcherConfigurationId, applicationId); + break; + case WardeinWatcherType.WardeinHeartbeat: + if (config.TryGetValue("heartbeat", out tokens)) + tokens.AddDefaultProps(watcherConfigurationId, applicationId); + break; + default: + return; + } + } + + internal static void AddDefaultProps(this JToken token, int watcherConfigurationId, int applicationId) + { + token["applicationId"] = applicationId; + token["watcherConfigurationId"] = watcherConfigurationId; + } + } } diff --git a/Elfo.Wardein.Core/ConfigurationManagers/WardeinConfigurationManagerFromJSON.cs b/Elfo.Wardein.Core/ConfigurationManagers/WardeinConfigurationManagerFromJSON.cs index 9c220f0..d0c09f0 100644 --- a/Elfo.Wardein.Core/ConfigurationManagers/WardeinConfigurationManagerFromJSON.cs +++ b/Elfo.Wardein.Core/ConfigurationManagers/WardeinConfigurationManagerFromJSON.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Text; -namespace Elfo.Wardein.Core.ConfigurationReader +namespace Elfo.Wardein.Core.ConfigurationManagers { public class WardeinConfigurationManagerFromJSON : IAmWardeinConfigurationManager { diff --git a/Elfo.Wardein.Core/DependencyInjection/ServicesContainer.cs b/Elfo.Wardein.Core/DependencyInjection/ServicesContainer.cs index 8094c7d..d4e0c75 100644 --- a/Elfo.Wardein.Core/DependencyInjection/ServicesContainer.cs +++ b/Elfo.Wardein.Core/DependencyInjection/ServicesContainer.cs @@ -1,14 +1,12 @@ -using Elfo.Firmenich.Wardein.Abstractions.Configuration.Models; -using Elfo.Firmenich.Wardein.Abstractions.HeartBeat; -using Elfo.Firmenich.Wardein.Abstractions.Watchers; -using Elfo.Firmenich.Wardein.Abstractions.WebWatcher; -using Elfo.Firmenich.Wardein.Core.HeartBeat; -using Elfo.Firmenich.Wardein.Core.Persistence; -using Elfo.Firmenich.Wardein.Core.ServiceManager; +using Elfo.Wardein.Abstractions; using Elfo.Wardein.Abstractions.Configuration; +using Elfo.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Abstractions.HeartBeat; using Elfo.Wardein.Abstractions.Services; -using Elfo.Wardein.Abstractions.Services.Models; -using Elfo.Wardein.Core.ConfigurationReader; +using Elfo.Wardein.Abstractions.Watchers; +using Elfo.Wardein.Abstractions.WebWatcher; +using Elfo.Wardein.Core.ConfigurationManagers; +using Elfo.Wardein.Core.HeartBeat; using Elfo.Wardein.Core.Helpers; using Elfo.Wardein.Core.NotificationService; using Elfo.Wardein.Core.Persistence; @@ -27,14 +25,16 @@ public class ServicesContainer private IServiceCollection serviceCollection; private static volatile ServicesContainer serviceContainer; private ServiceProvider serviceProvider; + private readonly WardeinBaseConfiguration wardeinBaseConfiguration; #endregion #region Constructor - protected ServicesContainer() + protected ServicesContainer(WardeinBaseConfiguration wardeinBaseConfiguration) { Configure(); + this.wardeinBaseConfiguration = wardeinBaseConfiguration; } #endregion @@ -44,10 +44,56 @@ protected ServicesContainer() protected void Configure() { - serviceCollection = serviceCollection = new ServiceCollection() - .AddSingleton>(sp => filePath => new MailConfigurationManagerFromJSON(filePath)) - .AddSingleton>(sp => filePath => new WardeinConfigurationManagerFromJSON(filePath)) - .AddTransient>>(sp => filePath => new WindowsServiceStatsPersistenceInJSON(filePath)) + serviceCollection = new ServiceCollection() + .AddSingleton(sp => + { + switch (wardeinBaseConfiguration.MailConnectionType) + { + case ConnectionType.FileSystem: + return new MailConfigurationManagerFromJSON(wardeinBaseConfiguration.MailConnectionString); + default: + throw new NotImplementedException(); + + } + }) + .AddSingleton(sp => + { + switch (wardeinBaseConfiguration.StorageConnectionType) + { + case ConnectionType.FileSystem: + return new WardeinConfigurationManagerFromJSON(wardeinBaseConfiguration.StorageConnectionString); + case ConnectionType.Oracle: + return new OracleWardeinConfigurationManager(sp.GetService(), HostHelper.GetName()); + default: + throw new NotImplementedException(); + + } + }) + .AddSingleton(sp => + { + var configs = wardeinBaseConfiguration.OracleAdditionalParams; + var builder = new OracleConnectionConfiguration.Builder(wardeinBaseConfiguration.StorageConnectionString); + if (configs != null) + builder + .WithClientId(configs.ClientId) + .WithClientInfo(configs.ClientInfo) + .WithModuleName(configs.ModuleName) + .WithDateLanguage(configs.DateLanguage); + return builder.Build(); + }) + .AddSingleton(new HttpClientUrlResponseManager()) + .AddTransient(sp => new OracleHelper(sp.GetService())) + .AddTransient(sp => + { + switch (wardeinBaseConfiguration.StorageConnectionType) + { + case ConnectionType.Oracle: + return new OracleWatcherPersistenceService(sp.GetService()); + default: + throw new NotImplementedException(); + + } + }) .AddTransient>(sp => notificationType => { switch (notificationType) @@ -72,16 +118,20 @@ protected void Configure() throw new KeyNotFoundException($"Notification service {serviceManagerType.ToString()} not supported yet"); } }) - .AddTransient>(sp => (connectionString) => new OracleConnectionConfiguration(connectionString)) - .AddTransient>(sp => (connectionString) - => new OracleWardeinHeartBeatPersistanceService(new OracleConnectionConfiguration.Builder(connectionString).Build()) // TODO add clientinfo etc - ).AddTransient>(sp => (connectionString) - => new OracleWatcherPersistenceService(new OracleConnectionConfiguration.Builder(connectionString).Build()) // TODO add clientinfo etc - ) - .AddTransient>(); - - - serviceProvider = serviceCollection.BuildServiceProvider(); + .AddTransient(sp => + { + switch (wardeinBaseConfiguration.StorageConnectionType) + { + case ConnectionType.Oracle: + return new OracleWardeinHeartBeatPersistanceService(sp.GetService()); + default: + throw new NotImplementedException(); + + } + }); + + + serviceProvider = serviceCollection.BuildServiceProvider(); } #endregion @@ -100,46 +150,36 @@ public static ServicesContainer Current lock (sync) { if (serviceContainer == null) - serviceContainer = new ServicesContainer(); + throw new InvalidOperationException("ServicesContainer must be initialized first"); } } return serviceContainer; } } - #endregion - - #region Objects - - public static IAmPersistenceService PersistenceService(string filePath) - { - var instanceResolver = Current.serviceProvider.GetService>>(); - return instanceResolver(filePath); - } - - public static IAmWatcherPersistenceService WatcherPersistenceService(string connectionString) + public static void Initialize(WardeinBaseConfiguration wardeinBaseConfiguration) { - var instanceResolver = Current.serviceProvider.GetService>(); - return instanceResolver(connectionString); + if (serviceContainer == null) + { + lock (sync) + { + if (serviceContainer == null) + serviceContainer = new ServicesContainer(wardeinBaseConfiguration); + } + } } - public static IAmWardeinHeartBeatPersistanceService WardeinHeartBeatPersistenceService(string connectionString) - { - var instanceResolver = Current.serviceProvider.GetService>(); - return instanceResolver(connectionString); - } + #endregion - public static IAmMailConfigurationManager MailConfigurationManager(string filePath) - { - var instanceResolver = Current.serviceProvider.GetService>(); - return instanceResolver(filePath); - } + #region Objects - public static IAmWardeinConfigurationManager WardeinConfigurationManager(string filePath) - { - var instanceResolver = Current.serviceProvider.GetService>(); - return instanceResolver(filePath); - } + public static IAmWatcherPersistenceService WatcherPersistenceService() => Current.serviceProvider.GetService(); + public static IAmWardeinHeartBeatPersistanceService WardeinHeartBeatPersistenceService() => Current.serviceProvider.GetService(); + public static IAmMailConfigurationManager MailConfigurationManager() => Current.serviceProvider.GetService(); + public static IAmWardeinConfigurationManager WardeinConfigurationManager() => Current.serviceProvider.GetService(); + public static OracleConnectionConfiguration OracleConnectionConfiguration() => Current.serviceProvider.GetService(); + public static IAmUrlResponseManager UrlResponseManager() => Current.serviceProvider.GetService(); + public static IOracleHelper OracleHelper() => Current.serviceProvider.GetService(); public static IAmNotificationService NotificationService(NotificationType notificationType) { @@ -151,7 +191,7 @@ public static IAmServiceManager ServiceManager(string serviceName, ServiceManage { var instanceResolver = Current.serviceProvider.GetService>(); return instanceResolver(serviceManagerType, serviceName); - } + } #endregion } diff --git a/Elfo.Wardein.Core/DependencyInjection/WardeinBaseConfiguration.cs b/Elfo.Wardein.Core/DependencyInjection/WardeinBaseConfiguration.cs new file mode 100644 index 0000000..f00b69d --- /dev/null +++ b/Elfo.Wardein.Core/DependencyInjection/WardeinBaseConfiguration.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Elfo.Wardein.Core +{ + public class WardeinBaseConfiguration + { + public ConnectionType StorageConnectionType { get; set; } + public string StorageConnectionString { get; set; } + public OracleSessionParameters OracleAdditionalParams { get; set; } + + public ConnectionType MailConnectionType { get; set; } + public string MailConnectionString { get; set; } + + + } + + public enum ConnectionType + { + FileSystem, + Oracle + } + + public class OracleSessionParameters + { + public string ClientId { get; set; } + public string ClientInfo { get; set; } + public string ModuleName { get; set; } + public string DateLanguage { get; set; } + } +} diff --git a/Elfo.Wardein.Core/Elfo.Wardein.Core.csproj b/Elfo.Wardein.Core/Elfo.Wardein.Core.csproj index 87f4c04..7fd153a 100644 --- a/Elfo.Wardein.Core/Elfo.Wardein.Core.csproj +++ b/Elfo.Wardein.Core/Elfo.Wardein.Core.csproj @@ -3,8 +3,8 @@ netstandard2.1 latest - Elfo.Firmenich.Wardein.Core - Elfo.Firmenich.Wardein.Core + Elfo.Wardein.Core + Elfo.Wardein.Core 1.1.0 diff --git a/Elfo.Wardein.Core/HeartBeat/OracleWardeinHeartBeatPersistanceService.cs b/Elfo.Wardein.Core/HeartBeat/OracleWardeinHeartBeatPersistanceService.cs index 72cc309..9f4f883 100644 --- a/Elfo.Wardein.Core/HeartBeat/OracleWardeinHeartBeatPersistanceService.cs +++ b/Elfo.Wardein.Core/HeartBeat/OracleWardeinHeartBeatPersistanceService.cs @@ -1,4 +1,4 @@ -using Elfo.Firmenich.Wardein.Abstractions.HeartBeat; +using Elfo.Wardein.Abstractions.HeartBeat; using Elfo.Wardein.Core.Helpers; using Oracle.ManagedDataAccess.Client; using System; @@ -7,7 +7,7 @@ using System.Text; using System.Threading.Tasks; -namespace Elfo.Firmenich.Wardein.Core.HeartBeat +namespace Elfo.Wardein.Core.HeartBeat { public class OracleWardeinHeartBeatPersistanceService : IAmWardeinHeartBeatPersistanceService { diff --git a/Elfo.Wardein.Core/Helpers/ExternalResources/DB/Oracle/Abstractions/IOracleHelper.cs b/Elfo.Wardein.Core/Helpers/ExternalResources/DB/Oracle/Abstractions/IOracleHelper.cs new file mode 100644 index 0000000..a0b0029 --- /dev/null +++ b/Elfo.Wardein.Core/Helpers/ExternalResources/DB/Oracle/Abstractions/IOracleHelper.cs @@ -0,0 +1,16 @@ +using Oracle.ManagedDataAccess.Client; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Elfo.Wardein.Core.Helpers +{ + public interface IOracleHelper + { + Task CallProcedureAsync(string packageName, string procedureName, Func howToGetResult, params OracleParameter[] parameters); + int Execute(string command, IDictionary parameters = null); + Task ExecuteAsync(string command, IDictionary parameters = null); + IEnumerable Query(string query, IDictionary parameters = null); + Task> QueryAsync(string query, IDictionary parameters = null); + } +} \ No newline at end of file diff --git a/Elfo.Wardein.Core/Helpers/ExternalResources/DB/Oracle/OracleHelper.cs b/Elfo.Wardein.Core/Helpers/ExternalResources/DB/Oracle/OracleHelper.cs index 628d68d..fec9cd6 100644 --- a/Elfo.Wardein.Core/Helpers/ExternalResources/DB/Oracle/OracleHelper.cs +++ b/Elfo.Wardein.Core/Helpers/ExternalResources/DB/Oracle/OracleHelper.cs @@ -1,4 +1,4 @@ -using Elfo.Firmenich.Wardein.Abstractions.Watchers; +using Elfo.Wardein.Abstractions.Watchers; using Oracle.ManagedDataAccess.Client; using System; using System.Collections.Generic; @@ -9,7 +9,7 @@ namespace Elfo.Wardein.Core.Helpers { - public class OracleHelper + public class OracleHelper : IOracleHelper { private readonly OracleConnectionConfiguration oracleConfiguration; @@ -46,7 +46,7 @@ public IEnumerable Query(string query, IDictionary paramet } } - public Task CallProcedureAsync(string packageName, string procedureName, Func howToGetResult, params OracleParameter[] parameters) + public async Task CallProcedureAsync(string packageName, string procedureName, Func howToGetResult, params OracleParameter[] parameters) { try { @@ -74,18 +74,18 @@ public Task CallProcedureAsync(string packageName, string procedureName, F command.ExecuteNonQuery(); + await transaction.CommitAsync(); + if (parameters.Count() > 0) { - return Task.FromResult( - howToGetResult( - parameters.Where(x => - x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput - ).ToArray() - ) + return howToGetResult( + parameters.Where(x => + x.Direction == ParameterDirection.Output || x.Direction == ParameterDirection.InputOutput + ).ToArray() ); } - else - return Task.FromResult(default(T)); + else + return default(T); } } } diff --git a/Elfo.Wardein.Core/NotificationService/MailNotificationService.cs b/Elfo.Wardein.Core/NotificationService/MailNotificationService.cs index 8deb68c..3fc6b6a 100644 --- a/Elfo.Wardein.Core/NotificationService/MailNotificationService.cs +++ b/Elfo.Wardein.Core/NotificationService/MailNotificationService.cs @@ -27,7 +27,7 @@ public async Task SendNotificationAsync(string recipientAddress, string notifica MailConfiguration GetMailConfiguration() { - var config = ServicesContainer.MailConfigurationManager(Const.MAIL_CONFIG_PATH)?.GetConfiguration(); + var config = ServicesContainer.MailConfigurationManager()?.GetConfiguration(); if (config == null) throw new ArgumentNullException("Cannot find Mail SMTP Configuration"); diff --git a/Elfo.Wardein.Core/Persistence/OracleWatcherPersistenceService.cs b/Elfo.Wardein.Core/Persistence/OracleWatcherPersistenceService.cs index 12949e1..0aefa33 100644 --- a/Elfo.Wardein.Core/Persistence/OracleWatcherPersistenceService.cs +++ b/Elfo.Wardein.Core/Persistence/OracleWatcherPersistenceService.cs @@ -1,4 +1,4 @@ -using Elfo.Firmenich.Wardein.Abstractions.Watchers; +using Elfo.Wardein.Abstractions.Watchers; using Elfo.Wardein.Core.Helpers; using Oracle.ManagedDataAccess.Client; using System; @@ -7,7 +7,7 @@ using System.Text; using System.Threading.Tasks; -namespace Elfo.Firmenich.Wardein.Core.Persistence +namespace Elfo.Wardein.Core.Persistence { public class OracleWatcherPersistenceService : IAmWatcherPersistenceService { @@ -41,7 +41,7 @@ public async Task UpsertCurrentStatus(int watcherConfigurat new OracleParameter("p_is_healthy", OracleDbType.Varchar2, isHealthy ? "Y" : "N" , System.Data.ParameterDirection.Input), new OracleParameter("p_flr_msg", OracleDbType.Varchar2, failureException != null ? failureException.ToString() : null, System.Data.ParameterDirection.Input), new OracleParameter("po_flr_count", OracleDbType.Int32, System.Data.ParameterDirection.Output), - new OracleParameter("po_prv_status", OracleDbType.Varchar2, System.Data.ParameterDirection.Output) + new OracleParameter("po_prv_status", OracleDbType.Varchar2, 3, "N", System.Data.ParameterDirection.Output) ); } } diff --git a/Elfo.Wardein.Core/Persistence/WindowsServiceStatsPersistenceInJSON.cs b/Elfo.Wardein.Core/Persistence/WindowsServiceStatsPersistenceInJSON.cs index 6f03eeb..fbafd51 100644 --- a/Elfo.Wardein.Core/Persistence/WindowsServiceStatsPersistenceInJSON.cs +++ b/Elfo.Wardein.Core/Persistence/WindowsServiceStatsPersistenceInJSON.cs @@ -9,6 +9,7 @@ namespace Elfo.Wardein.Core.Persistence { + [Obsolete] public class WindowsServiceStatsPersistenceInJSON : IAmPersistenceService { private readonly string filePath; diff --git a/Elfo.Wardein.Core/ServiceManager/GenericServiceManager.cs b/Elfo.Wardein.Core/ServiceManager/GenericServiceManager.cs index d1ea8ab..7b6a28d 100644 --- a/Elfo.Wardein.Core/ServiceManager/GenericServiceManager.cs +++ b/Elfo.Wardein.Core/ServiceManager/GenericServiceManager.cs @@ -6,6 +6,7 @@ using System.Linq; using System.ServiceProcess; using System.Text; +using System.Threading.Tasks; namespace Elfo.Wardein.Core.ServiceManager { @@ -25,9 +26,9 @@ public GenericServiceManager(string serviceName) : base(serviceName) } #endregion - public override bool IsStillAlive => this.serviceController?.Status == ServiceControllerStatus.Running; + public override async Task IsStillAlive() => await Task.FromResult(this.serviceController?.Status == ServiceControllerStatus.Running); - public override void ForceKill() + public override async Task ForceKill() { try { @@ -56,11 +57,11 @@ public override void ForceKill() } } - public override void Start() + public override async Task Start() { try { - if (!IsStillAlive) + if (!await IsStillAlive()) this.serviceController.Start(); } catch (Exception ex) @@ -70,12 +71,12 @@ public override void Start() } } - public override void Stop() + public override async Task Stop() { try { - if (IsStillAlive) - ForceKill(); + if (await IsStillAlive()) + await ForceKill(); } catch (Exception ex) { @@ -84,14 +85,14 @@ public override void Stop() } } - public override void Restart() + public override async Task Restart() { try { - Stop(); + await Stop(); this.serviceController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30)); // TODO: get this value from config log.Info($"Service {base.serviceName} stopped @ {DateTime.Now}."); - Start(); + await Start(); this.serviceController.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(30)); // TODO: get this value from config log.Info($"Service {base.serviceName} started @ {DateTime.Now}."); } @@ -102,11 +103,11 @@ public override void Restart() } } - public override string GetStatus() + public override async Task GetStatus() { try { - return this.serviceController.Status.ToString(); + return await Task.FromResult(this.serviceController.Status.ToString()); } catch (Exception ex) { diff --git a/Elfo.Wardein.Core/ServiceManager/HttpClientUrlResponseManager.cs b/Elfo.Wardein.Core/ServiceManager/HttpClientUrlResponseManager.cs index 173a068..3ad69bf 100644 --- a/Elfo.Wardein.Core/ServiceManager/HttpClientUrlResponseManager.cs +++ b/Elfo.Wardein.Core/ServiceManager/HttpClientUrlResponseManager.cs @@ -1,4 +1,4 @@ -using Elfo.Firmenich.Wardein.Abstractions.WebWatcher; +using Elfo.Wardein.Abstractions.WebWatcher; using Elfo.Wardein.Core.ServiceManager; using System; using System.Net; @@ -7,7 +7,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; -namespace Elfo.Firmenich.Wardein.Core.ServiceManager +namespace Elfo.Wardein.Core.ServiceManager { public class HttpClientUrlResponseManager : IAmUrlResponseManager { diff --git a/Elfo.Wardein.Core/ServiceManager/IISPoolManager.cs b/Elfo.Wardein.Core/ServiceManager/IISPoolManager.cs index 9fa7457..2133e65 100644 --- a/Elfo.Wardein.Core/ServiceManager/IISPoolManager.cs +++ b/Elfo.Wardein.Core/ServiceManager/IISPoolManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading.Tasks; namespace Elfo.Wardein.Core.ServiceManager { @@ -24,9 +25,9 @@ public IISPoolManager(string appPoolName) : base(appPoolName) throw new ArgumentNullException($"ApplicationPool {appPoolName} doesn't exist"); } - public override bool IsStillAlive => this.applicationPool.State == ObjectState.Started; + public override async Task IsStillAlive() => await Task.FromResult(this.applicationPool.State == ObjectState.Started); - public override void ForceKill() + public override async Task ForceKill() { try { @@ -42,12 +43,12 @@ public override void ForceKill() } } - public override void Restart() + public override async Task Restart() { try { - Stop(); - Start(); + await Stop(); + await Start(); } catch (Exception ex) { @@ -56,12 +57,12 @@ public override void Restart() } } - public override void Start() + public override async Task Start() { try { log.Info($"Starting pool {base.serviceName} @ {DateTime.UtcNow}"); - if (!IsStillAlive) + if (!await IsStillAlive()) this.applicationPool.Start(); this.applicationPool.WaitForStatus(ObjectState.Started, TimeSpan.FromSeconds(30)); log.Info($"{base.serviceName} pool started @ {DateTime.UtcNow}"); @@ -73,12 +74,12 @@ public override void Start() } } - public override void Stop() + public override async Task Stop() { try { - if (IsStillAlive) - this.ForceKill(); + if (await IsStillAlive()) + await this.ForceKill(); } catch (Exception ex) { @@ -87,11 +88,11 @@ public override void Stop() } } - public override string GetStatus() + public override async Task GetStatus() { try { - return this.applicationPool.State.ToString(); + return await Task.FromResult(this.applicationPool.State.ToString()); } catch (Exception ex) { diff --git a/Elfo.Wardein.Core/ServiceManager/WindowsServiceManager.cs b/Elfo.Wardein.Core/ServiceManager/WindowsServiceManager.cs index c4aa8f7..e2cd9b1 100644 --- a/Elfo.Wardein.Core/ServiceManager/WindowsServiceManager.cs +++ b/Elfo.Wardein.Core/ServiceManager/WindowsServiceManager.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using System.ServiceProcess; +using System.Threading.Tasks; namespace Elfo.Wardein.Core.ServiceManager { @@ -23,9 +24,9 @@ public WindowsServiceManager(string serviceName) : base(serviceName) } #endregion - public override bool IsStillAlive => this.serviceController?.Status == ServiceControllerStatus.Running; + public override async Task IsStillAlive() => await Task.FromResult(this.serviceController?.Status == ServiceControllerStatus.Running); - public override void ForceKill() + public override async Task ForceKill() { try { @@ -54,11 +55,11 @@ public override void ForceKill() } } - public override void Start() + public override async Task Start() { try { - if (!IsStillAlive) + if (!await IsStillAlive()) this.serviceController.Start(); } catch (Exception ex) @@ -68,12 +69,12 @@ public override void Start() } } - public override void Stop() + public override async Task Stop() { try { - if (IsStillAlive) - ForceKill(); + if (await IsStillAlive()) + await ForceKill(); } catch (Exception ex) { @@ -82,14 +83,14 @@ public override void Stop() } } - public override void Restart() + public override async Task Restart() { try { - Stop(); + await Stop(); this.serviceController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30)); // TODO: get this value from config log.Info($"Service {base.serviceName} stopped @ {DateTime.Now}."); - Start(); + await Start(); this.serviceController.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(30)); // TODO: get this value from config log.Info($"Service {base.serviceName} started @ {DateTime.Now}."); } @@ -100,11 +101,11 @@ public override void Restart() } } - public override string GetStatus() + public override async Task GetStatus() { try { - return this.serviceController.Status.ToString(); + return await Task.FromResult(this.serviceController.Status.ToString()); } catch (Exception ex) { diff --git a/Elfo.Wardein.Integrations/Elfo.Wardein.Integrations.csproj b/Elfo.Wardein.Integrations/Elfo.Wardein.Integrations.csproj index 49880f0..8cea463 100644 --- a/Elfo.Wardein.Integrations/Elfo.Wardein.Integrations.csproj +++ b/Elfo.Wardein.Integrations/Elfo.Wardein.Integrations.csproj @@ -3,8 +3,8 @@ netstandard2.1 latest - Elfo.Firmenich.Wardein.Integrations - Elfo.Firmenich.Wardein.Integrations + Elfo.Wardein.Integrations + Elfo.Wardein.Integrations false false diff --git a/Elfo.Wardein.Services/Elfo.Wardein.Services.csproj b/Elfo.Wardein.Services/Elfo.Wardein.Services.csproj index fb440cf..230e612 100644 --- a/Elfo.Wardein.Services/Elfo.Wardein.Services.csproj +++ b/Elfo.Wardein.Services/Elfo.Wardein.Services.csproj @@ -4,6 +4,16 @@ netcoreapp3.1 + + + + + + + PreserveNewest + + + diff --git a/Elfo.Wardein.Services/ServiceBuilder.cs b/Elfo.Wardein.Services/ServiceBuilder.cs index f871771..14026c9 100644 --- a/Elfo.Wardein.Services/ServiceBuilder.cs +++ b/Elfo.Wardein.Services/ServiceBuilder.cs @@ -13,6 +13,12 @@ using Elfo.Wardein.Watchers.WindowsService; using Elfo.Wardein.Watchers.IISPool; using Elfo.Wardein.Watchers.HeartBeat; +using Elfo.Wardein.Core.ConfigurationManagers; +using Elfo.Wardein.Core.Helpers; +using System.Linq; +using Elfo.Wardein.Watchers.GenericService; +using Elfo.Wardein.Core; +using Elfo.Wardein.Watchers.WebWatcher; namespace Elfo.Wardein.Services { @@ -22,22 +28,32 @@ public class ServiceBuilder public async Task ConfigureAndRunWarden() { - var appConfiguration = new ConfigurationBuilder() - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .Build(); + var wardeinConfigurationManager = ServicesContainer.WardeinConfigurationManager(); + var wardeinConfiguration = wardeinConfigurationManager.GetConfiguration(); - var connectionString = appConfiguration["ConnectionStrings:Db"]; - - var configuration = WardenConfiguration + var configurationBuilder = WardenConfiguration .Create() - .IntegrateWithOracle(connectionString) - .AddFileSystemWatcher(null) - .AddWindowsServiceWatcher(null) - .AddIISPoolWatcher(null) - .AddWardeinHeartBeatWatcher(null) - .Build(); - - warden = WardenInstance.Create(configuration); + .AddWardeinHeartBeatWatcher(wardeinConfiguration.Heartbeat, "HeartbeatWatcher"); + + // TODO: refactor and add oracle integration (even if useles at the moment) + + if (wardeinConfiguration.Urls?.Count() > 0) + foreach (var url in wardeinConfiguration.Urls) + configurationBuilder.AddWebWatcher(url, "WebWatcher"); + + if (wardeinConfiguration.Services?.Count() > 0) + foreach (var service in wardeinConfiguration.Services) + configurationBuilder.AddWindowsServiceWatcher(service, "ServiceWatcher"); + + if (wardeinConfiguration.IISPools?.Count() > 0) + foreach (var pool in wardeinConfiguration.IISPools) + configurationBuilder.AddIISPoolWatcher(pool, "WebWatcher"); + + if (wardeinConfiguration.CleanUps?.Count() > 0) + foreach (var cleanUp in wardeinConfiguration.CleanUps) + configurationBuilder.AddFileSystemWatcher(cleanUp, "CleanUpWatcher"); + + warden = WardenInstance.Create(configurationBuilder.Build()); await warden.StartAsync(); } diff --git a/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemCleanUpConfig.cs b/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemCleanUpConfig.cs deleted file mode 100644 index 41c419f..0000000 --- a/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemCleanUpConfig.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Elfo.Wardein.Watchers.FileSystem -{ - public class FileSystemCleanUpConfig - { - [JsonProperty(PropertyName = "filePath")] - public string FilePath { get; set; } - - [JsonProperty(PropertyName = "timeSpanFromSeconds")] - public double TimeSpanFromSeconds { get; set; } = 10; // Default values - - [JsonProperty(PropertyName = "cleanUpOptions")] - public FileSystemCleanUpOptions CleanUpOptions { get; set; } = new FileSystemCleanUpOptions(); - } -} diff --git a/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemCleanUpOptions.cs b/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemCleanUpOptions.cs deleted file mode 100644 index a3ae2a6..0000000 --- a/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemCleanUpOptions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Elfo.Wardein.Watchers.FileSystem -{ - public class FileSystemCleanUpOptions - { - [JsonProperty(PropertyName = "thresholdInSeconds")] - public int ThresholdInSeconds { get; set; } - - [JsonProperty(PropertyName = "thresholdInDays")] - public int ThresholdInDays { get; set; } - - [JsonProperty(PropertyName = "displayOnly")] - public bool DisplayOnly { get; set; } = false; - - [JsonProperty(PropertyName = "useRecycleBin")] - public bool UseRecycleBin { get; set; } = false; - - [JsonProperty(PropertyName = "removeEmptyFolders")] - public bool RemoveEmptyFolders { get; set; } = false; - - [JsonProperty(PropertyName = "recursive")] - public bool Recursive { get; set; } = false; - } -} diff --git a/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemWatcherConfig.cs b/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemWatcherConfig.cs deleted file mode 100644 index cf318a6..0000000 --- a/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemWatcherConfig.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Elfo.Wardein.Watchers.FileSystem -{ - public class FileSystemWatcherConfig : IWatcherConfig - { - /// - /// Property defines if watcher has to be running in maintainance mode - /// Default value false - /// - [JsonProperty(PropertyName = "isInMaintenanceMode")] - public bool IsInMaintenanceMode { get; set; } = false; - - /// - /// Property that defines frequency of watcher polling - /// Default value 10 seconds - /// - [JsonProperty(PropertyName = "timeSpanFromSeconds")] - public double TimeSpanFromSeconds { get; set; } = 10; - - /// - /// List of folder configurations that has to be monitored and cleaned by criteria - /// - [JsonProperty(PropertyName = "cleanUps")] - public IEnumerable CleanUps { get; set; } = new List(); - } -} diff --git a/Elfo.Wardein.Watchers/FileSystem/Extensions.cs b/Elfo.Wardein.Watchers/FileSystem/Extensions.cs index 88b603b..a886f52 100644 --- a/Elfo.Wardein.Watchers/FileSystem/Extensions.cs +++ b/Elfo.Wardein.Watchers/FileSystem/Extensions.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Warden; +using Elfo.Wardein.Abstractions.Configuration.Models; +using System; using Warden.Core; using Warden.Watchers; @@ -10,7 +8,7 @@ namespace Elfo.Wardein.Watchers.FileSystem public static class Extensions { public static WardenConfiguration.Builder AddFileSystemWatcher(this WardenConfiguration.Builder builder, - FileSystemWatcherConfig config, + FileSystemConfigurationModel config, string group = null, Action hooks = null) { diff --git a/Elfo.Wardein.Watchers/FileSystem/FileSystemWatcher.cs b/Elfo.Wardein.Watchers/FileSystem/FileSystemWatcher.cs index c308044..9e3d0a0 100644 --- a/Elfo.Wardein.Watchers/FileSystem/FileSystemWatcher.cs +++ b/Elfo.Wardein.Watchers/FileSystem/FileSystemWatcher.cs @@ -1,4 +1,5 @@ using Elfo.CleanUpManager; +using Elfo.Wardein.Abstractions.Configuration.Models; using Elfo.Wardein.Core.Helpers; using System; using System.Text; @@ -7,12 +8,12 @@ namespace Elfo.Wardein.Watchers.FileSystem { - public class FileSystemWatcher : WardeinWatcher + public class FileSystemWatcher : WardeinWatcher { - protected FileSystemWatcher(FileSystemWatcherConfig config, string group = null) : base(nameof(FileSystemWatcher), config, group) + protected FileSystemWatcher(FileSystemConfigurationModel config, string group = null) : base(nameof(FileSystemWatcher), config, group) { } - public static FileSystemWatcher Create(FileSystemWatcherConfig config, string group = null) + public static FileSystemWatcher Create(FileSystemConfigurationModel config, string group = null) { return new FileSystemWatcher(config, group); } diff --git a/Elfo.Wardein.Watchers/FileSystem/Config/FileSystemWatcherCheckResult.cs b/Elfo.Wardein.Watchers/FileSystem/FileSystemWatcherCheckResult.cs similarity index 100% rename from Elfo.Wardein.Watchers/FileSystem/Config/FileSystemWatcherCheckResult.cs rename to Elfo.Wardein.Watchers/FileSystem/FileSystemWatcherCheckResult.cs diff --git a/Elfo.Wardein.Watchers/GenericService/Config/GenericServiceWatcherConfig.cs b/Elfo.Wardein.Watchers/GenericService/Config/GenericServiceWatcherConfig.cs deleted file mode 100644 index 84e8700..0000000 --- a/Elfo.Wardein.Watchers/GenericService/Config/GenericServiceWatcherConfig.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - - -namespace Elfo.Wardein.Watchers.GenericService -{ - public class GenericServiceWatcherConfig : IWatcherConfig - { - /// - /// Property that defines frequency of watcher polling - /// Default value 10 seconds - /// - [JsonProperty(PropertyName = "timeSpanFromSeconds")] - public double TimeSpanFromSeconds { get; set; } = 10; - - [JsonProperty(PropertyName = "sendRepeatedNotificationAfterSeconds")] - public double SendRepeatedNotificationAfterSeconds { get; set; } = 3600; // Default values - - [JsonProperty(PropertyName = "numberOfNotificationsWithoutRateLimitation")] - public int NumberOfNotificationsWithoutRateLimitation { get; set; } = 2; // Default values - - [JsonProperty(PropertyName = "services")] - public IEnumerable Services { get; set; } - - [JsonProperty(PropertyName = "isInMaintenanceMode")] - public bool IsInMaintenanceMode { get; set; } = false; - } -} diff --git a/Elfo.Wardein.Watchers/GenericService/Config/ObservarableGenericServiceWatcher.cs b/Elfo.Wardein.Watchers/GenericService/Config/ObservarableGenericServiceWatcher.cs deleted file mode 100644 index 0dccdac..0000000 --- a/Elfo.Wardein.Watchers/GenericService/Config/ObservarableGenericServiceWatcher.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Elfo.Firmenich.Wardein.Abstractions.Configuration.Models; -using Newtonsoft.Json; - - -namespace Elfo.Wardein.Watchers.GenericService -{ - public class ObservarableGenericServiceWatcher - { - [JsonProperty(PropertyName = "serviceName")] - public string ServiceName { get; set; } - - [JsonProperty(PropertyName = "maxRetryCount")] - public int MaxRetryCount { get; set; } - - [JsonProperty(PropertyName = "failMessage")] - public string FailMessage { get; set; } - - [JsonProperty(PropertyName = "restoredMessage")] - public string RestoredMessage { get; set; } - - [JsonProperty(PropertyName = "recipientAddress")] - public string RecipientAddress { get; set; } - - [JsonProperty(PropertyName = "notificationType")] - public string NotificationType { get; set; } = "Mail"; - - [JsonProperty(PropertyName = "sendRepeatedNotificationAfterSeconds")] - public double? SendRepeatedNotificationAfterSeconds { get; set; } - - [JsonProperty(PropertyName = "numberOfNotificationsWithoutRateLimitation")] - public int? NumberOfNotificationsWithoutRateLimitation { get; set; } - - [JsonProperty(PropertyName = "serviceManagerType")] - public ServiceManagerType ServiceManagerType { get; set; } - } -} diff --git a/Elfo.Wardein.Watchers/GenericService/GenericServiceWatcher.cs b/Elfo.Wardein.Watchers/GenericService/GenericServiceWatcher.cs index 8fa60c1..a6ad0df 100644 --- a/Elfo.Wardein.Watchers/GenericService/GenericServiceWatcher.cs +++ b/Elfo.Wardein.Watchers/GenericService/GenericServiceWatcher.cs @@ -1,4 +1,6 @@ -using Elfo.Wardein.Abstractions.Services; +using Elfo.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Abstractions.Services; +using Elfo.Wardein.Abstractions.Watchers; using Elfo.Wardein.Core; using Elfo.Wardein.Core.Helpers; using Elfo.Wardein.Core.NotificationService; @@ -9,166 +11,77 @@ namespace Elfo.Wardein.Watchers.GenericService { - public abstract class GenericServiceWatcher : WardeinWatcher + public abstract class GenericServiceWatcher : WardeinWatcherWithResolution { + private readonly IAmServiceManager serviceManager; + private readonly IAmWatcherPersistenceService watcherPersistenceService; - internal GenericServiceWatcher(GenericServiceWatcherConfig config, string name, string group = null) : base(name, config, group) + internal GenericServiceWatcher(GenericServiceConfigurationModel config, string name, string group = null) : base(name, config, group) { + serviceManager = GetServiceManager(); + watcherPersistenceService = ServicesContainer.WatcherPersistenceService(); } + public string ServiceDisplayType => Config.ServiceManagerType.ToString(); + public override async Task ExecuteWatcherActionAsync() { + bool result = false; Log.Info($"---\tStarting {Name}\t---"); try { var guid = Guid.NewGuid(); - log.Info($"{Environment.NewLine}{"-".Repeat(24)} Services health check @ {guid} started {"-".Repeat(24)}"); + log.Info($"{Environment.NewLine}{"-".Repeat(24)} {ServiceDisplayType} health check on {Config.ServiceName} @ {guid} started {"-".Repeat(24)}"); - await RunCheck(); + result = await RunCheck(); - log.Info($"{Environment.NewLine}{"-".Repeat(24)} Services health check @ {guid} finished {"-".Repeat(24)}{Environment.NewLine.Repeat(24)}"); + log.Info($"{Environment.NewLine}{"-".Repeat(24)} {ServiceDisplayType} health check on {Config.ServiceName} @ {guid} finished {"-".Repeat(24)}{Environment.NewLine}"); } catch (Exception ex) { log.Error(ex, $"Exception inside polling action: {ex.ToString()}\n"); } - return await Task.FromResult(null); + return WatcherCheckResult.Create(this, result); } - internal virtual async Task RunCheck() + internal virtual async Task RunCheck() { log.Info($"{Environment.NewLine}> CHECKING SERVICES HEALTH"); - - foreach (var service in Config.Services) + + var notificationService = ServicesContainer.NotificationService(Config.NotificationType); + var isHealthy = await serviceManager.IsStillAlive(); + var currentStatus = await watcherPersistenceService.UpsertCurrentStatus + ( + watcherConfigurationId: Config.WatcherConfigurationId, + applicationId: Config.ApplicationId, + applicationHostname: Config.ApplicationHostname, + isHealthy: isHealthy + ); + + if (!isHealthy) { - using (var persistenceService = ServicesContainer.PersistenceService(Const.DB_PATH)) - { - IAmServiceManager serviceManager = GetServiceManager(); - if (serviceManager == null) - continue; // If the service doesn't exist, skip the check - - var notificationService = ServicesContainer.NotificationService(GetNotificationType()); - var item = persistenceService.GetEntityById(service.ServiceName); - - if (!serviceManager.IsStillAlive) - { - await PerformActionOnServiceDown(); - } - else - { - await PerformActionOnServiceAlive(); - } - - persistenceService.CreateOrUpdateCachedEntity(item); - - #region Local Functions - - IAmServiceManager GetServiceManager() - { - IAmServiceManager svc = null; - try - { - svc = ServicesContainer.ServiceManager(service.ServiceName, service.ServiceManagerType); - } - catch (ArgumentNullException ex) - { - log.Warn(ex.Message); - } - return svc; - } - - NotificationType GetNotificationType() - { - if (!Enum.TryParse(service.NotificationType, out NotificationType result)) - throw new ArgumentException($"Notification type {service.NotificationType} not supported"); - return result; - } - - async Task PerformActionOnServiceDown() - { - serviceManager.Start(); - item.RetryCount++; - - if (IsRetryCountExceededOrEqual() && IsMultipleOfMaxRetryCount()) - { - if (IAmAllowedToSendANewNotification()) - { - log.Warn($"Sending Fail Notification"); - await notificationService.SendNotificationAsync(service.RecipientAddress, service.FailMessage, $"Attention: {service.ServiceName} service is down"); - item.LastNotificationSentAtThisTimeUTC = DateTime.UtcNow; - } - } - log.Info($"{service.ServiceName} is not active, retry: {item.RetryCount}"); - - #region Local Functions - - bool IAmAllowedToSendANewNotification() - { - if (item.RetryCount <= NumberOfNotificationAllowedAtMaximumRate() || NeverSentANotificationBefore()) - return true; - - return IsRepeatedMailTimeoutElapsed(); - - #region Local Functions - - int NumberOfNotificationAllowedAtMaximumRate() => service.MaxRetryCount * GetServiceNumberOfNotificationWithoutRateLimitationOrDefault(); - - bool IsRepeatedMailTimeoutElapsed() - { - var timeout = GetServiceSendRepeatedNotificationAfterSecondsOrDefault(); - - return DateTime.UtcNow.Subtract(item.LastNotificationSentAtThisTimeUTC.GetValueOrDefault(DateTime.MinValue)) >= timeout; - } - - bool NeverSentANotificationBefore() => item.LastNotificationSentAtThisTimeUTC.HasValue == false; - - #endregion - } - - bool IsRetryCountExceededOrEqual() => item.RetryCount >= service.MaxRetryCount; - - bool IsMultipleOfMaxRetryCount() => item.RetryCount % service.MaxRetryCount == 0; - - #endregion - } - - async Task PerformActionOnServiceAlive() - { - try - { - log.Info($"{service.ServiceName} is active"); - if (item.RetryCount > 0) - { - log.Info($"Send Restored Notification"); - await notificationService.SendNotificationAsync(service.RecipientAddress, service.RestoredMessage, $"Good news: {service.ServiceName} service has been restored succesfully"); - } - } - catch (Exception ex) - { - log.Error(ex, "Unable to send email"); - } - finally - { - item.RetryCount = 0; - } - } - - TimeSpan GetServiceSendRepeatedNotificationAfterSecondsOrDefault() => - TimeSpan.FromSeconds(service.SendRepeatedNotificationAfterSeconds.GetValueOrDefault(Config.SendRepeatedNotificationAfterSeconds)); - - int GetServiceNumberOfNotificationWithoutRateLimitationOrDefault() - { - var result = service.NumberOfNotificationsWithoutRateLimitation.GetValueOrDefault(Config.NumberOfNotificationsWithoutRateLimitation); - if (result <= 0) - return int.MaxValue; - - return result; - } + await PerformActionOnServiceDown(currentStatus, async (config) => await serviceManager.Restart()); + } + else + { + await PerformActionOnServiceAlive(currentStatus); + } + return await Task.FromResult(false); + } - #endregion - } + protected virtual IAmServiceManager GetServiceManager() + { + IAmServiceManager svc = null; + try + { + svc = ServicesContainer.ServiceManager(Config.ServiceName, Config.ServiceManagerType); + } + catch (ArgumentNullException ex) + { + log.Warn(ex.Message); } + return svc; } } } diff --git a/Elfo.Wardein.Watchers/GenericService/Config/GenericServiceWatcherCheckResult.cs b/Elfo.Wardein.Watchers/GenericService/GenericServiceWatcherCheckResult.cs similarity index 100% rename from Elfo.Wardein.Watchers/GenericService/Config/GenericServiceWatcherCheckResult.cs rename to Elfo.Wardein.Watchers/GenericService/GenericServiceWatcherCheckResult.cs diff --git a/Elfo.Wardein.Watchers/HeartBeat/Config/HeartBeatWatcherConfig.cs b/Elfo.Wardein.Watchers/HeartBeat/Config/HeartBeatWatcherConfig.cs deleted file mode 100644 index a3b3d66..0000000 --- a/Elfo.Wardein.Watchers/HeartBeat/Config/HeartBeatWatcherConfig.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Net; - -namespace Elfo.Wardein.Watchers.HeartBeat.Config -{ - public class HeartBeatWatcherConfig : IWatcherConfig - { - /// - /// Property defines if watcher has to be running in maintainance mode - /// Default value false - /// - [JsonProperty(PropertyName = "isInMaintenanceMode")] - public bool IsInMaintenanceMode { get; set; } = false; - - [JsonProperty(PropertyName = "heartBeatAppName")] - public string HeartBeatAppName { get; set; } = Dns.GetHostName()?.ToUpperInvariant(); - - /// - /// Property that defines frequency of watcher polling - /// Default value 10 seconds - /// - [JsonProperty(PropertyName = "timeSpanFromSeconds")] - public double TimeSpanFromSeconds { get; set; } = 10; - - [JsonProperty(PropertyName = "sendRepeatedNotificationAfterSeconds")] - public double SendRepeatedNotificationAfterSeconds { get; set; } = 3600; // Default values - - [JsonProperty(PropertyName = "numberOfNotificationsWithoutRateLimitation")] - public int NumberOfNotificationsWithoutRateLimitation { get; set; } = 2; // Default values - - [JsonProperty(PropertyName = "connectionString")] - public string ConnectionString { get; set; } - - } -} diff --git a/Elfo.Wardein.Watchers/HeartBeat/Config/ObservarableHeartBeat.cs b/Elfo.Wardein.Watchers/HeartBeat/Config/ObservarableHeartBeat.cs deleted file mode 100644 index 26dd995..0000000 --- a/Elfo.Wardein.Watchers/HeartBeat/Config/ObservarableHeartBeat.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json; - - -namespace Elfo.Wardein.Watchers.HeartBeat.Config -{ - public class ObservarableHeartBeat - { - [JsonProperty(PropertyName = "serviceName")] - public string ServiceName { get; set; } - - [JsonProperty(PropertyName = "maxRetryCount")] - public int MaxRetryCount { get; set; } - - [JsonProperty(PropertyName = "failMessage")] - public string FailMessage { get; set; } - - [JsonProperty(PropertyName = "restoredMessage")] - public string RestoredMessage { get; set; } - - [JsonProperty(PropertyName = "recipientAddress")] - public string RecipientAddress { get; set; } - - [JsonProperty(PropertyName = "notificationType")] - public string NotificationType { get; set; } = "Mail"; - - [JsonProperty(PropertyName = "sendRepeatedNotificationAfterSeconds")] - public double? SendRepeatedNotificationAfterSeconds { get; set; } - - [JsonProperty(PropertyName = "numberOfNotificationsWithoutRateLimitation")] - public int? NumberOfNotificationsWithoutRateLimitation { get; set; } - } -} diff --git a/Elfo.Wardein.Watchers/HeartBeat/Extensions.cs b/Elfo.Wardein.Watchers/HeartBeat/Extensions.cs index 47a23c0..317f65e 100644 --- a/Elfo.Wardein.Watchers/HeartBeat/Extensions.cs +++ b/Elfo.Wardein.Watchers/HeartBeat/Extensions.cs @@ -1,5 +1,4 @@ -using Elfo.Wardein.Watchers.GenericService; -using Elfo.Wardein.Watchers.HeartBeat.Config; +using Elfo.Wardein.Abstractions.Configuration.Models; using System; using Warden.Core; using Warden.Watchers; @@ -9,7 +8,7 @@ namespace Elfo.Wardein.Watchers.HeartBeat public static class Extensions { public static WardenConfiguration.Builder AddWardeinHeartBeatWatcher(this WardenConfiguration.Builder builder, - HeartBeatWatcherConfig config, + HeartbeatConfigurationModel config, string group = null, Action hooks = null) { diff --git a/Elfo.Wardein.Watchers/HeartBeat/HeartBeatWatcher.cs b/Elfo.Wardein.Watchers/HeartBeat/HeartBeatWatcher.cs index aa0e8c3..7e5dbc4 100644 --- a/Elfo.Wardein.Watchers/HeartBeat/HeartBeatWatcher.cs +++ b/Elfo.Wardein.Watchers/HeartBeat/HeartBeatWatcher.cs @@ -1,37 +1,37 @@ using System; using System.Threading.Tasks; -using Elfo.Firmenich.Wardein.Abstractions.HeartBeat; +using Elfo.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Abstractions.HeartBeat; using Elfo.Wardein.Core; -using Elfo.Wardein.Watchers.HeartBeat.Config; using Warden.Watchers; namespace Elfo.Wardein.Watchers.HeartBeat { - public class HeartBeatWatcher : WardeinWatcher + public class HeartBeatWatcher : WardeinWatcher { private readonly string heartBeatAppHostname; private readonly IAmWardeinHeartBeatPersistanceService heartBeatPersistanceService; - public HeartBeatWatcher(HeartBeatWatcherConfig config, string name, string group = null) : base(name, config, group ) + public HeartBeatWatcher(HeartbeatConfigurationModel config, string name, string group = null) : base(name, config, group ) { if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Application name can not be empty", nameof(HeartBeatWatcherConfig)); + throw new ArgumentException("Application name can not be empty", nameof(HeartBeatWatcher)); - heartBeatAppHostname = config.HeartBeatAppName; + heartBeatAppHostname = config.ApplicationHostname; - heartBeatPersistanceService = ServicesContainer.WardeinHeartBeatPersistenceService(config.ConnectionString); + heartBeatPersistanceService = ServicesContainer.WardeinHeartBeatPersistenceService(); } - public static HeartBeatWatcher Create(HeartBeatWatcherConfig config, string group = null) + public static HeartBeatWatcher Create(HeartbeatConfigurationModel config, string group = null) { return new HeartBeatWatcher(config, $"{nameof(HeartBeatWatcher)}", group); } public override async Task ExecuteWatcherActionAsync() { - await heartBeatPersistanceService.UpdateHeartBeat(heartBeatAppHostname); - return await Task.FromResult(Task.CompletedTask as IWatcherCheckResult); + var result = await heartBeatPersistanceService.UpdateHeartBeat(heartBeatAppHostname); + return HeartBeatWatcherCheckResult.Create(this, result); } } } diff --git a/Elfo.Wardein.Watchers/HeartBeat/Config/HeartBeatWatcherCheckResult.cs b/Elfo.Wardein.Watchers/HeartBeat/HeartBeatWatcherCheckResult.cs similarity index 100% rename from Elfo.Wardein.Watchers/HeartBeat/Config/HeartBeatWatcherCheckResult.cs rename to Elfo.Wardein.Watchers/HeartBeat/HeartBeatWatcherCheckResult.cs diff --git a/Elfo.Wardein.Watchers/IISPool/Extensions.cs b/Elfo.Wardein.Watchers/IISPool/Extensions.cs index 363dc8f..a9eaf81 100644 --- a/Elfo.Wardein.Watchers/IISPool/Extensions.cs +++ b/Elfo.Wardein.Watchers/IISPool/Extensions.cs @@ -1,4 +1,5 @@ -using Elfo.Wardein.Watchers.GenericService; +using Elfo.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Watchers.GenericService; using System; using Warden.Core; using Warden.Watchers; @@ -8,7 +9,7 @@ namespace Elfo.Wardein.Watchers.IISPool public static class Extensions { public static WardenConfiguration.Builder AddIISPoolWatcher(this WardenConfiguration.Builder builder, - GenericServiceWatcherConfig config, + GenericServiceConfigurationModel config, string group = null, Action hooks = null) { diff --git a/Elfo.Wardein.Watchers/IISPool/IISPoolWatcher.cs b/Elfo.Wardein.Watchers/IISPool/IISPoolWatcher.cs index d994c76..ab810df 100644 --- a/Elfo.Wardein.Watchers/IISPool/IISPoolWatcher.cs +++ b/Elfo.Wardein.Watchers/IISPool/IISPoolWatcher.cs @@ -1,14 +1,15 @@ -using Elfo.Wardein.Watchers.GenericService; +using Elfo.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Watchers.GenericService; namespace Elfo.Wardein.Watchers.IISPool { public class IISPoolWatcher : GenericServiceWatcher { - protected IISPoolWatcher(GenericServiceWatcherConfig config, string name, string group = null) : base(config, name, group) + protected IISPoolWatcher(GenericServiceConfigurationModel config, string name, string group = null) : base(config, name, group) { } - public static IISPoolWatcher Create(GenericServiceWatcherConfig config, string group = null) + public static IISPoolWatcher Create(GenericServiceConfigurationModel config, string group = null) { return new IISPoolWatcher(config, $"{nameof(IISPoolWatcher)}", group); } diff --git a/Elfo.Wardein.Watchers/WardeinWatcher.cs b/Elfo.Wardein.Watchers/WardeinWatcher.cs index c9be5b6..0f2943f 100644 --- a/Elfo.Wardein.Watchers/WardeinWatcher.cs +++ b/Elfo.Wardein.Watchers/WardeinWatcher.cs @@ -1,10 +1,11 @@ -using NLog; +using Elfo.Wardein.Abstractions.Configuration.Models; +using NLog; using System.Threading.Tasks; using Warden.Watchers; namespace Elfo.Wardein.Watchers { - public abstract class WardeinWatcher : IWatcher where TConfig : IWatcherConfig + public abstract class WardeinWatcher : IWatcher where TConfig : IAmBaseConfigurationModel { protected WardeinWatcher(string name, TConfig config, string group = null) { diff --git a/Elfo.Wardein.Watchers/WardeinWatcherWithResolution.cs b/Elfo.Wardein.Watchers/WardeinWatcherWithResolution.cs new file mode 100644 index 0000000..961844d --- /dev/null +++ b/Elfo.Wardein.Watchers/WardeinWatcherWithResolution.cs @@ -0,0 +1,67 @@ +using Elfo.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Abstractions.Services; +using Elfo.Wardein.Abstractions.Watchers; +using Elfo.Wardein.Core; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Warden.Watchers; + +namespace Elfo.Wardein.Watchers +{ + public abstract class WardeinWatcherWithResolution : WardeinWatcher where T : IAmConfigurationModelWithResolution + { + private readonly IAmNotificationService notificationService; + + protected WardeinWatcherWithResolution(string name, T config, string group = null) : base(name, config, group) + { + notificationService = ServicesContainer.NotificationService(config.NotificationType); + } + + protected virtual string GetLoggingDisplayName => $"WatcherConfigurationId: {base.Config.WatcherConfigurationId}"; + + protected virtual async Task PerformActionOnServiceDown(WatcherStatusResult watcherStatusResult, Func howToActInCaseOfError) + { + if (IsFailureCountEqualToMaxRetyrCount() || IsMultipleOfReminderRetryCount()) + { + log.Warn($"Sending Fail Notification for {GetLoggingDisplayName}"); + await notificationService.SendNotificationAsync(Config.RecipientAddresses, Config.FailureMessage, + $"Attention: {GetLoggingDisplayName} is down on {Config.ApplicationHostname}"); + } + + if (howToActInCaseOfError != null) + { + log.Info($"trying to restore {GetLoggingDisplayName} on {Config.ApplicationHostname}"); + await howToActInCaseOfError(Config); + log.Info($"{GetLoggingDisplayName} was restarted"); + } + + #region Local Functions + + bool IsFailureCountEqualToMaxRetyrCount() => watcherStatusResult.FailureCount == Config.MaxRetryCount; + + bool IsMultipleOfReminderRetryCount() => watcherStatusResult.FailureCount % Config.SendReminderEmailAfterRetryCount == 0; + + #endregion + } + + protected virtual async Task PerformActionOnServiceAlive(WatcherStatusResult watcherStatusResult) + { + try + { + log.Info($"{GetLoggingDisplayName} is active"); + if (!watcherStatusResult.PreviousStatus) + { + log.Info($"Send Restored Notification for {GetLoggingDisplayName}"); + await notificationService.SendNotificationAsync(Config.RecipientAddresses, Config.RestoredMessage, + $"Good news: {GetLoggingDisplayName} has been restored succesfully"); + } + } + catch (Exception ex) + { + log.Error(ex, $"Unable to send email from {GetLoggingDisplayName} watcher"); + } + } + } +} diff --git a/Elfo.Wardein.Watchers/WebWatcher/Config/ObservarableWebWatcher.cs b/Elfo.Wardein.Watchers/WebWatcher/Config/ObservarableWebWatcher.cs deleted file mode 100644 index 69b2721..0000000 --- a/Elfo.Wardein.Watchers/WebWatcher/Config/ObservarableWebWatcher.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Elfo.Firmenich.Wardein.Abstractions.Configuration.Models; -using Newtonsoft.Json; - -namespace Elfo.Wardein.Watchers.WebWatcher.Config -{ - class ObservarableWebWatcher - { - [JsonProperty(PropertyName = "serviceName")] - public string ServiceName { get; set; } - - [JsonProperty(PropertyName = "maxRetryCount")] - public int MaxRetryCount { get; set; } - - [JsonProperty(PropertyName = "failMessage")] - public string FailMessage { get; set; } - - [JsonProperty(PropertyName = "restoredMessage")] - public string RestoredMessage { get; set; } - - [JsonProperty(PropertyName = "recipientAddress")] - public string RecipientAddress { get; set; } - - [JsonProperty(PropertyName = "notificationType")] - public string NotificationType { get; set; } = "Mail"; - - [JsonProperty(PropertyName = "sendRepeatedNotificationAfterSeconds")] - public double? SendRepeatedNotificationAfterSeconds { get; set; } - - [JsonProperty(PropertyName = "numberOfNotificationsWithoutRateLimitation")] - public int? NumberOfNotificationsWithoutRateLimitation { get; set; } - - [JsonProperty(PropertyName = "serviceManagerType")] - public ServiceManagerType ServiceManagerType { get; set; } - } -} diff --git a/Elfo.Wardein.Watchers/WebWatcher/Config/WebWatcherCheckResult.cs b/Elfo.Wardein.Watchers/WebWatcher/Config/WebWatcherCheckResult.cs deleted file mode 100644 index 01e6f81..0000000 --- a/Elfo.Wardein.Watchers/WebWatcher/Config/WebWatcherCheckResult.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using Warden.Watchers; - -namespace Elfo.Wardein.Watchers.WebWatcher -{ - //public class WebWatcherCheckResult : WatcherCheckResult - //{ - // public Uri Uri { get; } - - - // /// - // /// Factory method for creating a new instance of WebWatcherCheckResult. - // /// - // /// Instance of WebWatcher. - // /// Flag determining whether the performed check was valid. - // /// Base URL of the request. - // /// Instance of IHttpRequest. - // /// Instance of IHttpResponse. - // /// Custom description of the performed check. - // /// Instance of WebWatcherCheckResult. - // public static WebWatcherCheckResult Create(WebWatcher watcher, bool isValid, Uri uri, - // string description = "") - // => new WebWatcherCheckResult(); - //} -} diff --git a/Elfo.Wardein.Watchers/WebWatcher/Config/WebWatcherConfig.cs b/Elfo.Wardein.Watchers/WebWatcher/Config/WebWatcherConfig.cs deleted file mode 100644 index c5466ee..0000000 --- a/Elfo.Wardein.Watchers/WebWatcher/Config/WebWatcherConfig.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Newtonsoft.Json; -using System; - -namespace Elfo.Wardein.Watchers.WebWatcher -{ - public class WebWatcherConfig : IWatcherConfig - { - [JsonProperty(PropertyName = "isInMaintenanceMode")] - public bool IsInMaintenanceMode { get; protected set; } = false; - - [JsonProperty(PropertyName = "timeSpanFromSeconds")] - public double TimeSpanFromSeconds { get; protected set; } = 10; - - [JsonProperty(PropertyName = "useAuthentication")] - public bool UseAuthentication { get; protected set; } - - [JsonProperty(PropertyName = "authCredentials")] - public string AuthCredentials { get; protected set; } - - [JsonProperty(PropertyName = "assertWithRegex")] - public string AssertWithRegex { get; protected set; } - - [JsonProperty(PropertyName = "assertWithStatusCode")] - public bool AssertWithStatusCode { get; protected set; } - - [JsonProperty(PropertyName = "maxRetryCount")] - public int MaxRetryCount { get; protected set; } - - [JsonProperty(PropertyName = "sendReminderEmailAfterRetryCount")] - public int SendReminderEmailAfterRetryCount { get; protected set; } - - [JsonProperty(PropertyName = "associatedIISPool")] - public string AssociatedIISPool { get; protected set; } - - [JsonProperty(PropertyName = "recipientAddresses")] - public string RecipientAddresses { get; protected set; } - - [JsonProperty(PropertyName = "failureMessage")] - public string FailureMessage { get; protected set; } - - [JsonProperty(PropertyName = "restoredMessage")] - public string RestoredMessage { get; protected set; } - - [JsonProperty(PropertyName = "connectionString")] - public string ConnectionString { get; protected set; } - - [JsonProperty(PropertyName = "watcherConfigurationId")] - public int WatcherConfigurationId { get; protected set; } - [JsonProperty(PropertyName = "applicationId")] - public int ApplicationId { get; protected set; } - [JsonProperty(PropertyName = "applicationHostname")] - public string ApplicationHostname { get; protected set; } - - [JsonProperty(PropertyName = "url")] - public Uri Url { get; protected set; } - - [JsonProperty(PropertyName = "urlAlias")] - public string UrlAlias { get; protected set; } - } -} diff --git a/Elfo.Wardein.Watchers/WebWatcher/Extensions.cs b/Elfo.Wardein.Watchers/WebWatcher/Extensions.cs index 3801dcd..7aaa2dc 100644 --- a/Elfo.Wardein.Watchers/WebWatcher/Extensions.cs +++ b/Elfo.Wardein.Watchers/WebWatcher/Extensions.cs @@ -1,4 +1,5 @@ -using System; +using Elfo.Wardein.Abstractions.Configuration.Models; +using System; using System.Collections.Generic; using System.Text; using Warden.Core; @@ -8,16 +9,13 @@ namespace Elfo.Wardein.Watchers.WebWatcher { public static class Extensions { - //internal static string GetFullUrl(this IHttpRequest request, string baseUrl) - //{ - // var endpoint = request.Endpoint; - // if (string.IsNullOrWhiteSpace(endpoint)) - // return baseUrl; - - // if (baseUrl.EndsWith("/")) - // return $"{baseUrl}{(endpoint.StartsWith("/") ? endpoint.Substring(1) : $"{endpoint}")}"; - - // return $"{baseUrl}{(endpoint.StartsWith("/") ? endpoint : $"/{endpoint}")}"; - //} + public static WardenConfiguration.Builder AddWebWatcher(this WardenConfiguration.Builder builder, + WebWatcherConfigurationModel config, + string group = null, + Action hooks = null) + { + builder.AddWatcher(WebWatcher.Create(config, group), hooks, TimeSpan.FromSeconds(config.TimeSpanFromSeconds)); + return builder; + } } } diff --git a/Elfo.Wardein.Watchers/WebWatcher/WebWatcher.cs b/Elfo.Wardein.Watchers/WebWatcher/WebWatcher.cs index a41209b..2053034 100644 --- a/Elfo.Wardein.Watchers/WebWatcher/WebWatcher.cs +++ b/Elfo.Wardein.Watchers/WebWatcher/WebWatcher.cs @@ -1,6 +1,6 @@ -using Elfo.Firmenich.Wardein.Abstractions.Watchers; -using Elfo.Firmenich.Wardein.Abstractions.WebWatcher; -using Elfo.Firmenich.Wardein.Core.ServiceManager; +using Elfo.Wardein.Abstractions.Watchers; +using Elfo.Wardein.Abstractions.WebWatcher; +using Elfo.Wardein.Core.ServiceManager; using Elfo.Wardein.Core; using Elfo.Wardein.Core.Helpers; using Elfo.Wardein.Core.NotificationService; @@ -9,123 +9,82 @@ using System; using System.Threading.Tasks; using Warden.Watchers; +using Elfo.Wardein.Abstractions.Configuration.Models; namespace Elfo.Wardein.Watchers.WebWatcher { - public class WebWatcher : WardeinWatcher + public class WebWatcher : WardeinWatcherWithResolution { - private readonly WebWatcherConfig configuration; private readonly IAmWatcherPersistenceService watcherPersistenceService; - private readonly HttpClientUrlResponseManager urlResponseManager; + private readonly IAmUrlResponseManager urlResponseManager; protected static ILogger log = LogManager.GetCurrentClassLogger(); - protected WebWatcher(string name, WebWatcherConfig config, string group) : base(name, config, group) + protected WebWatcher(string name, string group, WebWatcherConfigurationModel config) : base(name, config, group) { if (string.IsNullOrEmpty(name)) throw new ArgumentException("Watcher name can not be empty."); - if (configuration == null) + if (config == null) { - throw new ArgumentNullException(nameof(configuration), + throw new ArgumentNullException(nameof(config), "Web Watcher configuration has not been provided."); } + watcherPersistenceService = ServicesContainer.WatcherPersistenceService(); + urlResponseManager = ServicesContainer.UrlResponseManager(); + } - configuration = config; - watcherPersistenceService = ServicesContainer.WatcherPersistenceService(configuration.ConnectionString); - + public static WebWatcher Create(WebWatcherConfigurationModel config, string group = null) + { + return new WebWatcher($"{nameof(WebWatcher)}", group, config); } public override async Task ExecuteWatcherActionAsync() { + bool result = false; Log.Info($"---\tStarting {Name}\t---"); try { var guid = Guid.NewGuid(); log.Info($"{Environment.NewLine}{"-".Repeat(24)} UrlWatcher check @ {guid} started {"-".Repeat(24)}"); - await RunCheck(); + result = await RunCheck(); - log.Info($"{Environment.NewLine}{"-".Repeat(24)} UrlWatcher check @ {guid} finished {"-".Repeat(24)}{Environment.NewLine.Repeat(24)}"); + log.Info($"{Environment.NewLine}{"-".Repeat(24)} UrlWatcher check @ {guid} finished {"-".Repeat(24)}{Environment.NewLine}"); } catch (Exception ex) { log.Error(ex, $"Exception inside UrlWatcher action: {ex.ToString()}\n"); } - - return await Task.FromResult(null); + return WebWatcherCheckResult.Create(this, result); } - internal virtual async Task RunCheck() + internal virtual async Task RunCheck() { - log.Info($"Starting check on {GetUrlDisplayName}"); + log.Info($"Starting check on {GetLoggingDisplayName}"); - var notificationService = ServicesContainer.NotificationService(NotificationType.Mail); - var isHealthy = await urlResponseManager.IsHealthy(configuration.AssertWithStatusCode, configuration.AssertWithRegex, configuration.Url); + var notificationService = ServicesContainer.NotificationService(Config.NotificationType); + var isHealthy = await urlResponseManager.IsHealthy(Config.AssertWithStatusCode, Config.AssertWithRegex, Config.Url); var currentStatus = await watcherPersistenceService.UpsertCurrentStatus ( - watcherConfigurationId: configuration.WatcherConfigurationId, - applicationId: configuration.ApplicationId, - applicationHostname: configuration.ApplicationHostname, + watcherConfigurationId: Config.WatcherConfigurationId, + applicationId: Config.ApplicationId, + applicationHostname: Config.ApplicationHostname, isHealthy: isHealthy ); if (!isHealthy) { - await PerformActionOnServiceDown(configuration.AssociatedIISPool); + await PerformActionOnServiceDown(currentStatus, async configuration => await urlResponseManager.RestartPool(configuration.AssociatedIISPool)); } else { - await PerformActionOnServiceAlive(); + await PerformActionOnServiceAlive(currentStatus); } - log.Info($"Finished checking {GetUrlDisplayName}"); - #region Local Functions - - async Task PerformActionOnServiceDown(string poolName) - { - if (IsFailureCountEqualToMaxRetyrCount() || IsMultipleOfReminderRetryCount()) - { - log.Warn($"Sending Fail Notification for {GetUrlDisplayName}"); - await notificationService.SendNotificationAsync(configuration.RecipientAddresses, configuration.FailureMessage, - $"Attention: {GetUrlDisplayName} is down on {configuration.ApplicationHostname}"); - } - - if (string.IsNullOrWhiteSpace(poolName)) - { - log.Info($"trying to restore {poolName} on {configuration.ApplicationHostname}"); - await urlResponseManager.RestartPool(poolName); - log.Info($"{poolName} was restarted"); - } - - #region Local Functions - - bool IsFailureCountEqualToMaxRetyrCount() => currentStatus.FailureCount == configuration.MaxRetryCount; - - bool IsMultipleOfReminderRetryCount() => currentStatus.FailureCount % configuration.SendReminderEmailAfterRetryCount == 0; - - #endregion - } - - async Task PerformActionOnServiceAlive() - { - try - { - log.Info($"{GetUrlDisplayName} is active"); - if (!currentStatus.PreviousStatus) - { - log.Info($"Send Restored Notification for {GetUrlDisplayName}"); - await notificationService.SendNotificationAsync(configuration.RecipientAddresses, configuration.RestoredMessage, - $"Good news: {GetUrlDisplayName} has been restored succesfully"); - } - } - catch (Exception ex) - { - log.Error(ex, $"Unable to send email fro {GetUrlDisplayName}"); - } - } - #endregion + log.Info($"Finished checking {GetLoggingDisplayName}"); + return isHealthy; } - private string GetUrlDisplayName => string.IsNullOrWhiteSpace(configuration.UrlAlias) ? configuration.Url.AbsoluteUri : configuration.UrlAlias; + protected override string GetLoggingDisplayName => string.IsNullOrWhiteSpace(Config.UrlAlias) ? Config.Url.AbsoluteUri : Config.UrlAlias; } } diff --git a/Elfo.Wardein.Watchers/WebWatcher/WebWatcherCheckResult.cs b/Elfo.Wardein.Watchers/WebWatcher/WebWatcherCheckResult.cs new file mode 100644 index 0000000..2e2a7ff --- /dev/null +++ b/Elfo.Wardein.Watchers/WebWatcher/WebWatcherCheckResult.cs @@ -0,0 +1,31 @@ +using System; +using Warden.Watchers; + +namespace Elfo.Wardein.Watchers.WebWatcher +{ + public class WebWatcherCheckResult : WatcherCheckResult + { + public WebWatcherCheckResult(WebWatcher watcher, bool isValid, string description, Uri uri) : base(watcher, isValid, description) + { + Uri = uri; + } + + public Uri Uri { get; } + + + /// + /// Factory method for creating a new instance of WebWatcherCheckResult. + /// + /// Instance of WebWatcher. + /// Flag determining whether the performed check was valid. + /// Base URL of the request. + /// Instance of IHttpRequest. + /// Instance of IHttpResponse. + /// Custom description of the performed check. + /// Instance of WebWatcherCheckResult. + public static WebWatcherCheckResult Create(WebWatcher watcher, bool isValid, Uri uri, string description = "") + { + return new WebWatcherCheckResult(watcher, isValid, description, uri); + } + } +} diff --git a/Elfo.Wardein.Watchers/WindowsService/Extensions.cs b/Elfo.Wardein.Watchers/WindowsService/Extensions.cs index e35fc52..a6fb4c4 100644 --- a/Elfo.Wardein.Watchers/WindowsService/Extensions.cs +++ b/Elfo.Wardein.Watchers/WindowsService/Extensions.cs @@ -1,4 +1,5 @@ -using Elfo.Wardein.Watchers.GenericService; +using Elfo.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Watchers.GenericService; using System; using System.Collections.Generic; using System.Text; @@ -11,7 +12,7 @@ namespace Elfo.Wardein.Watchers.WindowsService public static class Extensions { public static WardenConfiguration.Builder AddWindowsServiceWatcher(this WardenConfiguration.Builder builder, - GenericServiceWatcherConfig config, + GenericServiceConfigurationModel config, string group = null, Action hooks = null) { diff --git a/Elfo.Wardein.Watchers/WindowsService/WindowsServiceWatcher.cs b/Elfo.Wardein.Watchers/WindowsService/WindowsServiceWatcher.cs index 7e4e1cc..d451d4c 100644 --- a/Elfo.Wardein.Watchers/WindowsService/WindowsServiceWatcher.cs +++ b/Elfo.Wardein.Watchers/WindowsService/WindowsServiceWatcher.cs @@ -1,13 +1,14 @@ -using Elfo.Wardein.Watchers.GenericService; +using Elfo.Wardein.Abstractions.Configuration.Models; +using Elfo.Wardein.Watchers.GenericService; namespace Elfo.Wardein.Watchers.WindowsService { public class WindowsServiceWatcher : GenericServiceWatcher { - protected WindowsServiceWatcher(GenericServiceWatcherConfig config, string name, string group = null) : base(config, name, group) + protected WindowsServiceWatcher(GenericServiceConfigurationModel config, string name, string group = null) : base(config, name, group) { } - public static WindowsServiceWatcher Create(GenericServiceWatcherConfig config, string group = null) + public static WindowsServiceWatcher Create(GenericServiceConfigurationModel config, string group = null) { return new WindowsServiceWatcher(config, $"{nameof(WindowsServiceWatcher)}", group); } diff --git a/Elfo.Wardein/Elfo.Wardein.csproj b/Elfo.Wardein/Elfo.Wardein.csproj index 470c4b4..fce9e2c 100644 --- a/Elfo.Wardein/Elfo.Wardein.csproj +++ b/Elfo.Wardein/Elfo.Wardein.csproj @@ -7,6 +7,16 @@ Group 1.ico + + + + + + + PreserveNewest + + + diff --git a/Elfo.Wardein/Program.cs b/Elfo.Wardein/Program.cs index 48a1e07..ac843a9 100644 --- a/Elfo.Wardein/Program.cs +++ b/Elfo.Wardein/Program.cs @@ -1,7 +1,10 @@ -using Elfo.Wardein.Services; +using Elfo.Wardein.Core; +using Elfo.Wardein.Services; using Elfo.Wardein.Watchers; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; using NLog; using PeterKottas.DotNetCore.WindowsService; using PeterKottas.DotNetCore.WindowsService.Interfaces; @@ -17,6 +20,14 @@ static void Main(string[] args) { try { + var appConfiguration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .Build(); + + WardeinBaseConfiguration wbc = new WardeinBaseConfiguration(); + appConfiguration.Bind(wbc); + ServicesContainer.Initialize(wbc); + var serviceBuilder = new ServiceBuilder(); var wardeinService = new WardeinMicroService(serviceBuilder); @@ -52,15 +63,15 @@ static void Main(string[] args) }); }).Start(); - new Thread(() => - { - Thread.CurrentThread.IsBackground = true; + //new Thread(() => + //{ + // Thread.CurrentThread.IsBackground = true; - log.Debug("Starting APIs..."); - serviceBuilder.ConfigureAndRunAPIHosting().Wait(); - log.Debug("APIs started"); + // log.Debug("Starting APIs..."); + // serviceBuilder.ConfigureAndRunAPIHosting().Wait(); + // log.Debug("APIs started"); - }).Start(); + //}).Start(); } catch (Exception ex) {