From bdaa8b9a6099bfcc5c0750044552e85eef7f9a6d Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Thu, 5 Jul 2018 11:50:36 -0400 Subject: [PATCH] All that's left is to write the watchdog --- .../Models/DreamDaemon.cs | 16 +- .../Components/DreamDaemon.cs | 216 ------------------ .../Components/IDreamDaemon.cs | 93 -------- .../Components/IInstance.cs | 5 +- .../Components/ILaunchParametersFactory.cs | 19 -- .../Components/Instance.cs | 16 +- .../Components/Watchdog/IWatchdog.cs | 74 +++++- .../Components/Watchdog/LaunchResult.cs | 2 +- .../Components/Watchdog/RebootState.cs | 2 +- .../Components/Watchdog/Watchdog.cs | 3 - .../Watchdog/WatchdogLaunchResult.cs | 17 +- .../Controllers/DreamDaemonController.cs | 58 +++-- 12 files changed, 136 insertions(+), 385 deletions(-) delete mode 100644 src/Tgstation.Server.Host/Components/DreamDaemon.cs delete mode 100644 src/Tgstation.Server.Host/Components/IDreamDaemon.cs delete mode 100644 src/Tgstation.Server.Host/Components/ILaunchParametersFactory.cs diff --git a/src/Tgstation.Server.Api/Models/DreamDaemon.cs b/src/Tgstation.Server.Api/Models/DreamDaemon.cs index 4a332b36dd..3ca0f22d87 100644 --- a/src/Tgstation.Server.Api/Models/DreamDaemon.cs +++ b/src/Tgstation.Server.Api/Models/DreamDaemon.cs @@ -9,10 +9,16 @@ namespace Tgstation.Server.Api.Models public sealed class DreamDaemon : DreamDaemonSettings { /// - /// When the live revision was compiled + /// The live revision /// [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadRevision)] - public CompileJob CompileJob { get; set; } + public CompileJob ActiveCompileJob { get; set; } + + /// + /// The next revision to go live + /// + [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadRevision)] + public CompileJob StagedCompileJob { get; set; } /// /// The current status of @@ -31,5 +37,11 @@ namespace Tgstation.Server.Api.Models /// [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadMetadata)] public ushort? CurrentPort { get; set; } + + /// + /// The webclient status the running instance is set to + /// + [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadMetadata)] + public bool? CurrentAllowWebclient { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/DreamDaemon.cs b/src/Tgstation.Server.Host/Components/DreamDaemon.cs deleted file mode 100644 index f699150079..0000000000 --- a/src/Tgstation.Server.Host/Components/DreamDaemon.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Tgstation.Server.Api.Models; -using Tgstation.Server.Api.Models.Internal; - -namespace Tgstation.Server.Host.Components -{ - /// - sealed class DreamDaemon : IDreamDaemon, ILaunchParametersFactory, IDisposable - { - /// - public bool Running { get; private set; } - - /// - public ushort? CurrentPort { get; private set; } - - /// - public string AccessToken { get; private set; } - - /// - public DreamDaemonSecurity? CurrentSecurity { get; private set; } - - /// - public bool SoftRebooting { get; private set; } - - /// - public bool SoftStopping { get; private set; } - - /// - public DreamDaemonLaunchParameters LastLaunchParameters { get; private set; } - - /// - public Host.Models.CompileJob LastCompileJob => watchdog.CurrentCompileJob; - - /// - /// The for - /// - readonly IEventConsumer eventConsumer; - /// - /// The for - /// - readonly IInterop interop; - /// - /// The for - /// - readonly IWatchdog watchdog; - - /// - /// Used for write control to class variables - /// - readonly SemaphoreSlim semaphore; - - /// - /// If should start DD - /// - readonly bool autoStart; - - /// - /// The current - /// - DreamDaemonLaunchParameters currentLaunchParameters; - - /// - /// Construct - /// - /// The value of - /// The value of - /// The value of - /// The initial value of and - public DreamDaemon(IEventConsumer eventConsumer, IInterop interop, IWatchdog watchdog, DreamDaemonSettings initialSettings) - { - this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); - this.interop = interop ?? throw new ArgumentNullException(nameof(interop)); - this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog)); - currentLaunchParameters = initialSettings ?? throw new ArgumentNullException(nameof(initialSettings)); - - autoStart = initialSettings.AutoStart.Value; - - semaphore = new SemaphoreSlim(1); - } - - /// - public void Dispose() => semaphore.Dispose(); - - /// - public async Task CancelGracefulActions(CancellationToken cancellationToken) - { - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - SoftRebooting = false; - SoftStopping = false; - } - finally - { - semaphore.Release(); - } - } - - /// - public async Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken) - { - if (launchParameters == null) - throw new ArgumentNullException(nameof(launchParameters)); - Task launchTask; - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - currentLaunchParameters = launchParameters; - if (!Running) - launchTask = Launch(launchParameters, cancellationToken); - else - launchTask = Restart(true, cancellationToken); - } - finally - { - semaphore.Release(); - } - await launchTask.ConfigureAwait(false); - } - - /// - public async Task Launch(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken) - { - if (launchParameters == null) - throw new ArgumentNullException(nameof(launchParameters)); - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - Task watchdogStartup; - try - { - if (Running) - throw new InvalidOperationException("DreamDaemon already running!"); - Running = true; - await eventConsumer.HandleEvent(EventType.DDLaunched, null, cancellationToken).ConfigureAwait(false); - watchdogStartup = watchdog.Start(this, cancellationToken); - } - finally - { - semaphore.Release(); - } - //important to leave the lock so the watchdog can enter it - await watchdogStartup.ConfigureAwait(false); - } - - /// - public async Task Restart(bool graceful, CancellationToken cancellationToken) - { - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - if (graceful) - { - if (!Running) - throw new InvalidOperationException("DreamDaemon not running!"); - if (SoftStopping) - throw new InvalidOperationException("DreamDaemon has a graceful stop queued!"); - SoftRebooting = true; - return; - } - await eventConsumer.HandleEvent(EventType.DDRestart, null, cancellationToken).ConfigureAwait(false); - if (Running) - await watchdog.Stop().ConfigureAwait(false); - await Launch(currentLaunchParameters, cancellationToken).ConfigureAwait(false); - } - finally - { - semaphore.Release(); - } - } - - /// - public Task StartAsync(CancellationToken cancellationToken) => autoStart ? Launch(currentLaunchParameters, cancellationToken) : Task.CompletedTask; - - /// - public Task StopAsync(CancellationToken cancellationToken) => Terminate(false, cancellationToken); - - /// - public async Task Terminate(bool graceful, CancellationToken cancellationToken) - { - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - if (graceful) - { - if (!Running) - throw new InvalidOperationException("DreamDaemon not running!"); - SoftRebooting = false; - SoftStopping = true; - return; - } - await eventConsumer.HandleEvent(EventType.DDTerminated, null, cancellationToken).ConfigureAwait(false); - await watchdog.Stop().ConfigureAwait(false); - } - finally - { - semaphore.Release(); - } - } - - /// - public async Task GetLaunchParameters(CancellationToken cancellationToken) - { - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - LastLaunchParameters = currentLaunchParameters; - return currentLaunchParameters; - } - finally - { - semaphore.Release(); - } - } - } -} diff --git a/src/Tgstation.Server.Host/Components/IDreamDaemon.cs b/src/Tgstation.Server.Host/Components/IDreamDaemon.cs deleted file mode 100644 index e435a3af69..0000000000 --- a/src/Tgstation.Server.Host/Components/IDreamDaemon.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Microsoft.Extensions.Hosting; -using System.Threading; -using System.Threading.Tasks; -using Tgstation.Server.Api.Models; -using Tgstation.Server.Api.Models.Internal; - -namespace Tgstation.Server.Host.Components -{ - /// - /// For managing DreamDaemon - /// - public interface IDreamDaemon : IHostedService - { - /// - /// If DreamDaemon is running - /// - bool Running { get; } - - /// - /// If DreamDaemon is going to soft reboot - /// - bool SoftRebooting { get; } - - /// - /// If DreamDaemon is going to soft stop - /// - bool SoftStopping { get; } - - /// - /// The port DreamDaemon is currently running on - /// - ushort? CurrentPort { get; } - - /// - /// The current of - /// - DreamDaemonSecurity? CurrentSecurity { get; } - - /// - /// The access token used for communication with the DMAPI - /// - string AccessToken { get; } - - /// - /// The most recently used - /// - DreamDaemonLaunchParameters LastLaunchParameters { get; } - - /// - /// The most recently used - /// - Host.Models.CompileJob LastCompileJob { get; } - - /// - /// Launch DreamDaemon - /// - /// The for the launch - /// The for the operation - /// A representing the running operation - Task Launch(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken); - - /// - /// Changes the if currently . Triggers a graceful restart - /// - /// The new - /// The for the operation - /// A representing the running operation - Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken); - - /// - /// Restarts DreamDaemon - /// - /// If the restart will be delayed until a reboot is detected in the DMAPI and this function will retrun immediately. If the DMAPI isn't installed, this parameter is ignored - /// The for the operation - /// A representing the running operation - Task Restart(bool graceful, CancellationToken cancellationToken); - - /// - /// Terminates DreamDaemon - /// - /// If the termination will be delayed until a reboot is detected in the DMAPI and this function will return immediately. If the DMAPI isn't installed, this parameter is ignored - /// The for the operation - /// A representing the running operation - Task Terminate(bool graceful, CancellationToken cancellationToken); - - /// - /// Cancels any pending graceful reboots or terminations - /// - /// The for the operation - /// A representing the running operation - Task CancelGracefulActions(CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/IInstance.cs b/src/Tgstation.Server.Host/Components/IInstance.cs index a06c30c3a3..faa3c99de7 100644 --- a/src/Tgstation.Server.Host/Components/IInstance.cs +++ b/src/Tgstation.Server.Host/Components/IInstance.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Hosting; using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Watchdog; namespace Tgstation.Server.Host.Components { @@ -24,9 +25,9 @@ namespace Tgstation.Server.Host.Components IDreamMaker DreamMaker { get; } /// - /// The for the + /// The for the /// - IDreamDaemon DreamDaemon { get; } + IWatchdog Watchdog { get; } /// /// The for the diff --git a/src/Tgstation.Server.Host/Components/ILaunchParametersFactory.cs b/src/Tgstation.Server.Host/Components/ILaunchParametersFactory.cs deleted file mode 100644 index ee2a67ec21..0000000000 --- a/src/Tgstation.Server.Host/Components/ILaunchParametersFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Tgstation.Server.Api.Models.Internal; - -namespace Tgstation.Server.Host.Components -{ - /// - /// For retrieving current - /// - interface ILaunchParametersFactory - { - /// - /// Get the latest - /// - /// The for the operation - /// A resulting in the latest - Task GetLaunchParameters(CancellationToken cancellationToken); - } -} diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index aea3891519..04554e81ec 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Watchdog; using Tgstation.Server.Host.Core; namespace Tgstation.Server.Host.Components @@ -20,7 +21,7 @@ namespace Tgstation.Server.Host.Components public IDreamMaker DreamMaker { get; } /// - public IDreamDaemon DreamDaemon { get; } + public IWatchdog Watchdog { get; } /// public IChat Chat { get; } @@ -52,13 +53,13 @@ namespace Tgstation.Server.Host.Components /// CancellationTokenSource timerCts; - public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IDreamDaemon dreamDaemon, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory) + public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IWatchdog watchdog, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory) { this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager)); Byond = byond ?? throw new ArgumentNullException(nameof(byond)); DreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)); - DreamDaemon = dreamDaemon ?? throw new ArgumentNullException(nameof(dreamDaemon)); + watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog)); Chat = chat ?? throw new ArgumentNullException(nameof(chat)); Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer)); @@ -83,19 +84,22 @@ namespace Tgstation.Server.Host.Components await Task.Delay(new TimeSpan(0, minutes, 0), cancellationToken).ConfigureAwait(false); string accessToken = null, projectName = null; + int timeout = 0; var dbTask = databaseContextFactory.UseContext(async (db) => { var instanceQuery = db.Instances.Where(x => x.Id == metadata.Id); + var timeoutTask = instanceQuery.Select(x => x.DreamDaemonSettings.StartupTimeout).FirstAsync(cancellationToken); var projectNameTask = instanceQuery.Select(x => x.DreamMakerSettings.ProjectName).FirstOrDefaultAsync(cancellationToken); accessToken = await instanceQuery.Select(x => x.RepositorySettings.AccessToken).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); projectName = await projectNameTask.ConfigureAwait(false); + timeout = (await timeoutTask.ConfigureAwait(false)).Value; }); using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false)) { await dbTask.ConfigureAwait(false); await repo.FetchOrigin(accessToken, cancellationToken).ConfigureAwait(false); await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false); - await DreamMaker.Compile(projectName, repo, cancellationToken).ConfigureAwait(false); + await DreamMaker.Compile(projectName, timeout, repo, cancellationToken).ConfigureAwait(false); } } } @@ -114,10 +118,10 @@ namespace Tgstation.Server.Host.Components } /// - public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval), DreamDaemon.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken)); + public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval), Watchdog.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken)); /// - public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(null), DreamDaemon.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken)); + public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(null), Watchdog.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken)); /// public async Task SetAutoUpdateInterval(int? newInterval) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs index 18e6ef8dda..b7ee4ce2fd 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs @@ -1,14 +1,82 @@ -using System; +using Microsoft.Extensions.Hosting; +using System; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models.Internal; namespace Tgstation.Server.Host.Components.Watchdog { - interface IWatchdog : IDisposable + public interface IWatchdog : IHostedService, IDisposable { - DreamDaemonLaunchParameters LaunchParameters { get; set; } + /// + /// If the watchdog is running + /// + bool Running { get; } + /// + /// If the alpha server is the active server + /// + bool AlphaIsActive { get; } + + /// + /// The latest of the twin servers + /// + LaunchResult LastLaunchResult { get; } + + /// + /// The that is currently live + /// + Models.CompileJob LiveCompileJob { get; } + + /// + /// The that is staged to go live + /// + Models.CompileJob StagedCompileJob { get; } + + /// + /// The the active server is using + /// + DreamDaemonLaunchParameters ActiveLaunchParameters { get; } + + /// + /// The to be applied + /// + DreamDaemonLaunchParameters LastLaunchParameters { get; } + + /// + /// The of the active server + /// + RebootState? RebootState { get; } + + /// + /// Start the + /// + /// The for the operation + /// A resulting in the or if it was already running Task Launch(CancellationToken cancellationToken); + + /// + /// Changes the . If currently triggers a graceful restart + /// + /// The new + /// The for the operation + /// A representing the running operation + Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken); + + /// + /// Restarts the watchdog + /// + /// If the restart will be delayed until a reboot is detected in the active server's DMAPI and this function will retrun immediately + /// The for the operation + /// A resulting in the or if it was already running or is and is + Task Restart(bool graceful, CancellationToken cancellationToken); + + /// + /// Stops the watchdog + /// + /// If the termination will be delayed until a reboot is detected in the active server's DMAPI and this function will return immediately + /// The for the operation + /// A representing the running operation + Task Terminate(bool graceful, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/LaunchResult.cs b/src/Tgstation.Server.Host/Components/Watchdog/LaunchResult.cs index 8f8ae755ab..016d9f57d0 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/LaunchResult.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/LaunchResult.cs @@ -5,7 +5,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// Represents the result of trying to start a DD process /// - sealed class LaunchResult + public sealed class LaunchResult { /// /// The time it took for to return diff --git a/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs b/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs index b1fc80317a..6890df7bab 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs @@ -3,7 +3,7 @@ /// /// Represents the action to take when /world/Reboot() is called /// - enum RebootState : int + public enum RebootState : int { /// /// Run DreamDaemon's normal reboot process diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 4152b8bfb4..856aa8a941 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -77,8 +77,5 @@ namespace Tgstation.Server.Host.Components.Watchdog disposed = true; } } - - /// - public Task Launch(CancellationToken cancellationToken) => throw new NotImplementedException(); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs index 3eb27b555a..0532326e78 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs @@ -5,27 +5,16 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// Launch results for a /// - sealed class WatchdogLaunchResult + public sealed class WatchdogLaunchResult { - /// - /// If the watchdog is running - /// - bool Running { - get { - if (Alpha == null || Bravo == null) - throw new InvalidOperationException("Alpha or Bravo is null!"); - return !Alpha.ExitCode.HasValue && !Bravo.ExitCode.HasValue; - } - } - /// /// The for the alpha process /// - LaunchResult Alpha { get; set; } + public LaunchResult Alpha { get; set; } /// /// The for the bravo process /// - LaunchResult Bravo { get; set; } + public LaunchResult Bravo { get; set; } } } diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index a4570e2ff7..356e52fb9d 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -8,9 +8,9 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; -using Tgstation.Server.Api.Models.Internal; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Components.Watchdog; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; @@ -51,17 +51,9 @@ namespace Tgstation.Server.Host.Controllers { var instance = instanceManager.GetInstance(Instance); - if (instance.DreamDaemon.Running) + if (instance.Watchdog.Running) return StatusCode(HttpStatusCode.Gone); - var launchParams = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => new DreamDaemonLaunchParameters - { - AllowWebClient = x.DreamDaemonSettings.AllowWebClient, - PrimaryPort = x.DreamDaemonSettings.PrimaryPort, - SecondaryPort = x.DreamDaemonSettings.SecondaryPort, - SecurityLevel = x.DreamDaemonSettings.SecurityLevel - }).FirstAsync(cancellationToken).ConfigureAwait(false); - await jobManager.RegisterOperation(new Models.Job { Description = "Launch DreamDaemon", @@ -69,7 +61,16 @@ namespace Tgstation.Server.Host.Controllers CancelRightsType = RightsType.DreamDaemon, Instance = Instance, StartedBy = AuthenticationContext.User - }, (job, serviceProvider, innerCt) => instance.DreamDaemon.Launch(launchParams, innerCt), cancellationToken).ConfigureAwait(false); + }, + async (job, serviceProvider, innerCt) => + { + var result = await instance.Watchdog.Launch(innerCt).ConfigureAwait(false); + if (result == null) + throw new InvalidOperationException("Watchdog already running!"); + if (!instance.Watchdog.Running) + throw new Exception("Failed to launch watchdog!"); + }, + cancellationToken).ConfigureAwait(false); return Ok(); } @@ -77,48 +78,55 @@ namespace Tgstation.Server.Host.Controllers [TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)] public override async Task Read(CancellationToken cancellationToken) { - var dd = instanceManager.GetInstance(Instance).DreamDaemon; + var dd = instanceManager.GetInstance(Instance).Watchdog; var metadata = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (int)DreamDaemonRights.ReadMetadata) != 0; var revision = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (int)DreamDaemonRights.ReadRevision) != 0; var settings = metadata ? await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstAsync(cancellationToken).ConfigureAwait(false) : null; - Api.Models.DreamDaemon result = new Api.Models.DreamDaemon(); + var result = new DreamDaemon(); if(metadata) { + var alphaActive = dd.AlphaIsActive; + var llp = dd.LastLaunchParameters; result.AutoStart = settings.AutoStart; - result.CurrentPort = dd.CurrentPort; - result.CurrentSecurity = dd.CurrentSecurity; - result.PrimaryPort = dd.LastLaunchParameters.PrimaryPort; - result.AllowWebClient = dd.LastLaunchParameters.AllowWebClient; + result.CurrentPort = alphaActive ? llp.PrimaryPort : llp.SecondaryPort; + result.CurrentSecurity = llp.SecurityLevel; + result.CurrentAllowWebclient = llp.AllowWebClient; + result.PrimaryPort = dd.ActiveLaunchParameters.PrimaryPort; + result.AllowWebClient = dd.ActiveLaunchParameters.AllowWebClient; result.Running = dd.Running; result.SecondaryPort = dd.LastLaunchParameters.SecondaryPort; result.SecurityLevel = dd.LastLaunchParameters.SecurityLevel; - result.SoftRestart = dd.SoftRebooting; - result.SoftShutdown = dd.SoftStopping; + var rstate = dd.RebootState; + result.SoftRestart = rstate == RebootState.Restart; + result.SoftShutdown = rstate == RebootState.Shutdown; }; if (revision) - result.CompileJob = dd.LastCompileJob.ToApi(); + { + result.ActiveCompileJob = dd.LiveCompileJob?.ToApi(); + result.StagedCompileJob = dd.StagedCompileJob?.ToApi(); + } return Json(result); } /// [TgsAuthorize(DreamDaemonRights.Shutdown)] - public override async Task Delete([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken) + public override async Task Delete([FromBody] DreamDaemon model, CancellationToken cancellationToken) { var instance = instanceManager.GetInstance(Instance); - if (!instance.DreamDaemon.Running) + if (!instance.Watchdog.Running) return StatusCode(HttpStatusCode.Gone); - await instance.DreamDaemon.Terminate(false, cancellationToken).ConfigureAwait(false); + await instance.Watchdog.Terminate(false, cancellationToken).ConfigureAwait(false); return Ok(); } /// [TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown | DreamDaemonRights.Start)] - public override async Task Update([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken) + public override async Task Update([FromBody] DreamDaemon model, CancellationToken cancellationToken) { var current = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstAsync(cancellationToken).ConfigureAwait(false); @@ -155,7 +163,7 @@ namespace Tgstation.Server.Host.Controllers current.SoftShutdown = model.SoftShutdown; } - await instanceManager.GetInstance(Instance).DreamDaemon.ChangeSettings(current, cancellationToken).ConfigureAwait(false); + await instanceManager.GetInstance(Instance).Watchdog.ChangeSettings(current, cancellationToken).ConfigureAwait(false); await DatabaseContext.Save(default).ConfigureAwait(false); return Ok();