Skip to content

Commit

Permalink
added configuration for service recovery options
Browse files Browse the repository at this point in the history
  • Loading branch information
paulomorgado authored and Seabiscuit committed Apr 22, 2018
1 parent 4686319 commit 4c93f3c
Showing 5 changed files with 265 additions and 11 deletions.
145 changes: 145 additions & 0 deletions src/Topshelf.Extensions.Configuration.Tests/Configuration_Specs.cs
Original file line number Diff line number Diff line change
@@ -14,11 +14,15 @@ namespace Topshelf.Extensions.Configuration.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using Hosts;
using Microsoft.Extensions.Configuration;
using NUnit.Framework;
using Runtime;
using Topshelf.Configuration;
using Topshelf.HostConfigurators;
using Topshelf.Options;
using Topshelf.Runtime.Windows;

[TestFixture]
public class Passing_install
@@ -406,6 +410,52 @@ public void Should_create_a_service_that_runs_as_network_service()
Assert.AreEqual("", installHost.InstallSettings.Credentials.Password);
}

[Test]
public void Should_create_a_service_with_recovery_options()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string>
{
{ "topshelf:ServiceRecovery:RecoverOnCrashOnly", "true" },
{ "topshelf:ServiceRecovery:ResetPeriod", "123" },
{ "topshelf:ServiceRecovery:RecoveryActions:0:Type", "RestartService" },
{ "topshelf:ServiceRecovery:RecoveryActions:0:Delay", "1234" },
{ "topshelf:ServiceRecovery:RecoveryActions:1:Type", "RestartSystem" },
{ "topshelf:ServiceRecovery:RecoveryActions:1:Delay", "4567" },
{ "topshelf:ServiceRecovery:RecoveryActions:1:Message", "message" },
{ "topshelf:ServiceRecovery:RecoveryActions:2:Type", "RunProgram" },
{ "topshelf:ServiceRecovery:RecoveryActions:2:Delay", "8901" },
{ "topshelf:ServiceRecovery:RecoveryActions:2:Message", "command" },
})
.Build()
.GetSection("topshelf");

var parsedConfiguration = configuration.Parse().First();

Assert.IsInstanceOf<ServiceRecoveryOption>(parsedConfiguration);
var serviceRecoveryOptions = typeof(ServiceRecoveryOption)
.GetField("serviceRecoveryOptions", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.GetValue(parsedConfiguration) as ServiceRecoveryOptions;

Assert.AreEqual(true, serviceRecoveryOptions.RecoverOnCrashOnly);
Assert.AreEqual(123, serviceRecoveryOptions.ResetPeriod);

var actions = serviceRecoveryOptions.Actions.ToArray();

Assert.IsInstanceOf<RestartServiceRecoveryAction>(actions[0]);
var restartServiceRecoveryAction = actions[0] as RestartServiceRecoveryAction;
Assert.AreEqual(TimeSpan.FromMinutes(1234).TotalMilliseconds, restartServiceRecoveryAction.Delay);

Assert.IsInstanceOf<RestartSystemRecoveryAction>(actions[1]);
var restartSystemRecoveryAction = actions[1] as RestartSystemRecoveryAction;
Assert.AreEqual(TimeSpan.FromMinutes(4567).TotalMilliseconds, restartSystemRecoveryAction.Delay);
Assert.AreEqual("message", restartSystemRecoveryAction.RestartMessage);

Assert.IsInstanceOf<RunProgramRecoveryAction>(actions[2]);
var runProgramRecoveryAction = actions[2] as RunProgramRecoveryAction;
Assert.AreEqual(TimeSpan.FromMinutes(8901).TotalMilliseconds, runProgramRecoveryAction.Delay);
}

class MyService : ServiceControl
{
public bool Start(HostControl hostControl)
@@ -428,5 +478,100 @@ public bool Continue(HostControl hostControl)
throw new NotImplementedException();
}
}

class MyHostConfigurator : HostConfigurator
{
public HostBuilderConfigurator Configuratior { get; private set; }

public void AddCommandLineDefinition(string name, Action<string> callback)
{
throw new NotImplementedException();
}

public void AddCommandLineSwitch(string name, Action<bool> callback)
{
throw new NotImplementedException();
}

public void AddConfigurator(HostBuilderConfigurator configurator)
{
this.Configuratior = configurator;
}

public void ApplyCommandLine()
{
throw new NotImplementedException();
}

public void ApplyCommandLine(string commandLine)
{
throw new NotImplementedException();
}

public void EnablePauseAndContinue()
{
throw new NotImplementedException();
}

public void EnableSessionChanged()
{
throw new NotImplementedException();
}

public void EnableShutdown()
{
throw new NotImplementedException();
}

public void OnException(Action<Exception> callback)
{
throw new NotImplementedException();
}

public void SetDescription(string description)
{
throw new NotImplementedException();
}

public void SetDisplayName(string name)
{
throw new NotImplementedException();
}

public void SetInstanceName(string instanceName)
{
throw new NotImplementedException();
}

public void SetServiceName(string name)
{
throw new NotImplementedException();
}

public void SetStartTimeout(TimeSpan startTimeOut)
{
throw new NotImplementedException();
}

public void SetStopTimeout(TimeSpan stopTimeOut)
{
throw new NotImplementedException();
}

public void UseEnvironmentBuilder(EnvironmentBuilderFactory environmentBuilderFactory)
{
throw new NotImplementedException();
}

public void UseHostBuilder(HostBuilderFactory hostBuilderFactory)
{
throw new NotImplementedException();
}

public void UseServiceBuilder(ServiceBuilderFactory serviceBuilderFactory)
{
throw new NotImplementedException();
}
}
}
}
44 changes: 43 additions & 1 deletion src/Topshelf.Extensions.Configuration/ConfigurationExtensions.cs
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ namespace Topshelf.Configuration
using Microsoft.Extensions.Configuration;
using Topshelf.HostConfigurators;
using Topshelf.Options;
using Topshelf.Runtime.Windows;

/// <summary>
/// Provides Topshelf extensions for Microsoft extensions for configuration.
@@ -115,7 +116,7 @@ public static IEnumerable<Option> Parse(this IConfigurationSection configuration
}
else if (string.Equals(entry.Key, "Interactive", StringComparison.OrdinalIgnoreCase))
{
if ((bool)TypeDescriptor.GetConverter(typeof(bool)).ConvertFromInvariantString(entry.Value))
if (configuration.GetSection("Interactive").Get<bool>())
{
options.Add(new InteractiveOption());
}
@@ -157,6 +158,47 @@ public static IEnumerable<Option> Parse(this IConfigurationSection configuration
options.Add(new DependencyOption(dependency.Value));
}
}
else if (string.Equals(entry.Key, "ServiceRecovery", StringComparison.OrdinalIgnoreCase))
{
var section = configuration.GetSection("ServiceRecovery");

var serviceRecoveryOptions = new ServiceRecoveryOptions
{
RecoverOnCrashOnly = section.GetSection(nameof(ServiceRecoveryOptions.RecoverOnCrashOnly)).Get<bool>(),
ResetPeriod = section.GetSection(nameof(ServiceRecoveryOptions.ResetPeriod)).Get<int>()
};

foreach (var recoveryActionSecion in section.GetSection("RecoveryActions")?.GetChildren())
{
var recoveryActionType = recoveryActionSecion.GetSection("Type")?.Value;

switch (recoveryActionType)
{
case "RestartService":
serviceRecoveryOptions.AddAction(
new RestartServiceRecoveryAction(
TimeSpan.FromMinutes(
recoveryActionSecion.GetSection("Delay").Get<int>())));
break;
case "RestartSystem":
serviceRecoveryOptions.AddAction(
new RestartSystemRecoveryAction(
TimeSpan.FromMinutes(
recoveryActionSecion.GetSection("Delay").Get<int>()),
recoveryActionSecion.GetSection("Message").Value));
break;
case "RunProgram":
serviceRecoveryOptions.AddAction(
new RunProgramRecoveryAction(
TimeSpan.FromMinutes(
recoveryActionSecion.GetSection("Delay").Get<int>()),
recoveryActionSecion.GetSection("Command").Value));
break;
}
}

options.Add(new ServiceRecoveryOption(serviceRecoveryOptions));
}
#endif
}

Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ namespace Topshelf.Options
using Topshelf.HostConfigurators;

/// <summary>
/// Represents a option to set a service dependency.
/// Represents an option to set a service dependency.
/// </summary>
/// <seealso cref="Option" />
internal class DependencyOption
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2007-2014 Chris Patterson, Dru Sellers, Travis Smith, et. al.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#if !NETCORE
namespace Topshelf.Options
{
using Topshelf.HostConfigurators;
using Topshelf.Runtime.Windows;

/// <summary>
/// Represents an option to set a service recovery options.
/// </summary>
/// <seealso cref="Option" />
public class ServiceRecoveryOption
: Option
{
private readonly ServiceRecoveryOptions serviceRecoveryOptions;

/// <summary>
/// Initializes a new instance of the <see cref="ServiceRecoveryOption"/> class.
/// </summary>
/// <param name="serviceRecoveryOptions">The service recovery options.</param>
public ServiceRecoveryOption(ServiceRecoveryOptions serviceRecoveryOptions)
{
this.serviceRecoveryOptions = serviceRecoveryOptions;
}

/// <summary>
/// Applies the option to the specified host configurator.
/// </summary>
/// <param name="configurator">The host configurator.</param>
public void ApplyTo(HostConfigurator configurator)
{
var recoveryHostConfigurator = new ServiceRecoveryHostConfigurator();

foreach (var option in serviceRecoveryOptions.Actions)
{
switch (option)
{
case RestartServiceRecoveryAction restartServiceRecoveryAction:
recoveryHostConfigurator.RestartService(restartServiceRecoveryAction.Delay);
break;
case RestartSystemRecoveryAction restartSystemRecoveryAction:
recoveryHostConfigurator.RestartComputer(restartSystemRecoveryAction.Delay, restartSystemRecoveryAction.RestartMessage);
break;
case RunProgramRecoveryAction runProgramRecoveryAction:
recoveryHostConfigurator.RunProgram(runProgramRecoveryAction.Delay, runProgramRecoveryAction.Command);
break;
}
}

if (this.serviceRecoveryOptions.RecoverOnCrashOnly)
{
recoveryHostConfigurator.OnCrashOnly();
}

recoveryHostConfigurator.SetResetPeriod(this.serviceRecoveryOptions.ResetPeriod);

configurator.AddConfigurator(recoveryHostConfigurator);
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -21,16 +21,11 @@
<ProjectReference Include="..\Topshelf\Topshelf.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions">
<Version>2.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net452'">
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions">
<Version>1.1.2</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Options\" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="1.1.2" />
</ItemGroup>
</Project>

0 comments on commit 4c93f3c

Please sign in to comment.