mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-30 16:39:21 +01:00
Fix webroot issue in deployments.
Propagate startup exceptions to the watchdog
This commit is contained in:
@@ -39,6 +39,11 @@ namespace Tgstation.Server.Host.Components
|
||||
/// </summary>
|
||||
readonly IJobManager jobManager;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IServerControl"/> for the <see cref="InstanceManager"/>
|
||||
/// </summary>
|
||||
readonly IServerControl serverControl;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="InstanceManager"/>
|
||||
/// </summary>
|
||||
@@ -67,7 +72,7 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <param name="databaseContextFactory">The value of <paramref name="databaseContextFactory"/></param>
|
||||
/// <param name="application">The value of <see cref="application"/></param>
|
||||
/// <param name="jobManager">The value of <see cref="jobManager"/></param>
|
||||
/// <param name="serverControl">The <see cref="IServerControl"/> used to register the <see cref="InstanceManager"/> as a <see cref="IRestartHandler"/></param>
|
||||
/// <param name="serverControl">The value of <see cref="serverControl"/></param>
|
||||
/// <param name="logger">The value of <see cref="logger"/></param>
|
||||
public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger<InstanceManager> logger)
|
||||
{
|
||||
@@ -76,8 +81,7 @@ namespace Tgstation.Server.Host.Components
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
this.application = application ?? throw new ArgumentNullException(nameof(application));
|
||||
this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
|
||||
if (serverControl == null)
|
||||
throw new ArgumentNullException(nameof(serverControl));
|
||||
this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
serverControl.RegisterForRestart(this);
|
||||
@@ -215,7 +219,7 @@ namespace Tgstation.Server.Host.Components
|
||||
await factoryStartup.ConfigureAwait(false);
|
||||
await dbInstances.ForEachAsync(metadata => tasks.Add(metadata.Online.Value ? OnlineInstance(metadata, cancellationToken) : Task.CompletedTask), cancellationToken).ConfigureAwait(false);
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
logger.LogInformation("Instance manager ready!");
|
||||
logger.LogInformation("Server ready!");
|
||||
application.Ready(null);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -226,6 +230,14 @@ namespace Tgstation.Server.Host.Components
|
||||
{
|
||||
logger.LogCritical("Instance manager startup error! Exception: {0}", e);
|
||||
application.Ready(e);
|
||||
try
|
||||
{
|
||||
await serverControl.Die(e).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
logger.LogCritical("Failed to kill server! Exception: {0}", e2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -36,5 +36,12 @@ namespace Tgstation.Server.Host.Core
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task Restart();
|
||||
|
||||
/// <summary>
|
||||
/// Kill the server with a fatal exception
|
||||
/// </summary>
|
||||
/// <param name="exception">The <see cref="Exception"/> to propagate to the watchdog if any</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task Die(Exception exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,11 @@ namespace Tgstation.Server.Host
|
||||
/// </summary>
|
||||
CancellationTokenSource cancellationTokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Exception"/> to propagate when the server terminates
|
||||
/// </summary>
|
||||
Exception propagatedException;
|
||||
|
||||
/// <summary>
|
||||
/// If a server update has been or is being applied
|
||||
/// </summary>
|
||||
@@ -63,6 +68,8 @@ namespace Tgstation.Server.Host
|
||||
this.webHostBuilder = webHostBuilder ?? throw new ArgumentNullException(nameof(webHostBuilder));
|
||||
this.updatePath = updatePath;
|
||||
|
||||
webHostBuilder.ConfigureServices(serviceCollection => serviceCollection.AddSingleton<IServerControl>(this));
|
||||
|
||||
restartHandlers = new List<IRestartHandler>();
|
||||
}
|
||||
|
||||
@@ -72,13 +79,22 @@ namespace Tgstation.Server.Host
|
||||
/// <param name="checkWatchdog">If <see cref="WatchdogPresent"/> should be checked</param>
|
||||
void CheckSanity(bool checkWatchdog)
|
||||
{
|
||||
if (checkWatchdog && !WatchdogPresent)
|
||||
if (checkWatchdog && !WatchdogPresent && propagatedException == null)
|
||||
throw new InvalidOperationException("Server restarts are not supported");
|
||||
|
||||
if (cancellationTokenSource == null || logger == null)
|
||||
throw new InvalidOperationException("Tried to control a non-running Server!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-throw <see cref="propagatedException"/> if it exists
|
||||
/// </summary>
|
||||
void CheckExceptionPropagation()
|
||||
{
|
||||
if (propagatedException != null)
|
||||
throw propagatedException;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -94,16 +110,20 @@ namespace Tgstation.Server.Host
|
||||
};
|
||||
fsWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
using (var webHost = webHostBuilder
|
||||
.UseStartup<Application>()
|
||||
.ConfigureServices(serviceCollection => serviceCollection.AddSingleton<IServerControl>(this))
|
||||
.Build()
|
||||
)
|
||||
{
|
||||
logger = webHost.Services.GetRequiredService<ILogger<Server>>();
|
||||
await webHost.RunAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
using (var webHost = webHostBuilder.Build())
|
||||
try
|
||||
{
|
||||
logger = webHost.Services.GetRequiredService<ILogger<Server>>();
|
||||
await webHost.RunAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
CheckExceptionPropagation();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
CheckExceptionPropagation();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -163,7 +183,7 @@ namespace Tgstation.Server.Host
|
||||
throw;
|
||||
}
|
||||
|
||||
await Restart(version).ConfigureAwait(false);
|
||||
await Restart(version, null).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -207,14 +227,15 @@ namespace Tgstation.Server.Host
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Restart() => Restart(null);
|
||||
public Task Restart() => Restart(null, null);
|
||||
|
||||
/// <summary>
|
||||
/// Implements <see cref="Restart()"/>
|
||||
/// </summary>
|
||||
/// <param name="newVersion">The <see cref="Version"/> of any potential updates being applied</param>
|
||||
/// <param name="exception">The potential value of <see cref="propagatedException"/></param>
|
||||
/// <returns></returns>
|
||||
async Task Restart(Version newVersion)
|
||||
async Task Restart(Version newVersion, Exception exception)
|
||||
{
|
||||
CheckSanity(true);
|
||||
|
||||
@@ -228,33 +249,36 @@ namespace Tgstation.Server.Host
|
||||
return;
|
||||
}
|
||||
RestartRequested = true;
|
||||
propagatedException = exception;
|
||||
}
|
||||
|
||||
logger.LogInformation("Restarting server...");
|
||||
|
||||
using (var cts = new CancellationTokenSource())
|
||||
{
|
||||
logger.LogTrace("Running restart handlers...");
|
||||
var cancellationToken = cts.Token;
|
||||
var eventsTask = Task.WhenAll(restartHandlers.Select(x => x.HandleRestart(newVersion, cancellationToken)).ToList());
|
||||
//YA GOT 10 SECONDS
|
||||
var expiryTask = Task.Delay(TimeSpan.FromSeconds(10));
|
||||
await Task.WhenAny(eventsTask, expiryTask).ConfigureAwait(false);
|
||||
logger.LogTrace("Joining restart handlers...");
|
||||
cts.Cancel();
|
||||
try
|
||||
if (exception == null)
|
||||
using (var cts = new CancellationTokenSource())
|
||||
{
|
||||
await eventsTask.ConfigureAwait(false);
|
||||
logger.LogInformation("Restarting server...");
|
||||
var cancellationToken = cts.Token;
|
||||
var eventsTask = Task.WhenAll(restartHandlers.Select(x => x.HandleRestart(newVersion, cancellationToken)).ToList());
|
||||
//YA GOT 10 SECONDS
|
||||
var expiryTask = Task.Delay(TimeSpan.FromSeconds(10));
|
||||
await Task.WhenAny(eventsTask, expiryTask).ConfigureAwait(false);
|
||||
logger.LogTrace("Joining restart handlers...");
|
||||
cts.Cancel();
|
||||
try
|
||||
{
|
||||
await eventsTask.ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError("Restart handlers error! Exception: {0}", e);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError("Restart handlers error! Exception: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.LogTrace("Stopping host...");
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task Die(Exception exception) => Restart(null, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Tgstation.Server.Host.Core;
|
||||
|
||||
namespace Tgstation.Server.Host
|
||||
{
|
||||
@@ -7,6 +12,18 @@ namespace Tgstation.Server.Host
|
||||
public sealed class ServerFactory : IServerFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IServer CreateServer(string[] args, string updatePath) => new Server(WebHost.CreateDefaultBuilder(args ?? throw new ArgumentNullException(nameof(args))), updatePath);
|
||||
public IServer CreateServer(string[] args, string updatePath) {
|
||||
|
||||
var webHost = WebHost.CreateDefaultBuilder(args ?? throw new ArgumentNullException(nameof(args)))
|
||||
.ConfigureAppConfiguration((context, configurationBuilder) => configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()))
|
||||
.UseStartup<Application>()
|
||||
.SuppressStatusMessages(true)
|
||||
.UseShutdownTimeout(TimeSpan.FromMinutes(1));
|
||||
|
||||
if(updatePath != null)
|
||||
webHost.UseContentRoot(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
|
||||
|
||||
return new Server(webHost, updatePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user