Convert to BackgroundService where applicable

Also fix case of localhost SqlServer in SetupWizard
This commit is contained in:
Jordan Dominion
2023-06-26 00:04:55 -04:00
parent e0e9d9bce1
commit 2a3a72a1e7
4 changed files with 28 additions and 96 deletions
@@ -32,7 +32,7 @@ using YamlDotNet.Serialization;
namespace Tgstation.Server.Host.Setup
{
/// <inheritdoc />
sealed class SetupWizard : IHostedService
sealed class SetupWizard : BackgroundService
{
/// <summary>
/// The <see cref="IIOManager"/> for the <see cref="SetupWizard"/>.
@@ -129,15 +129,12 @@ namespace Tgstation.Server.Host.Setup
}
/// <inheritdoc />
public async Task StartAsync(CancellationToken cancellationToken)
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
await CheckRunWizard(cancellationToken);
applicationLifetime.StopApplication();
}
/// <inheritdoc />
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
/// <summary>
/// A prompt for a yes or no value.
/// </summary>
@@ -493,7 +490,7 @@ namespace Tgstation.Server.Host.Setup
bool useWinAuth;
if (databaseConfiguration.DatabaseType == DatabaseType.SqlServer && platformIdentifier.IsWindows)
{
var defaultResponse = serverAddressEntry.AddressList.Any(IPAddress.IsLoopback)
var defaultResponse = serverAddressEntry?.AddressList.Any(IPAddress.IsLoopback) ?? false
? (bool?)true
: null;
useWinAuth = await PromptYesNo("Use Windows Authentication?", defaultResponse, cancellationToken);
@@ -17,7 +17,7 @@ namespace Tgstation.Server.Host.System
/// <summary>
/// Implements the SystemD notify service protocol.
/// </summary>
sealed class SystemDManager : IHostedService, IRestartHandler, IDisposable
sealed class SystemDManager : BackgroundService, IRestartHandler, IDisposable
{
/// <summary>
/// The sd_notify command for notifying the watchdog we are alive.
@@ -44,16 +44,6 @@ namespace Tgstation.Server.Host.System
/// </summary>
readonly ILogger<SystemDManager> logger;
/// <summary>
/// The <see cref="CancellationTokenSource"/> for <see cref="runTask"/>.
/// </summary>
readonly CancellationTokenSource watchdogCts;
/// <summary>
/// The main task executing in the <see cref="SystemDManager"/>.
/// </summary>
Task runTask;
/// <summary>
/// If TGS is going to restart.
/// </summary>
@@ -87,22 +77,13 @@ namespace Tgstation.Server.Host.System
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
restartRegistration = serverControl.RegisterForRestart(this);
try
{
watchdogCts = new CancellationTokenSource();
}
catch
{
restartRegistration.Dispose();
throw;
}
}
/// <inheritdoc />
public void Dispose()
public override void Dispose()
{
base.Dispose();
restartRegistration.Dispose();
watchdogCts.Dispose();
}
/// <inheritdoc />
@@ -114,36 +95,16 @@ namespace Tgstation.Server.Host.System
}
/// <inheritdoc />
public Task StartAsync(CancellationToken cancellationToken)
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
if (SendSDNotify(SDNotifyWatchdog))
{
logger.LogDebug("SystemD detected");
runTask = RunAsync(watchdogCts.Token);
}
else
{
logger.LogDebug("SystemD not detected");
runTask = Task.CompletedTask;
return;
}
return Task.CompletedTask;
}
logger.LogDebug("SystemD detected");
/// <inheritdoc />
public async Task StopAsync(CancellationToken cancellationToken)
{
watchdogCts.Cancel();
await runTask.WithToken(cancellationToken);
}
/// <summary>
/// Runs the <see cref="SystemDManager"/>.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
async Task RunAsync(CancellationToken cancellationToken)
{
if (applicationLifetime.ApplicationStarted.IsCancellationRequested)
throw new InvalidOperationException("RunAsync called after application started!");
@@ -16,7 +16,7 @@ using Tgstation.Server.Host.Utils;
namespace Tgstation.Server.Host.System
{
/// <inheritdoc />
sealed class WindowsNetworkPromptReaper : IHostedService, INetworkPromptReaper, IDisposable
sealed class WindowsNetworkPromptReaper : BackgroundService, INetworkPromptReaper
{
/// <summary>
/// Number of times to send the button click message. Should be at least 2 or it may fail to focus the window.
@@ -38,21 +38,11 @@ namespace Tgstation.Server.Host.System
/// </summary>
readonly ILogger<WindowsNetworkPromptReaper> logger;
/// <summary>
/// The <see cref="CancellationTokenSource"/> for the <see cref="WindowsNetworkPromptReaper"/>.
/// </summary>
readonly CancellationTokenSource cancellationTokenSource;
/// <summary>
/// The list of <see cref="IProcess"/>s registered.
/// </summary>
readonly List<IProcess> registeredProcesses;
/// <summary>
/// The <see cref="Task"/> representing the lifetime of the <see cref="WindowsNetworkPromptReaper"/>.
/// </summary>
Task runTask;
/// <summary>
/// Callback for <see cref="NativeMethods.EnumChildWindows(IntPtr, NativeMethods.EnumWindowProc, IntPtr)"/>.
/// </summary>
@@ -106,26 +96,6 @@ namespace Tgstation.Server.Host.System
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
registeredProcesses = new List<IProcess>();
cancellationTokenSource = new CancellationTokenSource();
}
/// <inheritdoc />
public void Dispose() => cancellationTokenSource.Dispose();
/// <inheritdoc />
public Task StartAsync(CancellationToken cancellationToken)
{
runTask = Run(cancellationTokenSource.Token);
return Task.CompletedTask;
}
/// <inheritdoc />
public async Task StopAsync(CancellationToken cancellationToken)
{
logger.LogTrace("Stopping network prompt reaper...");
cancellationTokenSource.Cancel();
await runTask;
registeredProcesses.Clear();
}
/// <inheritdoc />
@@ -150,12 +120,8 @@ namespace Tgstation.Server.Host.System
}, TaskScheduler.Current);
}
/// <summary>
/// Main loop for the <see cref="WindowsNetworkPromptReaper"/>.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
async Task Run(CancellationToken cancellationToken)
/// <inheritdoc />
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
logger.LogDebug("Starting network prompt reaper...");
try
@@ -221,7 +187,8 @@ namespace Tgstation.Server.Host.System
}
finally
{
logger.LogDebug("Exiting network prompt reaper...");
registeredProcesses.Clear();
logger.LogTrace("Exiting network prompt reaper...");
}
}
}
@@ -91,13 +91,13 @@ namespace Tgstation.Server.Host.Setup.Tests
mockPlatformIdentifier.SetupGet(x => x.IsWindows).Returns(true).Verifiable();
mockAsyncDelayer.Setup(x => x.Delay(It.IsAny<TimeSpan>(), It.IsAny<CancellationToken>())).Returns(Task.CompletedTask).Verifiable();
await wizard.StartAsync(default);
await RunWizard();
testGeneralConfig.SetupWizardMode = SetupWizardMode.Force;
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => wizard.StartAsync(default));
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => RunWizard());
testGeneralConfig.SetupWizardMode = SetupWizardMode.Only;
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => wizard.StartAsync(default));
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => RunWizard());
mockConsole.SetupGet(x => x.Available).Returns(true).Verifiable();
mockIOManager.Setup(x => x.FileExists(It.IsNotNull<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(true)).Verifiable();
@@ -279,13 +279,20 @@ namespace Tgstation.Server.Host.Setup.Tests
.Returns(Task.CompletedTask)
.Verifiable();
await wizard.StartAsync(default);
async Task RunWizard()
{
await wizard.StartAsync(default);
await wizard.ExecuteTask;
await wizard.StopAsync(default);
}
await RunWizard();
//first real run
await wizard.StartAsync(default);
await RunWizard();
//second run
mockIOManager.Setup(x => x.ReadAllBytes(It.IsNotNull<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(Encoding.UTF8.GetBytes(String.Empty))).Verifiable();
await wizard.StartAsync(default);
await RunWizard();
//third run
testGeneralConfig.SetupWizardMode = SetupWizardMode.Autodetect;
@@ -309,7 +316,7 @@ namespace Tgstation.Server.Host.Setup.Tests
return Task.CompletedTask;
}).Verifiable();
await Assert.ThrowsExceptionAsync<OperationCanceledException>(() => wizard.StartAsync(default));
await Assert.ThrowsExceptionAsync<OperationCanceledException>(() => RunWizard());
Assert.AreEqual(finalInputSequence.Count, inputPos);
mockFailCommand.VerifyAll();