From 19e29e3340c5e184289fc7496496b48d03aae85a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 12:37:23 -0400 Subject: [PATCH 1/9] Remove old command line test project --- .../Tgstation.Server.CommandLine.Tests.csproj | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 tests/Tgstation.Server.CommandLine.Tests/Tgstation.Server.CommandLine.Tests.csproj diff --git a/tests/Tgstation.Server.CommandLine.Tests/Tgstation.Server.CommandLine.Tests.csproj b/tests/Tgstation.Server.CommandLine.Tests/Tgstation.Server.CommandLine.Tests.csproj deleted file mode 100644 index 43a487525f..0000000000 --- a/tests/Tgstation.Server.CommandLine.Tests/Tgstation.Server.CommandLine.Tests.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp2.0 - - false - latest - - - - - - - - - - - - - From 50e58782eb0347813d42ab4477a8387f7c79a20a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 12:39:36 -0400 Subject: [PATCH 2/9] Fix trusted mode prompt still showing on Windows --- .../Components/Byond/WindowsByondInstaller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs index a17aa4eece..99ac434050 100644 --- a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs @@ -21,7 +21,7 @@ namespace Tgstation.Server.Host.Components.Byond /// /// Directory to byond installation configuration /// - const string ByondConfigDir = "byond/config"; + const string ByondConfigDir = "byond/cfg"; /// /// BYOND's DreamDaemon config file /// From 24925f89cb4baf210ebb5286c880172eae7d3c38 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 12:52:06 -0400 Subject: [PATCH 3/9] Add validation for incorrect committer names/emails --- .../Controllers/RepositoryController.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs index 721e8b6bd7..1d1d26b4e8 100644 --- a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs +++ b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs @@ -287,6 +287,12 @@ namespace Tgstation.Server.Host.Controllers if (model.NewTestMerges?.Any(x => model.NewTestMerges.Any(y => x != y && x.Number == y.Number)) == true) return BadRequest(new ErrorMessage { Message = "Cannot test merge the same PR twice in one job!" }); + if (model.CommitterName?.Length == 0) + return BadRequest(new ErrorMessage { Message = "Cannot set empty committer name!" }); + + if (model.CommitterEmail?.Length == 0) + return BadRequest(new ErrorMessage { Message = "Cannot set empty committer e=mail!" }); + var newTestMerges = model.NewTestMerges != null && model.NewTestMerges.Count > 0; var userRights = (RepositoryRights)AuthenticationContext.GetRight(RightsType.Repository); if (newTestMerges && !userRights.HasFlag(RepositoryRights.MergePullRequest)) From d4389d3c3365ec1a2e23e1282c557e171fe9d6d5 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 12:54:04 -0400 Subject: [PATCH 4/9] Throw a JobException if watchdog startup fails from controller --- src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index 59dfd9016b..1100022216 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -71,7 +71,7 @@ namespace Tgstation.Server.Host.Controllers { var result = await instance.Watchdog.Launch(innerCt).ConfigureAwait(false); if (result == null) - throw new InvalidOperationException("Watchdog already running!"); + throw new JobException("Watchdog already running!"); }, cancellationToken).ConfigureAwait(false); return Accepted(job.ToApi()); From d684886dcd1d222eff6032ddf6b1a280bd5558b2 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 13:13:56 -0400 Subject: [PATCH 5/9] Start watchdog on startup onlining via a job. Clean up dependencies a little --- .../Components/InstanceFactory.cs | 12 ++++-- .../Components/Watchdog/IWatchdogFactory.cs | 8 +++- .../Components/Watchdog/Watchdog.cs | 28 +++++++++++-- .../Components/Watchdog/WatchdogFactory.cs | 41 +++---------------- src/Tgstation.Server.Host/Core/Application.cs | 6 ++- 5 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index d676351557..cec17226f2 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -84,6 +84,11 @@ namespace Tgstation.Server.Host.Components /// readonly IPostWriteHandler postWriteHandler; + /// + /// The for the + /// + readonly IWatchdogFactory watchdogFactory; + /// /// Construct an /// @@ -100,7 +105,8 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of /// The value of - public InstanceFactory(IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, ILoggerFactory loggerFactory, IByondTopicSender byondTopicSender, IServerControl serverUpdater, ICryptographySuite cryptographySuite, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IByondInstaller byondInstaller, IProviderFactory providerFactory, IProcessExecutor processExecutor, IPostWriteHandler postWriteHandler) + /// The value of + public InstanceFactory(IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, ILoggerFactory loggerFactory, IByondTopicSender byondTopicSender, IServerControl serverUpdater, ICryptographySuite cryptographySuite, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IByondInstaller byondInstaller, IProviderFactory providerFactory, IProcessExecutor processExecutor, IPostWriteHandler postWriteHandler, IWatchdogFactory watchdogFactory) { this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); @@ -115,6 +121,7 @@ namespace Tgstation.Server.Host.Components this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory)); this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor)); this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler)); + this.watchdogFactory = watchdogFactory ?? throw new ArgumentNullException(nameof(watchdogFactory)); } /// @@ -148,8 +155,7 @@ namespace Tgstation.Server.Host.Components { var sessionControllerFactory = new SessionControllerFactory(processExecutor, byond, byondTopicSender, cryptographySuite, application, gameIoManager, chat, loggerFactory, metadata.CloneMetadata()); var reattachInfoHandler = new ReattachInfoHandler(databaseContextFactory, dmbFactory, metadata.CloneMetadata()); - var watchdogFactory = new WatchdogFactory(chat, sessionControllerFactory, serverUpdater, loggerFactory, reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, metadata.CloneMetadata()); - var watchdog = watchdogFactory.CreateWatchdog(dmbFactory, metadata.DreamDaemonSettings); + var watchdog = watchdogFactory.CreateWatchdog(chat, dmbFactory, reattachInfoHandler, configuration, sessionControllerFactory, metadata.CloneMetadata(), metadata.DreamDaemonSettings); eventConsumer.SetWatchdog(watchdog); commandFactory.SetWatchdog(watchdog); try diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdogFactory.cs index 68717edc9c..7c4091e959 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdogFactory.cs @@ -1,4 +1,5 @@ using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Chat; using Tgstation.Server.Host.Components.Compiler; namespace Tgstation.Server.Host.Components.Watchdog @@ -11,9 +12,14 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// Creates a /// + /// The for the /// The for the with + /// The for the + /// The for the + /// The for the + /// The for the /// The initial for the /// A new - IWatchdog CreateWatchdog(IDmbFactory dmbFactory, DreamDaemonSettings settings); + IWatchdog CreateWatchdog(IChat chat, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, IEventConsumer eventConsumer, ISessionControllerFactory sessionControllerFactory, Api.Models.Instance instance, DreamDaemonSettings settings); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index e0f82236a9..a2aeafe404 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -10,6 +10,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components.Chat; using Tgstation.Server.Host.Components.Compiler; using Tgstation.Server.Host.Components.Interop; @@ -86,6 +87,11 @@ namespace Tgstation.Server.Host.Components.Watchdog /// readonly IEventConsumer eventConsumer; + /// + /// The for the + /// + readonly IJobManager jobManager; + /// /// The for the /// @@ -141,11 +147,12 @@ namespace Tgstation.Server.Host.Components.Watchdog /// The value of /// The value of /// The value of + /// The value of + /// The value of /// The initial value of /// The value of /// The value of - /// The value of - public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerControl serverUpdater, ILogger logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart) + public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerControl serverUpdater, ILogger logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, IJobManager jobManager, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart) { this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory)); @@ -155,6 +162,7 @@ namespace Tgstation.Server.Host.Components.Watchdog this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); this.instance = instance ?? throw new ArgumentNullException(nameof(instance)); this.autoStart = autoStart; @@ -789,8 +797,20 @@ namespace Tgstation.Server.Host.Components.Watchdog /// public async Task StartAsync(CancellationToken cancellationToken) { - if (autoStart) - await LaunchNoLock(true, true, true, cancellationToken).ConfigureAwait(false); + if (!autoStart) + return; + + var job = new Models.Job + { + StartedBy = new Models.User + { + Id = 1 //just use admin for this cause whatever + }, + Description = "Instance startup watchdog launch", + CancelRight = (ulong)DreamDaemonRights.Shutdown, + CancelRightsType = RightsType.DreamDaemon + }; + await jobManager.RegisterOperation(job, (j, serviceProvider, progressFunction, ct) => Launch(ct), cancellationToken).ConfigureAwait(false); } /// diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs index 65b0452042..b200d8f93b 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs @@ -11,16 +11,6 @@ namespace Tgstation.Server.Host.Components.Watchdog /// sealed class WatchdogFactory : IWatchdogFactory { - /// - /// The for the - /// - readonly IChat chat; - - /// - /// The for the - /// - readonly ISessionControllerFactory sessionControllerFactory; - /// /// The for the /// @@ -31,11 +21,6 @@ namespace Tgstation.Server.Host.Components.Watchdog /// readonly ILoggerFactory loggerFactory; - /// - /// The for the - /// - readonly IReattachInfoHandler reattachInfoHandler; - /// /// The for the /// @@ -47,42 +32,28 @@ namespace Tgstation.Server.Host.Components.Watchdog readonly IByondTopicSender byondTopicSender; /// - /// The for the + /// The for the /// - readonly IEventConsumer eventConsumer; - - /// - /// The for the - /// - readonly Api.Models.Instance instance; - + readonly IJobManager jobManager; /// /// Construct a /// - /// The value of - /// The value of /// The value of /// The value of - /// The value of /// The value of /// The value of - /// The value of - /// The value of - public WatchdogFactory(IChat chat, ISessionControllerFactory sessionControllerFactory, IServerControl serverUpdater, ILoggerFactory loggerFactory, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, Api.Models.Instance instance) + /// The value of + public WatchdogFactory(IServerControl serverUpdater, ILoggerFactory loggerFactory, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IJobManager jobManager) { - this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); - this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory)); this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater)); this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler)); this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); - this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); - this.instance = instance ?? throw new ArgumentNullException(nameof(instance)); + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); } /// - public IWatchdog CreateWatchdog(IDmbFactory dmbFactory, DreamDaemonSettings settings) => new Watchdog(chat, sessionControllerFactory, dmbFactory, serverUpdater, loggerFactory.CreateLogger(), reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, settings, instance, settings.AutoStart.Value); + public IWatchdog CreateWatchdog(IChat chat, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, IEventConsumer eventConsumer, ISessionControllerFactory sessionControllerFactory, Api.Models.Instance instance, DreamDaemonSettings settings) => new Watchdog(chat, sessionControllerFactory, dmbFactory, serverUpdater, loggerFactory.CreateLogger(), reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, jobManager, settings, instance, settings.AutoStart.Value); } } diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index a397f43b17..dd9de80415 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -22,6 +22,7 @@ using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Components.Byond; using Tgstation.Server.Host.Components.Chat; using Tgstation.Server.Host.Components.StaticFiles; +using Tgstation.Server.Host.Components.Watchdog; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Controllers; using Tgstation.Server.Host.IO; @@ -200,8 +201,9 @@ namespace Tgstation.Server.Host.Core SendTimeout = 5000 }); - services.AddSingleton(); - services.AddSingleton(x => x.GetRequiredService()); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(x => x.GetRequiredService()); services.AddSingleton(x => x.GetRequiredService()); From 4068843f939f89d14cb2ec39ab0b734cebbae47a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 13:23:47 -0400 Subject: [PATCH 6/9] Add missing instance field to watchdog onlining job --- src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index a2aeafe404..4d36e949d0 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -806,6 +806,10 @@ namespace Tgstation.Server.Host.Components.Watchdog { Id = 1 //just use admin for this cause whatever }, + Instance = new Models.Instance + { + Id = instance.Id + }, Description = "Instance startup watchdog launch", CancelRight = (ulong)DreamDaemonRights.Shutdown, CancelRightsType = RightsType.DreamDaemon From aac185e51b271c0778ca97b864a28d7f8b7b0c99 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 14:43:49 -0400 Subject: [PATCH 7/9] Add restart on config change if possible --- src/Tgstation.Server.Host/Core/Application.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index a397f43b17..17404a3033 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using System; @@ -221,7 +222,8 @@ namespace Tgstation.Server.Host.Core /// /// The to configure /// The for the - public void Configure(IApplicationBuilder applicationBuilder, ILogger logger) + /// The for the application + public void Configure(IApplicationBuilder applicationBuilder, ILogger logger, IServerControl serverControl) { if (applicationBuilder == null) throw new ArgumentNullException(nameof(applicationBuilder)); @@ -229,6 +231,9 @@ namespace Tgstation.Server.Host.Core throw new ArgumentNullException(nameof(logger)); logger.LogInformation(VersionString); + + //attempt to restart the server if the configuration changes + ChangeToken.OnChange(configuration.GetReloadToken, () => serverControl.Restart()); applicationBuilder.UseDeveloperExceptionPage(); //it is not worth it to limit this, you should only ever get it if you're an authorized user From 6c743e06ee5ea95965990e77b0e89846d0c8822b Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 14:44:00 -0400 Subject: [PATCH 8/9] Remove unused using --- src/Tgstation.Server.Host/Core/Application.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index 17404a3033..fa6d8264aa 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -3,7 +3,6 @@ using Cyberboss.AspNetCore.AsyncInitializer; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; From e1fa1d3faf03d551f0e109cf8e25b39a86f1d936 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 15:07:14 -0400 Subject: [PATCH 9/9] Fix more null reattach info errors --- src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 4d36e949d0..6ce7108c42 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -650,7 +650,7 @@ namespace Tgstation.Server.Host.Components.Watchdog throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!"); var reattachInfo = doReattach ? await reattachInfoHandler.Load(cancellationToken).ConfigureAwait(false) : null; - var doesntNeedNewDmb = doReattach && reattachInfo.Alpha != null && reattachInfo.Bravo != null; + var doesntNeedNewDmb = doReattach && reattachInfo?.Alpha != null && reattachInfo?.Bravo != null; var dmbToUse = doesntNeedNewDmb ? null : dmbFactory.LockNextDmb(2); try @@ -705,7 +705,7 @@ namespace Tgstation.Server.Host.Components.Watchdog cancellationToken.ThrowIfCancellationRequested(); //both servers are now running, alpha is the active server, huzzah - AlphaIsActive = doReattach ? reattachInfo.AlphaIsActive : true; + AlphaIsActive = doReattach ? reattachInfo?.AlphaIsActive ?? true : true; LastLaunchResult = alphaLrt.Result; logger.LogInformation("Launched servers successfully"); Running = true;