All that's left is to write the watchdog

This commit is contained in:
Cyberboss
2018-07-05 11:50:36 -04:00
parent 916139f1a6
commit bdaa8b9a60
12 changed files with 136 additions and 385 deletions
+14 -2
View File
@@ -9,10 +9,16 @@ namespace Tgstation.Server.Api.Models
public sealed class DreamDaemon : DreamDaemonSettings
{
/// <summary>
/// When the live revision was compiled
/// The live revision
/// </summary>
[Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadRevision)]
public CompileJob CompileJob { get; set; }
public CompileJob ActiveCompileJob { get; set; }
/// <summary>
/// The next revision to go live
/// </summary>
[Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadRevision)]
public CompileJob StagedCompileJob { get; set; }
/// <summary>
/// The current status of <see cref="DreamDaemon"/>
@@ -31,5 +37,11 @@ namespace Tgstation.Server.Api.Models
/// </summary>
[Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadMetadata)]
public ushort? CurrentPort { get; set; }
/// <summary>
/// The webclient status the running <see cref="DreamDaemon"/> instance is set to
/// </summary>
[Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadMetadata)]
public bool? CurrentAllowWebclient { get; set; }
}
}
@@ -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
{
/// <inheritdoc />
sealed class DreamDaemon : IDreamDaemon, ILaunchParametersFactory, IDisposable
{
/// <inheritdoc />
public bool Running { get; private set; }
/// <inheritdoc />
public ushort? CurrentPort { get; private set; }
/// <inheritdoc />
public string AccessToken { get; private set; }
/// <inheritdoc />
public DreamDaemonSecurity? CurrentSecurity { get; private set; }
/// <inheritdoc />
public bool SoftRebooting { get; private set; }
/// <inheritdoc />
public bool SoftStopping { get; private set; }
/// <inheritdoc />
public DreamDaemonLaunchParameters LastLaunchParameters { get; private set; }
/// <inheritdoc />
public Host.Models.CompileJob LastCompileJob => watchdog.CurrentCompileJob;
/// <summary>
/// The <see cref="IEventConsumer"/> for <see cref="DreamDaemon"/>
/// </summary>
readonly IEventConsumer eventConsumer;
/// <summary>
/// The <see cref="IInterop"/> for <see cref="DreamDaemon"/>
/// </summary>
readonly IInterop interop;
/// <summary>
/// The <see cref="IWatchdog"/> for <see cref="DreamDaemon"/>
/// </summary>
readonly IWatchdog watchdog;
/// <summary>
/// Used for write control to class variables
/// </summary>
readonly SemaphoreSlim semaphore;
/// <summary>
/// If <see cref="StartAsync(CancellationToken)"/> should start DD
/// </summary>
readonly bool autoStart;
/// <summary>
/// The current <see cref="DreamDaemonLaunchParameters"/>
/// </summary>
DreamDaemonLaunchParameters currentLaunchParameters;
/// <summary>
/// Construct <see cref="DreamDaemon"/>
/// </summary>
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
/// <param name="interop">The value of <see cref="interop"/></param>
/// <param name="watchdog">The value of <see cref="watchdog"/></param>
/// <param name="initialSettings">The initial value of <see cref="currentLaunchParameters"/> and <see cref="autoStart"/></param>
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);
}
/// <inheritdoc />
public void Dispose() => semaphore.Dispose();
/// <inheritdoc />
public async Task CancelGracefulActions(CancellationToken cancellationToken)
{
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
SoftRebooting = false;
SoftStopping = false;
}
finally
{
semaphore.Release();
}
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
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();
}
}
/// <inheritdoc />
public Task StartAsync(CancellationToken cancellationToken) => autoStart ? Launch(currentLaunchParameters, cancellationToken) : Task.CompletedTask;
/// <inheritdoc />
public Task StopAsync(CancellationToken cancellationToken) => Terminate(false, cancellationToken);
/// <inheritdoc />
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();
}
}
/// <inheritdoc />
public async Task<DreamDaemonLaunchParameters> GetLaunchParameters(CancellationToken cancellationToken)
{
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
LastLaunchParameters = currentLaunchParameters;
return currentLaunchParameters;
}
finally
{
semaphore.Release();
}
}
}
}
@@ -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
{
/// <summary>
/// For managing DreamDaemon
/// </summary>
public interface IDreamDaemon : IHostedService
{
/// <summary>
/// If DreamDaemon is running
/// </summary>
bool Running { get; }
/// <summary>
/// If DreamDaemon is going to soft reboot
/// </summary>
bool SoftRebooting { get; }
/// <summary>
/// If DreamDaemon is going to soft stop
/// </summary>
bool SoftStopping { get; }
/// <summary>
/// The port DreamDaemon is currently running on
/// </summary>
ushort? CurrentPort { get; }
/// <summary>
/// The current <see cref="DreamDaemonSecurity"/> of <see cref="DreamDaemon"/>
/// </summary>
DreamDaemonSecurity? CurrentSecurity { get; }
/// <summary>
/// The access token used for communication with the DMAPI
/// </summary>
string AccessToken { get; }
/// <summary>
/// The <see cref="DreamDaemonLaunchParameters"/> most recently used
/// </summary>
DreamDaemonLaunchParameters LastLaunchParameters { get; }
/// <summary>
/// The <see cref="Host.Models.CompileJob"/> most recently used
/// </summary>
Host.Models.CompileJob LastCompileJob { get; }
/// <summary>
/// Launch DreamDaemon
/// </summary>
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> for the launch</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task Launch(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken);
/// <summary>
/// Changes the <see cref="DreamDaemonLaunchParameters"/> if currently <see cref="Running"/>. Triggers a graceful restart
/// </summary>
/// <param name="launchParameters">The new <see cref="DreamDaemonLaunchParameters"/></param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken);
/// <summary>
/// Restarts DreamDaemon
/// </summary>
/// <param name="graceful">If <see langword="true"/> 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</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task Restart(bool graceful, CancellationToken cancellationToken);
/// <summary>
/// Terminates DreamDaemon
/// </summary>
/// <param name="graceful">If <see langword="true"/> 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</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task Terminate(bool graceful, CancellationToken cancellationToken);
/// <summary>
/// Cancels any pending graceful reboots or terminations
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task CancelGracefulActions(CancellationToken cancellationToken);
}
}
@@ -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; }
/// <summary>
/// The <see cref="IDreamDaemon"/> for the <see cref="IInstance"/>
/// The <see cref="IWatchdog"/> for the <see cref="IInstance"/>
/// </summary>
IDreamDaemon DreamDaemon { get; }
IWatchdog Watchdog { get; }
/// <summary>
/// The <see cref="IChat"/> for the <see cref="IInstance"/>
@@ -1,19 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
namespace Tgstation.Server.Host.Components
{
/// <summary>
/// For retrieving current <see cref="DreamDaemonLaunchParameters"/>
/// </summary>
interface ILaunchParametersFactory
{
/// <summary>
/// Get the latest <see cref="DreamDaemonLaunchParameters"/>
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the latest <see cref="DreamDaemonLaunchParameters"/></returns>
Task<DreamDaemonLaunchParameters> GetLaunchParameters(CancellationToken cancellationToken);
}
}
@@ -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; }
/// <inheritdoc />
public IDreamDaemon DreamDaemon { get; }
public IWatchdog Watchdog { get; }
/// <inheritdoc />
public IChat Chat { get; }
@@ -52,13 +53,13 @@ namespace Tgstation.Server.Host.Components
/// </summary>
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
}
/// <inheritdoc />
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));
/// <inheritdoc />
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));
/// <inheritdoc />
public async Task SetAutoUpdateInterval(int? newInterval)
@@ -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; }
/// <summary>
/// If the watchdog is running
/// </summary>
bool Running { get; }
/// <summary>
/// If the alpha server is the active server
/// </summary>
bool AlphaIsActive { get; }
/// <summary>
/// The latest <see cref="LaunchResult"/> of the twin servers
/// </summary>
LaunchResult LastLaunchResult { get; }
/// <summary>
/// The <see cref="Models.CompileJob"/> that is currently live
/// </summary>
Models.CompileJob LiveCompileJob { get; }
/// <summary>
/// The <see cref="Models.CompileJob"/> that is staged to go live
/// </summary>
Models.CompileJob StagedCompileJob { get; }
/// <summary>
/// The <see cref="DreamDaemonLaunchParameters"/> the active server is using
/// </summary>
DreamDaemonLaunchParameters ActiveLaunchParameters { get; }
/// <summary>
/// The <see cref="DreamDaemonLaunchParameters"/> to be applied
/// </summary>
DreamDaemonLaunchParameters LastLaunchParameters { get; }
/// <summary>
/// The <see cref="Components.Watchdog.RebootState"/> of the active server
/// </summary>
RebootState? RebootState { get; }
/// <summary>
/// Start the <see cref="IWatchdog"/>
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="WatchdogLaunchResult"/> or <see langword="null"/> if it was already running</returns>
Task<WatchdogLaunchResult> Launch(CancellationToken cancellationToken);
/// <summary>
/// Changes the <see cref="ActiveLaunchParameters"/>. If currently <see cref="Running"/> triggers a graceful restart
/// </summary>
/// <param name="launchParameters">The new <see cref="DreamDaemonLaunchParameters"/></param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken);
/// <summary>
/// Restarts the watchdog
/// </summary>
/// <param name="graceful">If <see langword="true"/> the restart will be delayed until a reboot is detected in the active server's DMAPI and this function will retrun immediately</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="WatchdogLaunchResult"/> or <see langword="null"/> if it was already running or <paramref name="graceful"/> is <see langword="true"/> and <see cref="Running"/> is <see langword="false"/></returns>
Task<WatchdogLaunchResult> Restart(bool graceful, CancellationToken cancellationToken);
/// <summary>
/// Stops the watchdog
/// </summary>
/// <param name="graceful">If <see langword="true"/> the termination will be delayed until a reboot is detected in the active server's DMAPI and this function will return immediately</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task Terminate(bool graceful, CancellationToken cancellationToken);
}
}
@@ -5,7 +5,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <summary>
/// Represents the result of trying to start a DD process
/// </summary>
sealed class LaunchResult
public sealed class LaunchResult
{
/// <summary>
/// The time it took for <see cref="System.Diagnostics.Process.WaitForInputIdle"/> to return
@@ -3,7 +3,7 @@
/// <summary>
/// Represents the action to take when /world/Reboot() is called
/// </summary>
enum RebootState : int
public enum RebootState : int
{
/// <summary>
/// Run DreamDaemon's normal reboot process
@@ -77,8 +77,5 @@ namespace Tgstation.Server.Host.Components.Watchdog
disposed = true;
}
}
/// <inheritdoc />
public Task<WatchdogLaunchResult> Launch(CancellationToken cancellationToken) => throw new NotImplementedException();
}
}
@@ -5,27 +5,16 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <summary>
/// Launch results for a <see cref="IWatchdog"/>
/// </summary>
sealed class WatchdogLaunchResult
public sealed class WatchdogLaunchResult
{
/// <summary>
/// If the watchdog is running
/// </summary>
bool Running {
get {
if (Alpha == null || Bravo == null)
throw new InvalidOperationException("Alpha or Bravo is null!");
return !Alpha.ExitCode.HasValue && !Bravo.ExitCode.HasValue;
}
}
/// <summary>
/// The <see cref="LaunchResult"/> for the alpha process
/// </summary>
LaunchResult Alpha { get; set; }
public LaunchResult Alpha { get; set; }
/// <summary>
/// The <see cref="LaunchResult"/> for the bravo process
/// </summary>
LaunchResult Bravo { get; set; }
public LaunchResult Bravo { get; set; }
}
}
@@ -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<IActionResult> 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);
}
/// <inheritdoc />
[TgsAuthorize(DreamDaemonRights.Shutdown)]
public override async Task<IActionResult> Delete([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken)
public override async Task<IActionResult> 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();
}
/// <inheritdoc />
[TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown | DreamDaemonRights.Start)]
public override async Task<IActionResult> Update([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken)
public override async Task<IActionResult> 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();