mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-23 13:07:07 +01:00
Hey it actually compiles now
This commit is contained in:
@@ -13,6 +13,16 @@ namespace Tgstation.Server.Host.Components
|
||||
/// </summary>
|
||||
sealed class DmbFactory : IDmbFactory, ICompileJobConsumer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task OnNewerDmb
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
return newerDmbTcs.Task;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContextFactory"/> for the <see cref="DmbFactory"/>
|
||||
/// </summary>
|
||||
@@ -109,13 +119,6 @@ namespace Tgstation.Server.Host.Components
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task OnNewerDmb()
|
||||
{
|
||||
lock (this)
|
||||
return newerDmbTcs.Task;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) =>
|
||||
{
|
||||
|
||||
@@ -36,6 +36,10 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <inheritdoc />
|
||||
public CompilerStatus Status { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IByond"/> for <see cref="DreamMaker"/>
|
||||
/// </summary>
|
||||
readonly IByond byond;
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for <see cref="DreamMaker"/>
|
||||
/// </summary>
|
||||
@@ -49,10 +53,6 @@ namespace Tgstation.Server.Host.Components
|
||||
/// </summary>
|
||||
readonly ISessionControllerFactory sessionControllerFactory;
|
||||
/// <summary>
|
||||
/// The <see cref="IByond"/> for <see cref="DreamMaker"/>
|
||||
/// </summary>
|
||||
readonly IByond byond;
|
||||
/// <summary>
|
||||
/// The <see cref="ICompileJobConsumer"/> for <see cref="DreamMaker"/>
|
||||
/// </summary>
|
||||
readonly ICompileJobConsumer compileJobConsumer;
|
||||
@@ -64,16 +64,17 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <summary>
|
||||
/// Construct <see cref="DreamMaker"/>
|
||||
/// </summary>
|
||||
/// <param name="byond">The value of <see cref="byond"/></param>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/></param>
|
||||
/// <param name="configuration">The value of <see cref="configuration"/></param>
|
||||
/// <param name="dreamDaemonExecutor">The value of <see cref="dreamDaemonExecutor"/></param>
|
||||
/// <param name="byond">The value of <see cref="byond"/></param>
|
||||
/// <param name="interop">The value of <see cref="interop"/></param>
|
||||
/// <param name="compileJobConsumer">The value of <see cref="compileJobConsumer"/></param>
|
||||
/// <param name="application">The value of <see cref="application"/></param>
|
||||
///
|
||||
public DreamMaker(IIOManager ioManager, IConfiguration configuration, ISessionControllerFactory sessionControllerFactory, ICompileJobConsumer compileJobConsumer, IApplication application)
|
||||
public DreamMaker(IByond byond, IIOManager ioManager, IConfiguration configuration, ISessionControllerFactory sessionControllerFactory, ICompileJobConsumer compileJobConsumer, IApplication application)
|
||||
{
|
||||
this.byond = byond;
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
|
||||
|
||||
@@ -7,18 +7,18 @@ namespace Tgstation.Server.Host.Components
|
||||
/// Factory for <see cref="IDmbProvider"/>s
|
||||
/// </summary>
|
||||
interface IDmbFactory
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a <see cref="Task"/> that completes when the result of a call to <see cref="LockNextDmb(CancellationToken)"/> will be different than the previous call if any
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task OnNewerDmb { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next <see cref="IDmbProvider"/>
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task<IDmbProvider> LockNextDmb(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get a <see cref="Task"/> that completes when the result of a call to <see cref="LockNextDmb(CancellationToken)"/> will be different than the previous call if any
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task OnNewerDmb();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace Tgstation.Server.Host.Components
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
instances = new Dictionary<long, IInstance>();
|
||||
interopConsumers = new Dictionary<string, IInteropConsumer>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Watchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles saving and loading <see cref="WatchdogReattachInformation"/>
|
||||
/// </summary>
|
||||
interface IReattachInfoHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Save some <paramref name="reattachInformation"/>
|
||||
/// </summary>
|
||||
/// <param name="reattachInformation">The <see cref="WatchdogReattachInformation"/> to save</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task Save(WatchdogReattachInformation reattachInformation, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Load a saved <see cref="WatchdogReattachInformation"/>
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the stored <see cref="WatchdogReattachInformation"/> if any</returns>
|
||||
Task<WatchdogReattachInformation> Load(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Watchdog
|
||||
{
|
||||
@@ -21,5 +22,8 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// The peak virtual memory usage in bytes
|
||||
/// </summary>
|
||||
public long PeakMemory { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Exit Code: {0}, RAM: {1}, Time {2}ms", ExitCode, PeakMemory, StartupTime.TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Tgstation.Server.Host.Components.Watchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// The action for the monitor loop to take when control is returned to it
|
||||
/// </summary>
|
||||
enum MonitorAction
|
||||
{
|
||||
Continue,
|
||||
Break,
|
||||
Restart,
|
||||
Exit
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
public bool RebootingInactiveServer { get; set; }
|
||||
public bool InactiveServerHasStagedDmb { get; set; }
|
||||
|
||||
public MonitorAction NextAction { get; set; }
|
||||
|
||||
public ISessionController ActiveServer { get; set; }
|
||||
public ISessionController InactiveServer { get; set; }
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ClosePortsOnReboot { get; set; }
|
||||
public bool ClosePortOnReboot { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ApiValidated
|
||||
@@ -104,6 +104,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// <inheritdoc />
|
||||
public Task<int> Lifetime => session.Lifetime;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task OnReboot => rebootTcs.Task;
|
||||
|
||||
/// <summary>
|
||||
/// The up to date <see cref="ReattachInformation"/>
|
||||
/// </summary>
|
||||
@@ -142,6 +145,11 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// The port to assign DreamDaemon when it queries for it
|
||||
/// </summary>
|
||||
ushort nextPort;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="TaskCompletionSource{TResult}"/> that completes when DD tells us about a reboot
|
||||
/// </summary>
|
||||
TaskCompletionSource<object> rebootTcs;
|
||||
|
||||
/// <summary>
|
||||
/// If we know DreamDaemon currently has it's port closed
|
||||
@@ -181,6 +189,8 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
portClosed = false;
|
||||
disposed = false;
|
||||
apiValidated = false;
|
||||
|
||||
rebootTcs = new TaskCompletionSource<object>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -229,13 +239,16 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
apiValidated = true;
|
||||
break;
|
||||
case DMCommandWorldReboot:
|
||||
if (ClosePortsOnReboot)
|
||||
if (ClosePortOnReboot)
|
||||
{
|
||||
content = new Dictionary<string, int> { { DMParameterNewPort, 0 } };
|
||||
portClosed = true;
|
||||
}
|
||||
else
|
||||
ClosePortsOnReboot = true;
|
||||
ClosePortOnReboot = true;
|
||||
var oldTcs = rebootTcs;
|
||||
rebootTcs = new TaskCompletionSource<object>();
|
||||
oldTcs.SetResult(null);
|
||||
break;
|
||||
default:
|
||||
status = HttpStatusCode.BadRequest;
|
||||
|
||||
@@ -60,6 +60,31 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// </summary>
|
||||
readonly IChat chat;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a <see cref="SessionControllerFactory"/>
|
||||
/// </summary>
|
||||
/// <param name="executor">The value of <see cref="executor"/></param>
|
||||
/// <param name="byond">The value of <see cref="byond"/></param>
|
||||
/// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param>
|
||||
/// <param name="interopRegistrar">The value of <see cref="interopRegistrar"/></param>
|
||||
/// <param name="cryptographySuite">The value of <see cref="cryptographySuite"/></param>
|
||||
/// <param name="application">The value of <see cref="application"/></param>
|
||||
/// <param name="instance">The value of <see cref="instance"/></param>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/></param>
|
||||
/// <param name="chat">The value of <see cref="chat"/></param>
|
||||
public SessionControllerFactory(IExecutor executor, IByond byond, IByondTopicSender byondTopicSender, IInteropRegistrar interopRegistrar, ICryptographySuite cryptographySuite, IApplication application, IInstance instance, IIOManager ioManager, IChat chat)
|
||||
{
|
||||
this.executor = executor ?? throw new ArgumentNullException(nameof(executor));
|
||||
this.byond = byond ?? throw new ArgumentNullException(nameof(byond));
|
||||
this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
|
||||
this.interopRegistrar = interopRegistrar ?? throw new ArgumentNullException(nameof(interopRegistrar));
|
||||
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
|
||||
this.application = application ?? throw new ArgumentNullException(nameof(application));
|
||||
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ISessionController> LaunchNew(DreamDaemonLaunchParameters launchParameters, IDmbProvider dmbProvider, bool primaryPort, bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -139,15 +164,46 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
}
|
||||
catch
|
||||
{
|
||||
chatJsonTrackingTask.Dispose();
|
||||
chatJsonTrackingContext.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ISessionController> Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken)
|
||||
public async Task<ISessionController> Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (reattachInformation == null)
|
||||
throw new ArgumentNullException(nameof(reattachInformation));
|
||||
|
||||
var basePath = reattachInformation.IsPrimary ? reattachInformation.Dmb.PrimaryDirectory : reattachInformation.Dmb.SecondaryDirectory;
|
||||
var chatJsonTrackingContext = await chat.TrackJsons(basePath, reattachInformation.ChatChannelsJson, reattachInformation.ChatCommandsJson, cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var byondLock = byond.UseExecutables(reattachInformation.Dmb.CompileJob.ByondVersion);
|
||||
try
|
||||
{
|
||||
var session = executor.AttachToDreamDaemon(reattachInformation.ProcessId, byondLock);
|
||||
try
|
||||
{
|
||||
return new SessionController(reattachInformation, session, byondTopicSender, interopRegistrar, chatJsonTrackingContext, chat);
|
||||
}
|
||||
catch
|
||||
{
|
||||
session.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
byondLock.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
chatJsonTrackingContext.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Watchdog
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class Watchdog : IWatchdog
|
||||
{
|
||||
/// <summary>
|
||||
/// The time in milliseconds to wait from starting <see cref="alphaServer"/> to start <see cref="bravoServer"/>. Does not take responsiveness into account
|
||||
/// </summary>
|
||||
const int AlphaBravoStartupSeperationInterval = 3000;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Running { get; private set; }
|
||||
|
||||
@@ -33,7 +39,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
public DreamDaemonLaunchParameters LastLaunchParameters { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public RebootState? RebootState => Running ? (RebootState?)(AlphaIsActive ? alphaServer.RebootState : bravoServer.RebootState) : null;
|
||||
public RebootState? RebootState => Running ? (RebootState?)(AlphaIsActive ? alphaServer?.RebootState : bravoServer?.RebootState) : null;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IChat"/> for the <see cref="Watchdog"/>
|
||||
@@ -45,21 +51,21 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// </summary>
|
||||
readonly ISessionControllerFactory sessionControllerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IEventConsumer"/> for the <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
readonly IEventConsumer eventConsumer;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IInteropRegistrar"/> for the <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
readonly IInteropRegistrar interopRegistrar;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDmbFactory"/> for the <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
readonly IDmbFactory dmbFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger{TCategoryName}"/> for the <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
readonly ILogger<Watchdog> logger;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IReattachInfoHandler"/> for the <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
readonly IReattachInfoHandler reattachInfoHandler;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SemaphoreSlim"/> for the <see cref="Watchdog"/>
|
||||
/// </summary>
|
||||
@@ -82,19 +88,19 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// <param name="chat">The value of <see cref="chat"/></param>
|
||||
/// <param name="sessionControllerFactory">The value of <see cref="sessionControllerFactory"/></param>
|
||||
/// <param name="dmbFactory">The value of <see cref="dmbFactory"/></param>
|
||||
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
|
||||
/// <param name="interopRegistrar">The value of <see cref="interopRegistrar"/></param>
|
||||
/// <param name="serverUpdater">The <see cref="IServerUpdater"/> for the <see cref="Watchdog"/></param>
|
||||
/// <param name="logger">The value of <see cref="logger"/></param>
|
||||
/// <param name="reattachInfoHandler">The value of <see cref="reattachInfoHandler"/></param>
|
||||
/// <param name="initialLaunchParameters">The initial value of <see cref="ActiveLaunchParameters"/></param>
|
||||
public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IEventConsumer eventConsumer, IInteropRegistrar interopRegistrar, IServerUpdater serverUpdater, DreamDaemonLaunchParameters initialLaunchParameters)
|
||||
public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerUpdater serverUpdater, ILogger<Watchdog> logger, IReattachInfoHandler reattachInfoHandler, DreamDaemonLaunchParameters initialLaunchParameters)
|
||||
{
|
||||
this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
|
||||
this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
|
||||
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
|
||||
this.interopRegistrar = interopRegistrar ?? throw new ArgumentNullException(nameof(interopRegistrar));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler));
|
||||
|
||||
if(serverUpdater == null)
|
||||
if (serverUpdater == null)
|
||||
throw new ArgumentNullException(nameof(serverUpdater));
|
||||
|
||||
serverUpdater.RegisterForUpdate(() => releaseServers = true);
|
||||
@@ -112,9 +118,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
semaphore.Dispose();
|
||||
}
|
||||
|
||||
|
||||
void DisposeAndNullControllers()
|
||||
{
|
||||
logger.LogTrace("DisposeAndNullControllers");
|
||||
alphaServer?.Dispose();
|
||||
alphaServer = null;
|
||||
bravoServer?.Dispose();
|
||||
@@ -126,9 +132,17 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
var running = Running;
|
||||
if (!graceful || !running)
|
||||
{
|
||||
Task chatTask;
|
||||
if (running)
|
||||
await Terminate(false, cancellationToken).ConfigureAwait(false);
|
||||
return await Launch(cancellationToken).ConfigureAwait(false);
|
||||
{
|
||||
chatTask = chat.SendWatchdogMessage("Manual restart triggered...", cancellationToken);
|
||||
await TerminateNoLock(false, false, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
chatTask = Task.CompletedTask;
|
||||
var result = await LaunchNoLock(true, !running, false, cancellationToken).ConfigureAwait(false);
|
||||
await chatTask.ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
var toReboot = AlphaIsActive ? alphaServer : bravoServer;
|
||||
var other = AlphaIsActive ? bravoServer : alphaServer;
|
||||
@@ -138,53 +152,167 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
return null;
|
||||
}
|
||||
|
||||
async Task HandlerMonitorWakeup(MonitorActivationReason activationReason, MonitorState monitorState)
|
||||
async Task TerminateNoLock(bool graceful, bool announce, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
if (!Running)
|
||||
return;
|
||||
if (!graceful)
|
||||
{
|
||||
var chatTask = announce ? chat.SendWatchdogMessage("Terminating...", cancellationToken) : Task.CompletedTask;
|
||||
await StopMonitor().ConfigureAwait(false);
|
||||
DisposeAndNullControllers();
|
||||
await chatTask.ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var toKill = AlphaIsActive ? alphaServer : bravoServer;
|
||||
var other = AlphaIsActive ? bravoServer : alphaServer;
|
||||
if (toKill != null)
|
||||
await toKill.SetRebootState(Components.Watchdog.RebootState.Shutdown, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
async Task HandlerMonitorWakeup(MonitorActivationReason activationReason, MonitorState monitorState)
|
||||
{
|
||||
logger.LogInformation("Monitor activation. Reason: {0}", activationReason);
|
||||
await Task.Yield();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The loop that watches the watchdog
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
async Task MonitorLifetimes(CancellationToken cancellationToken)
|
||||
{
|
||||
var state = new MonitorState();
|
||||
while(true)
|
||||
logger.LogDebug("Entered MonitorLifetimes");
|
||||
var iteration = 1;
|
||||
for(var state = new MonitorState(); state.NextAction != MonitorAction.Exit; ++iteration)
|
||||
{
|
||||
logger.LogDebug("New iteration of monitor loop");
|
||||
try
|
||||
{
|
||||
var alphaServerTask = alphaServer.Lifetime;
|
||||
var bravoServerTask = bravoServer.Lifetime;
|
||||
var alphaServerReboot = alphaServer.OnReboot;
|
||||
var bravoServerReboot = bravoServer.OnReboot;
|
||||
if(AlphaIsActive)
|
||||
logger.LogDebug("Alpha is the active server");
|
||||
else
|
||||
logger.LogDebug("Bravo is the active server");
|
||||
|
||||
var activeServer = AlphaIsActive ? alphaServer : bravoServer;
|
||||
var inactiveServer = AlphaIsActive ? bravoServer : alphaServer;
|
||||
if(state.InactiveServerHasStagedDmb)
|
||||
logger.LogDebug("Inactive server has staged .dmb");
|
||||
if (state.RebootingInactiveServer)
|
||||
logger.LogDebug("Inactive server is rebooting");
|
||||
|
||||
state.ActiveServer = AlphaIsActive ? alphaServer : bravoServer;
|
||||
state.InactiveServer = AlphaIsActive ? bravoServer : alphaServer;
|
||||
|
||||
var activeServerLifetime = state.ActiveServer.Lifetime;
|
||||
var inactiveServerLifetime = state.InactiveServer.Lifetime;
|
||||
var activeServerReboot = state.ActiveServer.OnReboot;
|
||||
var inactiveServerReboot = state.InactiveServer.OnReboot;
|
||||
var inactiveServerStartup = state.InactiveServer.LaunchResult;
|
||||
var newDmbAvailable = dmbFactory.OnNewerDmb;
|
||||
|
||||
var cancelTcs = new TaskCompletionSource<object>();
|
||||
using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
|
||||
{
|
||||
var toWaitOn = Task.WhenAny(alphaServerTask, bravoServerTask, alphaServerReboot, bravoServerReboot, cancelTcs.Task);
|
||||
if (!watchReboot)
|
||||
toWaitOn = Task.WhenAny(toWaitOn, inactiveServer.LaunchResult);
|
||||
var toWaitOn = Task.WhenAny(activeServerLifetime, inactiveServerLifetime, activeServerReboot, inactiveServerReboot, newDmbAvailable, cancelTcs.Task);
|
||||
if (state.RebootingInactiveServer)
|
||||
toWaitOn = Task.WhenAny(toWaitOn, inactiveServerStartup);
|
||||
await toWaitOn.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
using (await SemaphoreContext.Lock(semaphore, default).ConfigureAwait(false))
|
||||
var chatTask = Task.CompletedTask;
|
||||
using (await SemaphoreContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
state.ActiveServer = AlphaIsActive ? alphaServer : bravoServer;
|
||||
state.InactiveServer = !AlphaIsActive ? alphaServer : bravoServer;
|
||||
MonitorActivationReason activationReason;
|
||||
if (activeServer.Lifetime.IsCompleted)
|
||||
activationReason = MonitorActivationReason.ActiveServerCrashed;
|
||||
else if(inactiveServer.Lifetime.IsCompleted)
|
||||
MonitorActivationReason activationReason = default;
|
||||
//multiple things may have happened, handle them one at a time
|
||||
for (var moreActivationsToProcess = true; moreActivationsToProcess && state.NextAction == MonitorAction.Continue; await HandlerMonitorWakeup(activationReason, state).ConfigureAwait(false))
|
||||
{
|
||||
if (activeServerLifetime?.IsCompleted == true)
|
||||
{
|
||||
activationReason = MonitorActivationReason.ActiveServerCrashed;
|
||||
activeServerLifetime = null;
|
||||
}
|
||||
else if (inactiveServerLifetime?.IsCompleted == true)
|
||||
{
|
||||
activationReason = MonitorActivationReason.InactiveServerCrashed;
|
||||
inactiveServerLifetime = null;
|
||||
}
|
||||
else if (activeServerReboot?.IsCompleted == true)
|
||||
{
|
||||
activationReason = MonitorActivationReason.ActiveServerRebooted;
|
||||
activeServerReboot = null;
|
||||
}
|
||||
else if (inactiveServerReboot?.IsCompleted == true)
|
||||
{
|
||||
activationReason = MonitorActivationReason.InactiveServerRebooted;
|
||||
inactiveServerReboot = null;
|
||||
}
|
||||
else if (inactiveServerStartup?.IsCompleted == true)
|
||||
{
|
||||
activationReason = MonitorActivationReason.InactiveServerStartupComplete;
|
||||
inactiveServerStartup = null;
|
||||
}
|
||||
else if (newDmbAvailable?.IsCompleted == true)
|
||||
{
|
||||
activationReason = MonitorActivationReason.NewDmbAvailable;
|
||||
newDmbAvailable = null;
|
||||
}
|
||||
else
|
||||
moreActivationsToProcess = false;
|
||||
}
|
||||
//full reboot required
|
||||
if (state.NextAction == MonitorAction.Restart)
|
||||
{
|
||||
logger.LogDebug("Next state action is to restart");
|
||||
DisposeAndNullControllers();
|
||||
Running = false;
|
||||
chatTask = chat.SendWatchdogMessage("Restarting due to complications...", cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
for (var retryAttempts = 1; state.NextAction == MonitorAction.Restart; ++retryAttempts)
|
||||
{
|
||||
WatchdogLaunchResult result;
|
||||
using (await SemaphoreContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
|
||||
result = await LaunchNoLock(false, false, false, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await chatTask.ConfigureAwait(false);
|
||||
if (Running)
|
||||
state.NextAction = MonitorAction.Continue;
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Failed to automatically restart the watchdog! Alpha: {0}; Bravo: {1}", result.Alpha.ToString(), result.Bravo.ToString());
|
||||
var retryDelay = Math.Min(Math.Pow(2, retryAttempts), 3600); //max of one hour
|
||||
chatTask = chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Failed to restart watchdog (Attempt: {0}), retrying in {1} seconds...", retryAttempts, retryDelay), cancellationToken);
|
||||
await Task.WhenAll(Task.Delay((int)retryDelay, cancellationToken), chatTask).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
logger.LogDebug("Monitor cancelled");
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Monitor crashed, this should NEVER happen! Restarting monitor... Error: {0}", e.Message), cancellationToken).ConfigureAwait(false);
|
||||
logger.LogError("Monitor crashed! Iteration: {0}, State: {1}", iteration, JsonConvert.SerializeObject(state));
|
||||
await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Monitor crashed, this should NEVER happen! Please report this, full details in logs! Restarting monitor... Error: {0}", e.Message), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task<bool> StopMonitor()
|
||||
{
|
||||
logger.LogTrace("StopMonitor");
|
||||
if (monitorTask == null)
|
||||
return false;
|
||||
monitorCts.Cancel();
|
||||
await monitorTask.ConfigureAwait(false);
|
||||
monitorCts.Dispose();
|
||||
monitorTask = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -196,99 +324,134 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
}
|
||||
}
|
||||
|
||||
async Task StopMonitor()
|
||||
{
|
||||
if (monitorTask == null)
|
||||
return;
|
||||
monitorCts.Cancel();
|
||||
await monitorTask.ConfigureAwait(false);
|
||||
monitorCts.Dispose();
|
||||
monitorTask = null;
|
||||
}
|
||||
|
||||
async Task<WatchdogLaunchResult> LaunchNoLock(bool startMonitor, CancellationToken cancellationToken)
|
||||
async Task<WatchdogLaunchResult> LaunchNoLock(bool startMonitor, bool announce, bool doReattach, CancellationToken cancellationToken)
|
||||
{
|
||||
using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
|
||||
{
|
||||
logger.LogTrace("Begin LaunchNoLock");
|
||||
if (Running)
|
||||
{
|
||||
logger.LogTrace("Aborted due to already running!");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Task chatTask;
|
||||
//this is necessary, the monitor could be in it's sleep loop trying to restart
|
||||
if (startMonitor && await StopMonitor().ConfigureAwait(false))
|
||||
chatTask = chat.SendWatchdogMessage("Automatic retry sequence cancelled by manual launch. Restarting...", cancellationToken);
|
||||
else if (announce)
|
||||
chatTask = chat.SendWatchdogMessage("Starting...", cancellationToken);
|
||||
else
|
||||
chatTask = Task.CompletedTask;
|
||||
//start both servers
|
||||
LastLaunchParameters = ActiveLaunchParameters;
|
||||
var dmbToUse = await dmbFactory.LockNextDmb(cancellationToken).ConfigureAwait(false);
|
||||
Task<ISessionController> alphaServerTask = null;
|
||||
|
||||
try
|
||||
{
|
||||
//good ole sanity
|
||||
if (alphaServer != null || bravoServer != null)
|
||||
throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!");
|
||||
|
||||
WatchdogReattachInformation reattachInfo = doReattach ? await reattachInfoHandler.Load(cancellationToken).ConfigureAwait(false) : null;
|
||||
var doesntNeedNewDmb = doReattach && reattachInfo.Alpha != null && reattachInfo.Bravo != null;
|
||||
var dmbToUse = doesntNeedNewDmb ? null : await dmbFactory.LockNextDmb(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Task<ISessionController> alphaServerTask = null;
|
||||
try
|
||||
{
|
||||
alphaServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, true, true, false, alphaStartCts.Token);
|
||||
//do a few seconds of delay so that any backends the servers use know that alpha came first
|
||||
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
|
||||
var bravoServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, false, false, false, cancellationToken);
|
||||
bravoServer = await bravoServerTask.ConfigureAwait(false);
|
||||
alphaServer = await alphaServerTask.ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (!doReattach || reattachInfo.Alpha == null)
|
||||
alphaServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, true, true, false, alphaStartCts.Token);
|
||||
else
|
||||
alphaServerTask = sessionControllerFactory.Reattach(reattachInfo.Alpha, cancellationToken);
|
||||
//do a few seconds of delay so that any backends the servers use know that alpha came first
|
||||
await Task.Delay(AlphaBravoStartupSeperationInterval, cancellationToken).ConfigureAwait(false);
|
||||
Task<ISessionController> bravoServerTask;
|
||||
if (!doReattach || reattachInfo.Bravo == null)
|
||||
bravoServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, false, false, false, cancellationToken);
|
||||
else
|
||||
bravoServerTask = sessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken);
|
||||
|
||||
bravoServer = await bravoServerTask.ConfigureAwait(false);
|
||||
alphaServer = await alphaServerTask.ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (alphaServerTask != null)
|
||||
if (alphaServerTask.Status == TaskStatus.RanToCompletion)
|
||||
alphaServer = await alphaServerTask.ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
alphaStartCts.Cancel();
|
||||
try
|
||||
{
|
||||
alphaServer = await alphaServerTask.ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
async Task<LaunchResult> CheckLaunch(ISessionController controller, string serverName)
|
||||
{
|
||||
var launch = await controller.LaunchResult.ConfigureAwait(false);
|
||||
if (launch.ExitCode.HasValue)
|
||||
//you killed us ray...
|
||||
throw new Exception(String.Format(CultureInfo.InvariantCulture, "{1} server failed to start: {0}", launch.ToString(), serverName));
|
||||
return launch;
|
||||
}
|
||||
|
||||
var alphaLrt = CheckLaunch(alphaServer, "Alpha");
|
||||
var bravoLrt = CheckLaunch(bravoServer, "Bravo");
|
||||
//now we have two booting servers, get them up and running
|
||||
var allTask = Task.WhenAll(alphaLrt, bravoLrt);
|
||||
|
||||
//don't forget about the cancelationToken
|
||||
var cancelTcs = new TaskCompletionSource<object>();
|
||||
using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
|
||||
await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false);
|
||||
|
||||
//both servers are now running, alpha is the active server, huzzah
|
||||
AlphaIsActive = doReattach ? reattachInfo.AlphaIsActive : true;
|
||||
LiveCompileJob = AlphaIsActive ? alphaServer.Dmb.CompileJob : bravoServer.Dmb.CompileJob;
|
||||
LastLaunchResult = alphaLrt.Result;
|
||||
StagedCompileJob = null;
|
||||
Running = true;
|
||||
|
||||
if (startMonitor)
|
||||
{
|
||||
await StopMonitor().ConfigureAwait(false);
|
||||
monitorCts = new CancellationTokenSource();
|
||||
monitorTask = MonitorLifetimes(monitorCts.Token);
|
||||
}
|
||||
return new WatchdogLaunchResult
|
||||
{
|
||||
Alpha = alphaLrt.Result,
|
||||
Bravo = bravoLrt.Result
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (alphaServerTask != null)
|
||||
if (alphaServerTask.Status == TaskStatus.RanToCompletion)
|
||||
alphaServer = await alphaServerTask.ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
alphaStartCts.Cancel();
|
||||
try
|
||||
{
|
||||
alphaServer = await alphaServerTask.ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
if (alphaServer == null && bravoServer == null)
|
||||
dmbToUse.Dispose(); //guaranteed to not be null here
|
||||
DisposeAndNullControllers();
|
||||
throw;
|
||||
}
|
||||
|
||||
async Task<LaunchResult> CheckLaunch(ISessionController controller, string serverName)
|
||||
{
|
||||
var launch = await controller.LaunchResult.ConfigureAwait(false);
|
||||
if (launch.ExitCode.HasValue)
|
||||
//you killed us ray...
|
||||
throw new Exception(String.Format(CultureInfo.InvariantCulture, "{2} server failed to start: Exit Code: {0}, RAM: {1}, Runtime {3}ms", launch.ExitCode, launch.StartupTime, serverName, launch.StartupTime.TotalMilliseconds));
|
||||
return launch;
|
||||
}
|
||||
|
||||
var alphaLrt = CheckLaunch(alphaServer, "Alpha");
|
||||
var bravoLrt = CheckLaunch(bravoServer, "Bravo");
|
||||
//now we have two booting servers, get them up and running
|
||||
var allTask = Task.WhenAll(alphaLrt, bravoLrt);
|
||||
|
||||
//don't forget about the cancelationToken
|
||||
var cancelTcs = new TaskCompletionSource<object>();
|
||||
using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
|
||||
await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false);
|
||||
|
||||
//both servers are now running, alpha is the active server, huzzah
|
||||
LiveCompileJob = dmbToUse.CompileJob;
|
||||
LastLaunchResult = alphaLrt.Result;
|
||||
StagedCompileJob = null;
|
||||
AlphaIsActive = true;
|
||||
Running = true;
|
||||
|
||||
if (startMonitor)
|
||||
{
|
||||
await StopMonitor().ConfigureAwait(false);
|
||||
monitorCts = new CancellationTokenSource();
|
||||
monitorTask = MonitorLifetimes(monitorCts.Token);
|
||||
}
|
||||
|
||||
return new WatchdogLaunchResult
|
||||
{
|
||||
Alpha = alphaLrt.Result,
|
||||
Bravo = bravoLrt.Result
|
||||
};
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
DisposeAndNullControllers();
|
||||
logger.LogWarning("Failed to start watchdog: {0}", e.ToString());
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
await chatTask.ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +459,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
public async Task<WatchdogLaunchResult> Launch(CancellationToken cancellationToken)
|
||||
{
|
||||
using (await SemaphoreContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
|
||||
return await LaunchNoLock(true, cancellationToken).ConfigureAwait(false);
|
||||
return await LaunchNoLock(true, true, false, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -310,37 +473,23 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
public async Task Terminate(bool graceful, CancellationToken cancellationToken)
|
||||
{
|
||||
using (await SemaphoreContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
if (!Running)
|
||||
return;
|
||||
await StopMonitor().ConfigureAwait(false);
|
||||
if (!graceful)
|
||||
{
|
||||
DisposeAndNullControllers();
|
||||
return;
|
||||
}
|
||||
var toKill = AlphaIsActive ? alphaServer : bravoServer;
|
||||
var other = AlphaIsActive ? bravoServer : alphaServer;
|
||||
if (toKill != null)
|
||||
await toKill.SetRebootState(Components.Watchdog.RebootState.Shutdown, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
await TerminateNoLock(graceful, true, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task StartAsync(CancellationToken cancellationToken) => Launch(cancellationToken);
|
||||
public Task StartAsync(CancellationToken cancellationToken) => LaunchNoLock(true, true, true, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (releaseServers)
|
||||
{
|
||||
ReattachInformation reattachInformation;
|
||||
if (AlphaIsActive)
|
||||
reattachInformation = alphaServer?.Release();
|
||||
else
|
||||
reattachInformation = bravoServer?.Release();
|
||||
var reattachInformation = new WatchdogReattachInformation { AlphaIsActive = AlphaIsActive };
|
||||
reattachInformation.Alpha = alphaServer?.Release();
|
||||
reattachInformation.Bravo = bravoServer?.Release();
|
||||
await reattachInfoHandler.Save(reattachInformation, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
return Terminate(false, cancellationToken);
|
||||
await Terminate(false, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Core;
|
||||
|
||||
@@ -7,11 +8,6 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// <inheritdoc />
|
||||
sealed class WatchdogFactory : IWatchdogFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IByond"/> for the <see cref="WatchdogFactory"/>
|
||||
/// </summary>
|
||||
readonly IByond byond;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IChat"/> for the <see cref="WatchdogFactory"/>
|
||||
/// </summary>
|
||||
@@ -22,40 +18,40 @@ namespace Tgstation.Server.Host.Components.Watchdog
|
||||
/// </summary>
|
||||
readonly ISessionControllerFactory sessionManagerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IEventConsumer"/> for the <see cref="WatchdogFactory"/>
|
||||
/// </summary>
|
||||
readonly IEventConsumer eventConsumer;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IInteropRegistrar"/> for the <see cref="WatchdogFactory"/>
|
||||
/// </summary>
|
||||
readonly IInteropRegistrar interopRegistrar;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IServerUpdater"/> for the <see cref="WatchdogFactory"/>
|
||||
/// </summary>
|
||||
readonly IServerUpdater serverUpdater;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILoggerFactory"/> for the <see cref="WatchdogFactory"/>
|
||||
/// </summary>
|
||||
readonly ILoggerFactory loggerFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IReattachInfoHandler"/> for the <see cref="WatchdogFactory"/>
|
||||
/// </summary>
|
||||
readonly IReattachInfoHandler reattachInfoHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a <see cref="WatchdogFactory"/>
|
||||
/// </summary>
|
||||
/// <param name="byond">The value of <see cref="byond"/></param>
|
||||
/// <param name="chat">The value of <see cref="chat"/></param>
|
||||
/// <param name="sessionManagerFactory">The value of <see cref="sessionManagerFactory"/></param>
|
||||
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
|
||||
/// <param name="interopRegistrar">The value of <see cref="interopRegistrar"/></param>
|
||||
/// <param name="serverUpdater">The value of <see cref="serverUpdater"/></param>
|
||||
public WatchdogFactory(IChat chat, ISessionControllerFactory sessionManagerFactory, IEventConsumer eventConsumer, IInteropRegistrar interopRegistrar, IServerUpdater serverUpdater)
|
||||
/// <param name="loggerFactory">The value of <see cref="loggerFactory"/></param>
|
||||
/// <param name="reattachInfoHandler">The value of <see cref="reattachInfoHandler"/></param>
|
||||
public WatchdogFactory(IChat chat, ISessionControllerFactory sessionManagerFactory, IServerUpdater serverUpdater, ILoggerFactory loggerFactory, IReattachInfoHandler reattachInfoHandler)
|
||||
{
|
||||
this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
this.sessionManagerFactory = sessionManagerFactory ?? throw new ArgumentNullException(nameof(sessionManagerFactory));
|
||||
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
|
||||
this.interopRegistrar = interopRegistrar ?? throw new ArgumentNullException(nameof(interopRegistrar));
|
||||
this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
|
||||
this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IWatchdog CreateWatchdog(IDmbFactory dmbFactory, DreamDaemonLaunchParameters launchParameters) => new Watchdog(chat, sessionManagerFactory, dmbFactory, eventConsumer, interopRegistrar, serverUpdater, launchParameters);
|
||||
public IWatchdog CreateWatchdog(IDmbFactory dmbFactory, DreamDaemonLaunchParameters launchParameters) => new Watchdog(chat, sessionManagerFactory, dmbFactory, serverUpdater, loggerFactory.CreateLogger<Watchdog>(), reattachInfoHandler, launchParameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
/// <summary>
|
||||
/// If the Alpha session is the active session
|
||||
/// </summary>
|
||||
bool AlphaIsActive { get; set; }
|
||||
public bool AlphaIsActive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ReattachInformation"/> for the Alpha session
|
||||
/// </summary>
|
||||
ReattachInformation Alpha { get; set; }
|
||||
public ReattachInformation Alpha { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ReattachInformation"/> for the Bravo session
|
||||
/// </summary>
|
||||
ReattachInformation Bravo { get; set; }
|
||||
public ReattachInformation Bravo { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -9,7 +10,9 @@ namespace Tgstation.Server.Host.Core.Tests
|
||||
[TestClass]
|
||||
public sealed class TestApplication : IServerUpdater
|
||||
{
|
||||
public void ApplyUpdate(string updatePath) => throw new System.NotImplementedException();
|
||||
public void ApplyUpdate(string updatePath) => throw new NotImplementedException();
|
||||
|
||||
public void RegisterForUpdate(Action action) => throw new NotImplementedException();
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestSuccessfulStartup()
|
||||
|
||||
Reference in New Issue
Block a user