From ad29d65cedd47334e05ecc537519c32f432309ba Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 14:55:45 -0400 Subject: [PATCH 01/26] Minor thing to make debugging with the host watchdog easier --- src/Tgstation.Server.Host.Watchdog/Watchdog.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs index bc81c91573..9a43ec441d 100644 --- a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs @@ -69,6 +69,9 @@ namespace Tgstation.Server.Host.Watchdog var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog +#if DEBUG + Directory.CreateDirectory(assemblyStoragePath); +#endif var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default")); #if DEBUG //just copy the shit where it belongs From 372cb75c285fff622ef58944fe23bd3aaf3605ba Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 16:06:13 -0400 Subject: [PATCH 02/26] More changes to make debugging Host Watchdog easier --- .../Properties/launchSettings.json | 1 + src/Tgstation.Server.Host.Watchdog/Watchdog.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host.Console/Properties/launchSettings.json b/src/Tgstation.Server.Host.Console/Properties/launchSettings.json index 5991274947..d8b2b440e6 100644 --- a/src/Tgstation.Server.Host.Console/Properties/launchSettings.json +++ b/src/Tgstation.Server.Host.Console/Properties/launchSettings.json @@ -10,6 +10,7 @@ "profiles": { "Tgstation.Server.Host.Console": { "commandName": "Project", + "commandLineArgs": "--attach-host-debugger", "workingDirectory": "bin\\Debug\\netcoreapp2.0", "launchBrowser": true, "environmentVariables": { diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs index 9a43ec441d..2f8905d7ad 100644 --- a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs @@ -124,7 +124,8 @@ namespace Tgstation.Server.Host.Watchdog '"' + assemblyPath + '"', updateDirectory }; - if (Debugger.IsAttached) + + if (Environment.GetCommandLineArgs().Any(x => x == "--attach-host-debugger")) arguments.Add("--attach-debugger"); arguments.AddRange(args); From 2f1a52d93156e3cec5a8d0f792eb22c456d1d53a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 16:06:49 -0400 Subject: [PATCH 03/26] Fixup instance watchdog handling --- .../Components/ReattachInfoHandler.cs | 16 +++++++++++++--- .../Components/Watchdog/SessionController.cs | 2 +- .../Components/Watchdog/Watchdog.cs | 12 ++++++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs index 0aba902d06..eac4b39a01 100644 --- a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs +++ b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs @@ -79,11 +79,21 @@ namespace Tgstation.Server.Host.Components { Models.WatchdogReattachInformation result = null; await databaseContextFactory.UseContext(async (db) => - result = await db.Instances.Where(x => x.Id == metadata.Id).Select(x => x.WatchdogReattachInformation).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false) - ).ConfigureAwait(false); + { + var instance = await db.Instances.Where(x => x.Id == metadata.Id) + .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha) + .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha) + .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + result = instance.WatchdogReattachInformation; + if (result == default) + return; + instance.WatchdogReattachInformation = null; + db.WatchdogReattachInformations.Remove(result); + await db.Save(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); if (result == default) - throw new JobException("Unable to load reattach information!"); + return null; var bravoDmbTask = dmbFactory.FromCompileJob(result.Bravo.CompileJob, cancellationToken); return new WatchdogReattachInformation(result, await dmbFactory.FromCompileJob(result.Alpha.CompileJob, cancellationToken).ConfigureAwait(false), await bravoDmbTask.ConfigureAwait(false)); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index 20b1115531..f017a51ed1 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -361,7 +361,7 @@ namespace Tgstation.Server.Host.Components.Watchdog reattachInformation.Dmb = null; released = true; Dispose(); - Dmb.KeepAlive(); + tmpProvider.KeepAlive(); reattachInformation.Dmb = tmpProvider; return reattachInformation; } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index e0f82236a9..b91af00859 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -625,12 +625,16 @@ namespace Tgstation.Server.Host.Components.Watchdog return null; } + var reattachInfoTask = doReattach ? reattachInfoHandler.Load(cancellationToken) : Task.FromResult(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); + { + var info = await reattachInfoTask.ConfigureAwait(false); + chatTask = chat.SendWatchdogMessage(info == null ? "Starting..." : "Reattaching...", cancellationToken); + } else chatTask = Task.CompletedTask; //start both servers @@ -641,7 +645,7 @@ namespace Tgstation.Server.Host.Components.Watchdog if (alphaServer != null || bravoServer != null) 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 reattachInfo = await reattachInfoTask.ConfigureAwait(false); var doesntNeedNewDmb = doReattach && reattachInfo.Alpha != null && reattachInfo.Bravo != null; var dmbToUse = doesntNeedNewDmb ? null : dmbFactory.LockNextDmb(2); @@ -798,6 +802,9 @@ namespace Tgstation.Server.Host.Components.Watchdog { if (releaseServers && Running) { + var chatTask = chat.SendWatchdogMessage("Detaching...", cancellationToken); + await StopMonitor().ConfigureAwait(false); + var reattachInformation = new WatchdogReattachInformation { AlphaIsActive = AlphaIsActive @@ -805,6 +812,7 @@ namespace Tgstation.Server.Host.Components.Watchdog reattachInformation.Alpha = alphaServer?.Release(); reattachInformation.Bravo = bravoServer?.Release(); await reattachInfoHandler.Save(reattachInformation, cancellationToken).ConfigureAwait(false); + await chatTask.ConfigureAwait(false); } await Terminate(false, cancellationToken).ConfigureAwait(false); } From 013a1c16fff175d9e124d0c8278d1d191d820355 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sat, 8 Sep 2018 16:45:13 -0400 Subject: [PATCH 04/26] Prevent some null reference exceptions --- src/Tgstation.Server.Host/Components/Instance.cs | 5 ++++- .../Controllers/DreamMakerController.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 080c469aab..d3faceb58c 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -184,7 +185,9 @@ namespace Tgstation.Server.Host.Components Instance = new Models.Instance { Id = metadata.Id - } + }, + ActiveTestMerges = new List(), + CompileJobs = new List() }; db.Instances.Attach(revInfo.Instance); } diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs index 9341fd8dd3..ace66c7692 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; @@ -177,7 +178,9 @@ namespace Tgstation.Server.Host.Controllers Instance = new Models.Instance { Id = Instance.Id - } + }, + ActiveTestMerges = new List(), + CompileJobs = new List() }; databaseContext.Instances.Attach(revInfo.Instance); } From c52b528a42d9835a05b18130e5153fbeab617daf Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 24 Sep 2018 22:37:35 -0400 Subject: [PATCH 05/26] Minor merge fixups --- src/Tgstation.Server.Host.Watchdog/Watchdog.cs | 8 ++++---- src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs index 8038984135..c6292a3641 100644 --- a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs @@ -69,13 +69,13 @@ namespace Tgstation.Server.Host.Watchdog } logger.LogInformation("Detected dotnet executable at {0}", dotnetPath); - var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog + var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog #if DEBUG - Directory.CreateDirectory(assemblyStoragePath); + Directory.CreateDirectory(assemblyStoragePath); #endif - var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default")); + var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default")); #if DEBUG //just copy the shit where it belongs Directory.Delete(assemblyStoragePath, true); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index b97eafd733..229468eec2 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -663,7 +663,7 @@ namespace Tgstation.Server.Host.Components.Watchdog throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!"); var reattachInfo = await reattachInfoTask.ConfigureAwait(false); - 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 From 4b6fa28e4fb0e119c5e825056b011ea34a1e064b Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 19:01:54 -0400 Subject: [PATCH 06/26] Fix clearing out pre-existing reattach information --- src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs index c4f61071a3..2e8fe33444 100644 --- a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs +++ b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Tgstation.Server.Host.Components.Compiler; using Tgstation.Server.Host.Components.Watchdog; using Tgstation.Server.Host.Core; +using Z.EntityFramework.Plus; namespace Tgstation.Server.Host.Components { @@ -56,6 +57,8 @@ namespace Tgstation.Server.Host.Components logger.LogDebug("Saving reattach information: {0}...", reattachInformation); + var deleteTask = db.WatchdogReattachInformations.Where(x => x.InstanceId == metadata.Id).DeleteAsync(cancellationToken); + var instance = new Models.Instance { Id = metadata.Id }; db.Instances.Attach(instance); @@ -84,6 +87,7 @@ namespace Tgstation.Server.Host.Components Bravo = ConvertReattachInfo(reattachInformation.Bravo), AlphaIsActive = reattachInformation.AlphaIsActive, }; + await deleteTask.ConfigureAwait(false); await db.Save(cancellationToken).ConfigureAwait(false); }); From 0e5701d12a25b47aa134676e5e0d89087ee7287c Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 19:08:34 -0400 Subject: [PATCH 07/26] Fix reattaches not happening if autostart is false --- .../Components/Watchdog/Watchdog.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index ff5d10ee66..b141505e7e 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -448,7 +448,7 @@ namespace Tgstation.Server.Host.Components.Watchdog monitorState.NextAction = MonitorAction.Continue; break; case MonitorActivationReason.NewDmbAvailable: - monitorState.InactiveServerHasStagedDmb = true; + monitorState.InactiveServerHasStagedDmb = true; goto case MonitorActivationReason.ActiveLaunchParametersUpdated; case MonitorActivationReason.ActiveLaunchParametersUpdated: await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); @@ -573,7 +573,7 @@ namespace Tgstation.Server.Host.Components.Watchdog WatchdogLaunchResult result; using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { - result = await LaunchNoLock(false, false, false, cancellationToken).ConfigureAwait(false); + result = await LaunchNoLock(false, false, null, cancellationToken).ConfigureAwait(false); if (Running) { logger.LogDebug("Relaunch successful, resetting monitor state..."); @@ -633,7 +633,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } } - async Task LaunchNoLock(bool startMonitor, bool announce, bool doReattach, CancellationToken cancellationToken) + async Task LaunchNoLock(bool startMonitor, bool announce, WatchdogReattachInformation reattachInfo, CancellationToken cancellationToken) { logger.LogTrace("Begin LaunchNoLock"); using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) @@ -644,16 +644,12 @@ namespace Tgstation.Server.Host.Components.Watchdog return null; } - var reattachInfoTask = doReattach ? reattachInfoHandler.Load(cancellationToken) : Task.FromResult(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) - { - var info = await reattachInfoTask.ConfigureAwait(false); - chatTask = chat.SendWatchdogMessage(info == null ? "Starting..." : "Reattaching...", cancellationToken); - } + chatTask = chat.SendWatchdogMessage(reattachInfo == null ? "Starting..." : "Reattaching...", cancellationToken); else chatTask = Task.CompletedTask; //start both servers @@ -664,8 +660,7 @@ namespace Tgstation.Server.Host.Components.Watchdog if (alphaServer != null || bravoServer != null) throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!"); - var reattachInfo = await reattachInfoTask.ConfigureAwait(false); - var doesntNeedNewDmb = doReattach && reattachInfo?.Alpha != null && reattachInfo?.Bravo != null; + var doesntNeedNewDmb = reattachInfo?.Alpha != null && reattachInfo?.Bravo != null; var dmbToUse = doesntNeedNewDmb ? null : dmbFactory.LockNextDmb(2); try @@ -720,7 +715,7 @@ namespace Tgstation.Server.Host.Components.Watchdog cancellationToken.ThrowIfCancellationRequested(); //both servers are now running, alpha is the active server(unless reattach), huzzah - AlphaIsActive = doReattach ? reattachInfo?.AlphaIsActive ?? true : true; + AlphaIsActive = reattachInfo?.AlphaIsActive ?? true; LastLaunchResult = alphaLrt.Result; var activeServer = AlphaIsActive ? alphaServer : bravoServer; @@ -779,7 +774,7 @@ namespace Tgstation.Server.Host.Components.Watchdog public async Task Launch(CancellationToken cancellationToken) { using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) - return await LaunchNoLock(true, true, false, cancellationToken).ConfigureAwait(false); + return await LaunchNoLock(true, true, null, cancellationToken).ConfigureAwait(false); } /// @@ -811,7 +806,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } else chatTask = Task.CompletedTask; - var result = await LaunchNoLock(true, !Running, false, cancellationToken).ConfigureAwait(false); + var result = await LaunchNoLock(true, !Running, null, cancellationToken).ConfigureAwait(false); await chatTask.ConfigureAwait(false); return result; } @@ -835,7 +830,8 @@ namespace Tgstation.Server.Host.Components.Watchdog /// public async Task StartAsync(CancellationToken cancellationToken) { - if (!autoStart) + var reattachInfo = await reattachInfoHandler.Load(cancellationToken).ConfigureAwait(false); + if (!autoStart && reattachInfo == null) return; long? adminUserId = null; @@ -855,7 +851,11 @@ namespace Tgstation.Server.Host.Components.Watchdog CancelRight = (ulong)DreamDaemonRights.Shutdown, CancelRightsType = RightsType.DreamDaemon }; - await jobManager.RegisterOperation(job, (j, databaseContext, progressFunction, ct) => Launch(ct), cancellationToken).ConfigureAwait(false); + await jobManager.RegisterOperation(job, async (j, databaseContext, progressFunction, ct) => + { + using (await SemaphoreSlimContext.Lock(semaphore, ct).ConfigureAwait(false)) + await LaunchNoLock(true, true, reattachInfo, ct).ConfigureAwait(false); + }, cancellationToken).ConfigureAwait(false); } /// From 3e5a2c81672f4e1f0e0dd2c72b3638b7912e5578 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 19:21:21 -0400 Subject: [PATCH 08/26] Fix not retrieving bravo server reattach info --- src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs index 2e8fe33444..500983d631 100644 --- a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs +++ b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs @@ -99,7 +99,7 @@ namespace Tgstation.Server.Host.Components { var instance = await db.Instances.Where(x => x.Id == metadata.Id) .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha) - .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha) + .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Bravo) .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); result = instance.WatchdogReattachInformation; if (result == default) From e7c9762d748107c3193b082aa91f40d19a07b164 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 19:27:12 -0400 Subject: [PATCH 09/26] Add missing ArgumentNullException --- src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs index 1c3a909b98..8b830e8fa7 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs @@ -186,6 +186,8 @@ namespace Tgstation.Server.Host.Components.Compiler /// public async Task FromCompileJob(CompileJob compileJob, CancellationToken cancellationToken) { + if (compileJob == null) + throw new ArgumentNullException(nameof(compileJob)); logger.LogTrace("Loading compile job {0}...", compileJob.Id); var providerSubmitted = false; var newProvider = new DmbProvider(compileJob, ioManager, () => From 961301b55380dde433df55d4f7cdadaed6e1388d Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 19:27:52 -0400 Subject: [PATCH 10/26] Add missing compile job metadata from reattach info query --- src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs index 500983d631..6c666e2a7f 100644 --- a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs +++ b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs @@ -98,8 +98,8 @@ namespace Tgstation.Server.Host.Components await databaseContextFactory.UseContext(async (db) => { var instance = await db.Instances.Where(x => x.Id == metadata.Id) - .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha) - .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Bravo) + .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Alpha).ThenInclude(x => x.CompileJob) + .Include(x => x.WatchdogReattachInformation).ThenInclude(x => x.Bravo).ThenInclude(x => x.CompileJob) .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); result = instance.WatchdogReattachInformation; if (result == default) From ac6290748fef4dce1c8957287085132a3ebaa831 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 19:29:17 -0400 Subject: [PATCH 11/26] Mark CompileJobs as Required in the ReattachInformation model --- src/Tgstation.Server.Host/Models/ReattachInformation.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Models/ReattachInformation.cs b/src/Tgstation.Server.Host/Models/ReattachInformation.cs index acabe246ce..d2fdda3374 100644 --- a/src/Tgstation.Server.Host/Models/ReattachInformation.cs +++ b/src/Tgstation.Server.Host/Models/ReattachInformation.cs @@ -1,4 +1,6 @@ -namespace Tgstation.Server.Host.Models +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Host.Models { /// /// Database representation of @@ -13,6 +15,7 @@ /// /// The for the /// + [Required] public CompileJob CompileJob { get; set; } } } From 021f6e6f0f43042020b49ebcbfc714358771e761 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 21:57:28 -0400 Subject: [PATCH 12/26] Fix detaching message showing twice --- src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index b141505e7e..6799fafd80 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -863,7 +863,6 @@ namespace Tgstation.Server.Host.Components.Watchdog { if (releaseServers && Running) { - var chatTask = chat.SendWatchdogMessage("Detaching...", cancellationToken); await StopMonitor().ConfigureAwait(false); var reattachInformation = new WatchdogReattachInformation @@ -873,7 +872,6 @@ namespace Tgstation.Server.Host.Components.Watchdog reattachInformation.Alpha = alphaServer?.Release(); reattachInformation.Bravo = bravoServer?.Release(); await reattachInfoHandler.Save(reattachInformation, cancellationToken).ConfigureAwait(false); - await chatTask.ConfigureAwait(false); } await Terminate(false, cancellationToken).ConfigureAwait(false); } From 2f0f7dea619f7f7654a1cbcb0de165737d6e76d9 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 21:57:39 -0400 Subject: [PATCH 13/26] Fix possible double enumeration --- src/Tgstation.Server.Host/Server.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Server.cs b/src/Tgstation.Server.Host/Server.cs index 18a5749478..6f7dafa4c9 100644 --- a/src/Tgstation.Server.Host/Server.cs +++ b/src/Tgstation.Server.Host/Server.cs @@ -177,7 +177,7 @@ namespace Tgstation.Server.Host using (var cts = new CancellationTokenSource()) { var cancellationToken = cts.Token; - var eventsTask = Task.WhenAll(restartHandlers.Select(x => x.HandleRestart(newVersion, cancellationToken))); + 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); From 1a0d8754d08ca4e1d985780497aea8f9c0da2d97 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 22:00:19 -0400 Subject: [PATCH 14/26] Remove superfluous messages when attaching to a process --- src/Tgstation.Server.Host/Core/Process.cs | 9 ++++++++- src/Tgstation.Server.Host/Core/ProcessExecutor.cs | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Tgstation.Server.Host/Core/Process.cs b/src/Tgstation.Server.Host/Core/Process.cs index 7ea9434c74..10e9712d39 100644 --- a/src/Tgstation.Server.Host/Core/Process.cs +++ b/src/Tgstation.Server.Host/Core/Process.cs @@ -28,7 +28,7 @@ namespace Tgstation.Server.Host.Core /// readonly ILogger logger; - public Process(System.Diagnostics.Process handle, Task lifetime, StringBuilder outputStringBuilder, StringBuilder errorStringBuilder, StringBuilder combinedStringBuilder, ILogger logger) + public Process(System.Diagnostics.Process handle, Task lifetime, StringBuilder outputStringBuilder, StringBuilder errorStringBuilder, StringBuilder combinedStringBuilder, ILogger logger, bool preExisting) { this.handle = handle ?? throw new ArgumentNullException(nameof(handle)); Lifetime = lifetime ?? throw new ArgumentNullException(nameof(lifetime)); @@ -40,6 +40,13 @@ namespace Tgstation.Server.Host.Core this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); Id = handle.Id; + + if (preExisting) + { + Startup = Task.CompletedTask; + return; + } + Startup = Task.Factory.StartNew(() => { try diff --git a/src/Tgstation.Server.Host/Core/ProcessExecutor.cs b/src/Tgstation.Server.Host/Core/ProcessExecutor.cs index 5aad8a9e8b..f1fc9e80bf 100644 --- a/src/Tgstation.Server.Host/Core/ProcessExecutor.cs +++ b/src/Tgstation.Server.Host/Core/ProcessExecutor.cs @@ -61,7 +61,7 @@ namespace Tgstation.Server.Host.Core var handle = System.Diagnostics.Process.GetProcessById(id); try { - return new Process(handle, AttachExitHandler(handle), null, null, null, loggerFactory.CreateLogger()); + return new Process(handle, AttachExitHandler(handle), null, null, null, loggerFactory.CreateLogger(), true); } catch { @@ -129,7 +129,7 @@ namespace Tgstation.Server.Host.Core } catch (InvalidOperationException) { } - return new Process(handle, lifetimeTask, outputStringBuilder, errorStringBuilder, combinedStringBuilder, loggerFactory.CreateLogger()); + return new Process(handle, lifetimeTask, outputStringBuilder, errorStringBuilder, combinedStringBuilder, loggerFactory.CreateLogger(), false); } catch { From 5590c464c1597eec800ac551279e64245776df70 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 22:17:34 -0400 Subject: [PATCH 15/26] IProcessExecutor returns null on failure to GetProcess() --- src/Tgstation.Server.Host/Core/IProcessExecutor.cs | 2 +- src/Tgstation.Server.Host/Core/ProcessExecutor.cs | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Core/IProcessExecutor.cs b/src/Tgstation.Server.Host/Core/IProcessExecutor.cs index acaf145cd4..3bd993ada9 100644 --- a/src/Tgstation.Server.Host/Core/IProcessExecutor.cs +++ b/src/Tgstation.Server.Host/Core/IProcessExecutor.cs @@ -21,7 +21,7 @@ /// Get a by /// /// The - /// The represented by + /// The represented by on success, on failure IProcess GetProcess(int id); } } diff --git a/src/Tgstation.Server.Host/Core/ProcessExecutor.cs b/src/Tgstation.Server.Host/Core/ProcessExecutor.cs index f1fc9e80bf..defa3db5db 100644 --- a/src/Tgstation.Server.Host/Core/ProcessExecutor.cs +++ b/src/Tgstation.Server.Host/Core/ProcessExecutor.cs @@ -58,7 +58,16 @@ namespace Tgstation.Server.Host.Core public IProcess GetProcess(int id) { logger.LogDebug("Attaching to process {0}...", id); - var handle = System.Diagnostics.Process.GetProcessById(id); + System.Diagnostics.Process handle; + try + { + handle = System.Diagnostics.Process.GetProcessById(id); + } + catch(Exception e) + { + logger.LogWarning("Unable to get process {0}! Exception: {1}", id, e); + return null; + } try { return new Process(handle, AttachExitHandler(handle), null, null, null, loggerFactory.CreateLogger(), true); From b83913a4d7c31540f92123905e6c1f6ed528a615 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 22:17:52 -0400 Subject: [PATCH 16/26] ISessionController returns null on failure to Reattach() --- .../Watchdog/ISessionControllerFactory.cs | 2 +- .../Watchdog/SessionControllerFactory.cs | 42 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs index 4873ce5b21..7b20cd1313 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs @@ -28,7 +28,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// The to use /// The for the operation - /// A resulting in a new + /// A resulting in a new on success or on failure to reattach Task Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index e8c9ad18eb..5e81afdb6e 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -268,6 +268,7 @@ namespace Tgstation.Server.Host.Components.Watchdog if (reattachInformation == null) throw new ArgumentNullException(nameof(reattachInformation)); + SessionController result = null; var basePath = reattachInformation.IsPrimary ? reattachInformation.Dmb.PrimaryDirectory : reattachInformation.Dmb.SecondaryDirectory; var chatJsonTrackingContext = await chat.TrackJsons(basePath, reattachInformation.ChatChannelsJson, reattachInformation.ChatCommandsJson, cancellationToken).ConfigureAwait(false); try @@ -279,34 +280,37 @@ namespace Tgstation.Server.Host.Components.Watchdog try { var process = processExecutor.GetProcess(reattachInformation.ProcessId); - try - { - networkPromptReaper.RegisterProcess(process); - return new SessionController(reattachInformation, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger(), null, null); - } - catch - { - process.Dispose(); - throw; - } + + if (process != null) + try + { + networkPromptReaper.RegisterProcess(process); + result = new SessionController(reattachInformation, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger(), null, null); + } + finally + { + if (result == null) + process.Dispose(); + } } - catch + finally { - context.Dispose(); - throw; + if (result == null) + context.Dispose(); } } - catch + finally { - byondLock.Dispose(); - throw; + if (result == null) + byondLock.Dispose(); } } - catch + finally { - chatJsonTrackingContext.Dispose(); - throw; + if (result == null) + chatJsonTrackingContext.Dispose(); } + return result; } } } From 3411f3c4fb4009c7baf3ce069873c1b19b48c3a1 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 22:18:34 -0400 Subject: [PATCH 17/26] Remove watchdog startup delay on reattaches --- src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 6799fafd80..9bbe5185ff 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -680,7 +680,7 @@ namespace Tgstation.Server.Host.Components.Watchdog var now = DateTimeOffset.Now; var delay = now - startTime; - if (delay.TotalSeconds < AlphaBravoStartupSeperationInterval) + if (reattachInfo == null && delay.TotalSeconds < AlphaBravoStartupSeperationInterval) await Task.Delay(startTime.AddSeconds(AlphaBravoStartupSeperationInterval) - now, cancellationToken).ConfigureAwait(false); Task bravoServerTask; From 6c5f00dffa8fff6ce09da562d8689696fe6f37ef Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 22:28:31 -0400 Subject: [PATCH 18/26] Remove WatchdogLaunchResult --- .../Components/Watchdog/IWatchdog.cs | 13 ++--- .../Components/Watchdog/Watchdog.cs | 52 ++++++++----------- .../Watchdog/WatchdogLaunchResult.cs | 20 ------- .../Controllers/DreamDaemonController.cs | 9 +--- 4 files changed, 28 insertions(+), 66 deletions(-) delete mode 100644 src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs index 2002597284..926d5239ba 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs @@ -26,11 +26,6 @@ namespace Tgstation.Server.Host.Components.Watchdog /// Models.CompileJob ActiveCompileJob { get; } - /// - /// The latest of the twin servers - /// - LaunchResult LastLaunchResult { get; } - /// /// The the active server is using /// @@ -50,8 +45,8 @@ namespace Tgstation.Server.Host.Components.Watchdog /// Start the /// /// The for the operation - /// A resulting in the or if it was already running - Task Launch(CancellationToken cancellationToken); + /// A representing the running operation + Task Launch(CancellationToken cancellationToken); /// /// Changes the . If currently triggers a graceful restart @@ -66,8 +61,8 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// If the restart will be delayed until a reboot is detected in the active server's DMAPI and this function will retrun immediately /// The for the operation - /// A resulting in the or if it was already running or is and is - Task Restart(bool graceful, CancellationToken cancellationToken); + /// A representing the running operation + Task Restart(bool graceful, CancellationToken cancellationToken); /// /// Stops the watchdog diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 9bbe5185ff..308760a9ec 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -36,9 +36,6 @@ namespace Tgstation.Server.Host.Components.Watchdog /// public Models.CompileJob ActiveCompileJob => (AlphaIsActive ? alphaServer : bravoServer)?.Dmb.CompileJob; - /// - public LaunchResult LastLaunchResult { get; private set; } - /// public DreamDaemonLaunchParameters ActiveLaunchParameters { get; private set; } @@ -109,7 +106,7 @@ namespace Tgstation.Server.Host.Components.Watchdog readonly Api.Models.Instance instance; /// - /// If the should in + /// If the should in /// readonly bool autoStart; @@ -570,21 +567,29 @@ namespace Tgstation.Server.Host.Components.Watchdog for (var retryAttempts = 1; monitorState.NextAction == MonitorAction.Restart; ++retryAttempts) { - WatchdogLaunchResult result; + Exception launchException = null; using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) - { - result = await LaunchNoLock(false, false, null, cancellationToken).ConfigureAwait(false); - if (Running) + try { - logger.LogDebug("Relaunch successful, resetting monitor state..."); - monitorState = new MonitorState(); //clean the slate + await LaunchNoLock(false, false, null, cancellationToken).ConfigureAwait(false); + if (Running) + { + logger.LogDebug("Relaunch successful, resetting monitor state..."); + monitorState = new MonitorState(); //clean the slate + } + } + catch (Exception e) + { + launchException = e; } - } await chatTask.ConfigureAwait(false); if (!Running) { - logger.LogWarning("Failed to automatically restart the watchdog! Alpha: {0}; Bravo: {1}", result.Alpha.ToString(), result.Bravo.ToString()); + if (launchException == null) + logger.LogWarning("Failed to automatically restart the watchdog!"); + else + logger.LogWarning("Failed to automatically restart the watchdog! Exception: {0}", launchException); 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); @@ -633,16 +638,13 @@ namespace Tgstation.Server.Host.Components.Watchdog } } - async Task LaunchNoLock(bool startMonitor, bool announce, WatchdogReattachInformation reattachInfo, CancellationToken cancellationToken) + async Task LaunchNoLock(bool startMonitor, bool announce, WatchdogReattachInformation reattachInfo, CancellationToken cancellationToken) { logger.LogTrace("Begin LaunchNoLock"); using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { if (Running) - { - logger.LogTrace("Aborted due to already running!"); - return null; - } + throw new JobException("Watchdog already running!"); Task chatTask; //this is necessary, the monitor could be in it's sleep loop trying to restart @@ -716,7 +718,6 @@ namespace Tgstation.Server.Host.Components.Watchdog //both servers are now running, alpha is the active server(unless reattach), huzzah AlphaIsActive = reattachInfo?.AlphaIsActive ?? true; - LastLaunchResult = alphaLrt.Result; var activeServer = AlphaIsActive ? alphaServer : bravoServer; activeServer.EnableCustomChatCommands(); @@ -730,11 +731,6 @@ namespace Tgstation.Server.Host.Components.Watchdog monitorCts = new CancellationTokenSource(); monitorTask = MonitorLifetimes(monitorCts.Token); } - return new WatchdogLaunchResult - { - Alpha = alphaLrt.Result, - Bravo = bravoLrt.Result - }; } catch { @@ -771,10 +767,10 @@ namespace Tgstation.Server.Host.Components.Watchdog } /// - public async Task Launch(CancellationToken cancellationToken) + public async Task Launch(CancellationToken cancellationToken) { using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) - return await LaunchNoLock(true, true, null, cancellationToken).ConfigureAwait(false); + await LaunchNoLock(true, true, null, cancellationToken).ConfigureAwait(false); } /// @@ -791,7 +787,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } /// - public async Task Restart(bool graceful, CancellationToken cancellationToken) + public async Task Restart(bool graceful, CancellationToken cancellationToken) { logger.LogTrace("Begin Restart. Graceful: {0}", graceful); using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) @@ -806,9 +802,8 @@ namespace Tgstation.Server.Host.Components.Watchdog } else chatTask = Task.CompletedTask; - var result = await LaunchNoLock(true, !Running, null, cancellationToken).ConfigureAwait(false); + await LaunchNoLock(true, !Running, null, cancellationToken).ConfigureAwait(false); await chatTask.ConfigureAwait(false); - return result; } var toReboot = AlphaIsActive ? alphaServer : bravoServer; if (toReboot != null) @@ -816,7 +811,6 @@ namespace Tgstation.Server.Host.Components.Watchdog if (!await toReboot.SetRebootState(Components.Watchdog.RebootState.Restart, cancellationToken).ConfigureAwait(false)) logger.LogWarning("Unable to send reboot state change event!"); } - return null; } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs deleted file mode 100644 index 0532326e78..0000000000 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Tgstation.Server.Host.Components.Watchdog -{ - /// - /// Launch results for a - /// - public sealed class WatchdogLaunchResult - { - /// - /// The for the alpha process - /// - public LaunchResult Alpha { get; set; } - - /// - /// The for the bravo process - /// - public LaunchResult Bravo { get; set; } - } -} diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index af3bb8cc15..ed43af0cf2 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -66,14 +66,7 @@ namespace Tgstation.Server.Host.Controllers Instance = Instance, StartedBy = AuthenticationContext.User }; - await jobManager.RegisterOperation(job, - async (paramJob, databaseContext, progressHandler, innerCt) => - { - var result = await instance.Watchdog.Launch(innerCt).ConfigureAwait(false); - if (result == null) - throw new JobException("Watchdog already running!"); - }, - cancellationToken).ConfigureAwait(false); + await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressHandler, innerCt) => instance.Watchdog.Launch(innerCt), cancellationToken).ConfigureAwait(false); return Accepted(job.ToApi()); } From ab6aeafee28ea72074ac5cdfb7bd65c98b14de00 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 23:01:44 -0400 Subject: [PATCH 19/26] Reduce a log level --- src/Tgstation.Server.Host/Core/ProcessExecutor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Core/ProcessExecutor.cs b/src/Tgstation.Server.Host/Core/ProcessExecutor.cs index defa3db5db..2786af6c83 100644 --- a/src/Tgstation.Server.Host/Core/ProcessExecutor.cs +++ b/src/Tgstation.Server.Host/Core/ProcessExecutor.cs @@ -65,7 +65,7 @@ namespace Tgstation.Server.Host.Core } catch(Exception e) { - logger.LogWarning("Unable to get process {0}! Exception: {1}", id, e); + logger.LogDebug("Unable to get process {0}! Exception: {1}", id, e); return null; } try From e0a13075adf677e47bedf61372191aa72506581e Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 23:10:15 -0400 Subject: [PATCH 20/26] Fix unecessarily writing out channels jsons --- src/Tgstation.Server.Host/Components/Chat/Chat.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Chat/Chat.cs b/src/Tgstation.Server.Host/Components/Chat/Chat.cs index e11d311609..191988addc 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Chat.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Chat.cs @@ -546,7 +546,7 @@ namespace Tgstation.Server.Host.Components.Chat { trackingContexts.Add(context); lock (mappedChannels) - task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken))); + task = context.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken); } await task.ConfigureAwait(false); return context; From ca0b29c6ec465adb040de9ecca16d746cd9c701c Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 23:20:00 -0400 Subject: [PATCH 21/26] Adds DeadSessionController --- .../Watchdog/DeadSessionController.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs diff --git a/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs new file mode 100644 index 0000000000..cccd429723 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs @@ -0,0 +1,99 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Implements a fake "dead" + /// + sealed class DeadSessionController : ISessionController + { + /// + public Task LaunchResult { get; } + + /// + public bool IsPrimary => false; + + /// + public bool TerminationWasRequested => true; + + /// + public ApiValidationStatus ApiValidationStatus => throw new NotSupportedException(); + + /// + public IDmbProvider Dmb { get; } + + /// + public ushort? Port => null; + + /// + public bool ClosePortOnReboot + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + /// + public RebootState RebootState => throw new NotSupportedException(); + + /// + public Task OnReboot { get; } + + /// + public Task Lifetime { get; } + + /// + /// If the was d + /// + bool disposed; + + /// + /// Construct a + /// + /// The value of + public DeadSessionController(IDmbProvider dmbProvider) + { + Dmb = dmbProvider ?? throw new ArgumentNullException(nameof(dmbProvider)); + LaunchResult = Task.FromResult(new LaunchResult + { + StartupTime = TimeSpan.FromSeconds(0) + }); + Lifetime = Task.FromResult(-1); + OnReboot = new TaskCompletionSource().Task; + } + + /// + public void Dispose() + { + lock (this) + { + if (disposed) + return; + disposed = true; + } + Dmb.Dispose(); + } + + /// + public void EnableCustomChatCommands() => throw new NotSupportedException(); + + /// + public ReattachInformation Release() => throw new NotSupportedException(); + + /// + public void ResetRebootState() => throw new NotSupportedException(); + + /// + public Task SendCommand(string command, CancellationToken cancellationToken) => throw new NotSupportedException(); + + /// + public void SetHighPriority() => throw new NotSupportedException(); + + /// + public Task SetPort(ushort newPort, CancellationToken cancellatonToken) => throw new NotSupportedException(); + + /// + public Task SetRebootState(RebootState newRebootState, CancellationToken cancellationToken) => throw new NotSupportedException(); + } +} From 589934f902d161a8bfeebb7801df200c6f88e1b1 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 23:20:19 -0400 Subject: [PATCH 22/26] Allow SessionControllerFactory to create DeadSessionControllers --- .../Components/Watchdog/ISessionControllerFactory.cs | 7 +++++++ .../Components/Watchdog/SessionControllerFactory.cs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs index 7b20cd1313..1bad93d59a 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs @@ -30,5 +30,12 @@ namespace Tgstation.Server.Host.Components.Watchdog /// The for the operation /// A resulting in a new on success or on failure to reattach Task Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken); + + /// + /// Creates a that appears to have started and died with exit code -1 + /// + /// The for the + /// A dead + ISessionController CreateDeadSession(IDmbProvider dmbProvider); } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index 5e81afdb6e..63ee94c543 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -312,5 +312,8 @@ namespace Tgstation.Server.Host.Components.Watchdog } return result; } + + /// + public ISessionController CreateDeadSession(IDmbProvider dmbProvider) => new DeadSessionController(dmbProvider); } } From 37a0859d70d0c8a187ce55d89167407855a0e74a Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 23:22:59 -0400 Subject: [PATCH 23/26] Return false as a get value for ClosePortOnReboot for DeadSessionController --- .../Components/Watchdog/DeadSessionController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs index cccd429723..112cd397d5 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs @@ -30,7 +30,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// public bool ClosePortOnReboot { - get => throw new NotSupportedException(); + get => false; set => throw new NotSupportedException(); } From 9c6d083b6666f504a8baa33eec102d757572874e Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 23:27:50 -0400 Subject: [PATCH 24/26] We did not request the termination of a DeadSessionController --- .../Components/Watchdog/DeadSessionController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs index 112cd397d5..c310f18d65 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/DeadSessionController.cs @@ -16,7 +16,7 @@ namespace Tgstation.Server.Host.Components.Watchdog public bool IsPrimary => false; /// - public bool TerminationWasRequested => true; + public bool TerminationWasRequested => false; /// public ApiValidationStatus ApiValidationStatus => throw new NotSupportedException(); From 0f6894ad21515383098c8cfc51e0f4f2e778e339 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 23:30:09 -0400 Subject: [PATCH 25/26] Various things Properly handle 1 server crash scenarios for reattaching Call SetHighPriority on rebooted servers LaunchNoLock => LaunchImplNoLock Fix sending extra watchdog messages --- .../Components/Watchdog/Watchdog.cs | 67 ++++++++++++++----- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 308760a9ec..7a8b9e6de3 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -106,7 +106,7 @@ namespace Tgstation.Server.Host.Components.Watchdog readonly Api.Models.Instance instance; /// - /// If the should in + /// If the should in /// readonly bool autoStart; @@ -285,6 +285,7 @@ namespace Tgstation.Server.Host.Components.Watchdog try { monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, newDmb, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false); + monitorState.InactiveServer.SetHighPriority(); usedMostRecentDmb = true; } catch (OperationCanceledException) @@ -307,6 +308,7 @@ namespace Tgstation.Server.Host.Components.Watchdog throw new JobException("Creating backup DMB provider failed!"); monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbBackup, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false); + monitorState.InactiveServer.SetHighPriority(); usedMostRecentDmb = false; await chat.SendWatchdogMessage("Staging newest DMB on inactive server failed: {0} Falling back to previous dmb...", cancellationToken).ConfigureAwait(false); } @@ -571,7 +573,7 @@ namespace Tgstation.Server.Host.Components.Watchdog using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) try { - await LaunchNoLock(false, false, null, cancellationToken).ConfigureAwait(false); + await LaunchImplNoLock(false, false, null, cancellationToken).ConfigureAwait(false); if (Running) { logger.LogDebug("Relaunch successful, resetting monitor state..."); @@ -638,7 +640,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } } - async Task LaunchNoLock(bool startMonitor, bool announce, WatchdogReattachInformation reattachInfo, CancellationToken cancellationToken) + async Task LaunchImplNoLock(bool startMonitor, bool announce, WatchdogReattachInformation reattachInfo, CancellationToken cancellationToken) { logger.LogTrace("Begin LaunchNoLock"); using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) @@ -656,6 +658,7 @@ namespace Tgstation.Server.Host.Components.Watchdog chatTask = Task.CompletedTask; //start both servers LastLaunchParameters = ActiveLaunchParameters; + var ignoreNestedException = false; try { //good ole sanity @@ -676,7 +679,7 @@ namespace Tgstation.Server.Host.Components.Watchdog //wait until this boy officially starts so as not to confuse the servers as to who came first var startTime = DateTimeOffset.Now; alphaServer = await alphaServerTask.ConfigureAwait(false); - alphaServer.SetHighPriority(); + alphaServer?.SetHighPriority(); //extra delay for total ordering var now = DateTimeOffset.Now; @@ -692,7 +695,38 @@ namespace Tgstation.Server.Host.Components.Watchdog bravoServerTask = sessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken); bravoServer = await bravoServerTask.ConfigureAwait(false); - bravoServer.SetHighPriority(); + bravoServer?.SetHighPriority(); + + //possiblity of null servers due to failed reattaches + if (alphaServer == null || bravoServer == null) + { + await chatTask.ConfigureAwait(false); + var bothServersDead = alphaServer == null && bravoServer == null; + if (bothServersDead + || (alphaServer == null && reattachInfo.AlphaIsActive) + || (bravoServer == null && !reattachInfo.AlphaIsActive)) + { + //we lost the active server, just restart + DisposeAndNullControllers(); + const string FailReattachMessage = "Unable to properly reattach to active server! Restarting..."; + logger.LogWarning(FailReattachMessage); + logger.LogDebug(bothServersDead ? "Also could not reattach to inactive server!" : "Inactive server was reattached successfully!"); + chatTask = chat.SendWatchdogMessage(FailReattachMessage, cancellationToken); + ignoreNestedException = true; + await LaunchImplNoLock(true, false, null, cancellationToken).ConfigureAwait(false); + return; + } + + const string InactiveReattachFailureMessage = "Unable to reattach to inactive server. Leaving for monitor to reboot..."; + chatTask = chat.SendWatchdogMessage(InactiveReattachFailureMessage, cancellationToken); + logger.LogWarning(InactiveReattachFailureMessage); + + //we still have the active server but the other one is dead to us, hand it off to the monitor to restart + if (reattachInfo.AlphaIsActive) + bravoServer = sessionControllerFactory.CreateDeadSession(reattachInfo.Bravo.Dmb); + else + alphaServer = sessionControllerFactory.CreateDeadSession(reattachInfo.Alpha.Dmb); + } async Task CheckLaunch(ISessionController controller, string serverName) { @@ -745,14 +779,17 @@ namespace Tgstation.Server.Host.Components.Watchdog } catch (Exception e) { - var originalChatTask = chatTask; - async Task ChainChatTaskWithErrorMessage() + if (!ignoreNestedException && !cancellationToken.IsCancellationRequested) { - await originalChatTask.ConfigureAwait(false); - await chat.SendWatchdogMessage("Startup failed!", cancellationToken).ConfigureAwait(false); + var originalChatTask = chatTask; + async Task ChainChatTaskWithErrorMessage() + { + await originalChatTask.ConfigureAwait(false); + await chat.SendWatchdogMessage("Startup failed!", cancellationToken).ConfigureAwait(false); + } + chatTask = ChainChatTaskWithErrorMessage(); + logger.LogWarning("Failed to start watchdog: {0}", e.ToString()); } - chatTask = ChainChatTaskWithErrorMessage(); - logger.LogWarning("Failed to start watchdog: {0}", e.ToString()); throw; } finally @@ -770,7 +807,7 @@ namespace Tgstation.Server.Host.Components.Watchdog public async Task Launch(CancellationToken cancellationToken) { using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) - await LaunchNoLock(true, true, null, cancellationToken).ConfigureAwait(false); + await LaunchImplNoLock(true, true, null, cancellationToken).ConfigureAwait(false); } /// @@ -802,7 +839,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } else chatTask = Task.CompletedTask; - await LaunchNoLock(true, !Running, null, cancellationToken).ConfigureAwait(false); + await LaunchImplNoLock(true, !Running, null, cancellationToken).ConfigureAwait(false); await chatTask.ConfigureAwait(false); } var toReboot = AlphaIsActive ? alphaServer : bravoServer; @@ -818,7 +855,7 @@ namespace Tgstation.Server.Host.Components.Watchdog public async Task Terminate(bool graceful, CancellationToken cancellationToken) { using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) - await TerminateNoLock(graceful, true, cancellationToken).ConfigureAwait(false); + await TerminateNoLock(graceful, !releaseServers, cancellationToken).ConfigureAwait(false); } /// @@ -848,7 +885,7 @@ namespace Tgstation.Server.Host.Components.Watchdog await jobManager.RegisterOperation(job, async (j, databaseContext, progressFunction, ct) => { using (await SemaphoreSlimContext.Lock(semaphore, ct).ConfigureAwait(false)) - await LaunchNoLock(true, true, reattachInfo, ct).ConfigureAwait(false); + await LaunchImplNoLock(true, true, reattachInfo, ct).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false); } From dd046bc0a40f099ac8fc6eff3df4d9d90dcadf43 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 25 Sep 2018 23:43:07 -0400 Subject: [PATCH 26/26] Migrations for the ReattachInformation CompileJob requirement --- ...5_MSReattachCompileJobRequired.Designer.cs | 680 ++++++++++++++++++ ...0926033145_MSReattachCompileJobRequired.cs | 61 ++ ...4_MYReattachCompileJobRequired.Designer.cs | 653 +++++++++++++++++ ...0926034014_MYReattachCompileJobRequired.cs | 61 ++ .../MySqlDatabaseContextModelSnapshot.cs | 5 +- .../SqlServerDatabaseContextModelSnapshot.cs | 5 +- 6 files changed, 1461 insertions(+), 4 deletions(-) create mode 100644 src/Tgstation.Server.Host/Models/Migrations/20180926033145_MSReattachCompileJobRequired.Designer.cs create mode 100644 src/Tgstation.Server.Host/Models/Migrations/20180926033145_MSReattachCompileJobRequired.cs create mode 100644 src/Tgstation.Server.Host/Models/Migrations/20180926034014_MYReattachCompileJobRequired.Designer.cs create mode 100644 src/Tgstation.Server.Host/Models/Migrations/20180926034014_MYReattachCompileJobRequired.cs diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180926033145_MSReattachCompileJobRequired.Designer.cs b/src/Tgstation.Server.Host/Models/Migrations/20180926033145_MSReattachCompileJobRequired.Designer.cs new file mode 100644 index 0000000000..5c778ac640 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/20180926033145_MSReattachCompileJobRequired.Designer.cs @@ -0,0 +1,680 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Tgstation.Server.Host.Models.Migrations +{ + [DbContext(typeof(SqlServerDatabaseContext))] + [Migration("20180926033145_MSReattachCompileJobRequired")] + partial class MSReattachCompileJobRequired + { + /// + /// Builds the target model + /// + /// The to use + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ConnectionString") + .IsRequired(); + + b.Property("Enabled"); + + b.Property("InstanceId"); + + b.Property("Name") + .IsRequired(); + + b.Property("Provider"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ChatSettingsId"); + + b.Property("DiscordChannelId") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("IrcChannel"); + + b.Property("IsAdminChannel") + .IsRequired(); + + b.Property("IsUpdatesChannel") + .IsRequired(); + + b.Property("IsWatchdogChannel") + .IsRequired(); + + b.Property("Tag"); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique() + .HasFilter("[DiscordChannelId] IS NOT NULL"); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique() + .HasFilter("[IrcChannel] IS NOT NULL"); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ByondVersion") + .IsRequired(); + + b.Property("DirectoryName") + .IsRequired(); + + b.Property("DmeName") + .IsRequired(); + + b.Property("JobId"); + + b.Property("MinimumSecurityLevel"); + + b.Property("Output") + .IsRequired(); + + b.Property("RevisionInformationId"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessToken"); + + b.Property("AllowWebClient") + .IsRequired(); + + b.Property("AutoStart") + .IsRequired(); + + b.Property("InstanceId"); + + b.Property("PrimaryPort"); + + b.Property("ProcessId"); + + b.Property("SecondaryPort"); + + b.Property("SecurityLevel"); + + b.Property("SoftRestart") + .IsRequired(); + + b.Property("SoftShutdown") + .IsRequired(); + + b.Property("StartupTimeout"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ApiValidationPort"); + + b.Property("ApiValidationSecurityLevel"); + + b.Property("InstanceId"); + + b.Property("ProjectName"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AutoUpdateInterval"); + + b.Property("ConfigurationType"); + + b.Property("Name") + .IsRequired(); + + b.Property("Online") + .IsRequired(); + + b.Property("Path") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ByondRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("ChatBotRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("ConfigurationRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("DreamDaemonRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("DreamMakerRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("InstanceId"); + + b.Property("InstanceUserRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("RepositoryRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("UserId", "InstanceId") + .IsUnique(); + + b.ToTable("InstanceUsers"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CancelRight") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("CancelRightsType") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("Cancelled") + .IsRequired(); + + b.Property("CancelledById"); + + b.Property("Description") + .IsRequired(); + + b.Property("ExceptionDetails"); + + b.Property("InstanceId"); + + b.Property("StartedAt") + .IsRequired(); + + b.Property("StartedById"); + + b.Property("StoppedAt"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessIdentifier") + .IsRequired(); + + b.Property("ChatChannelsJson") + .IsRequired(); + + b.Property("ChatCommandsJson") + .IsRequired(); + + b.Property("CompileJobId"); + + b.Property("IsPrimary"); + + b.Property("Port"); + + b.Property("ProcessId"); + + b.Property("RebootState"); + + b.Property("ServerCommandsJson") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessToken"); + + b.Property("AccessUser"); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired(); + + b.Property("AutoUpdatesSynchronize") + .IsRequired(); + + b.Property("CommitterEmail") + .IsRequired(); + + b.Property("CommitterName") + .IsRequired(); + + b.Property("InstanceId"); + + b.Property("PushTestMergeCommits") + .IsRequired(); + + b.Property("ShowTestMergeCommitters") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("RevisionInformationId"); + + b.Property("TestMergeId"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CommitSha") + .IsRequired() + .HasMaxLength(40); + + b.Property("InstanceId"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasMaxLength(40); + + b.HasKey("Id"); + + b.HasIndex("CommitSha") + .IsUnique(); + + b.HasIndex("InstanceId"); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Author") + .IsRequired(); + + b.Property("BodyAtMerge") + .IsRequired(); + + b.Property("Comment"); + + b.Property("MergedAt"); + + b.Property("MergedById"); + + b.Property("Number") + .IsRequired(); + + b.Property("PrimaryRevisionInformationId") + .IsRequired(); + + b.Property("PullRequestRevision") + .IsRequired(); + + b.Property("TitleAtMerge") + .IsRequired(); + + b.Property("Url") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AdministrationRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("CanonicalName") + .IsRequired(); + + b.Property("CreatedAt") + .IsRequired(); + + b.Property("CreatedById"); + + b.Property("Enabled") + .IsRequired(); + + b.Property("InstanceManagerRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("LastPasswordUpdate"); + + b.Property("Name") + .IsRequired(); + + b.Property("PasswordHash"); + + b.Property("SystemIdentifier"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AlphaId"); + + b.Property("AlphaIsActive"); + + b.Property("BravoId"); + + b.Property("InstanceId"); + + b.HasKey("Id"); + + b.HasIndex("AlphaId"); + + b.HasIndex("BravoId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("WatchdogReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstanceUsers") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User") + .WithMany("InstanceUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha") + .WithMany() + .HasForeignKey("AlphaId"); + + b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo") + .WithMany() + .HasForeignKey("BravoId"); + + b.HasOne("Tgstation.Server.Host.Models.Instance") + .WithOne("WatchdogReattachInformation") + .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180926033145_MSReattachCompileJobRequired.cs b/src/Tgstation.Server.Host/Models/Migrations/20180926033145_MSReattachCompileJobRequired.cs new file mode 100644 index 0000000000..1de3ab48ca --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/20180926033145_MSReattachCompileJobRequired.cs @@ -0,0 +1,61 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Models.Migrations +{ + /// + /// Marks the s of s as non-nullable for MSSQL + /// + public partial class MSReattachCompileJobRequired : Migration + { + /// + /// Applies the migration + /// + /// The to use + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + table: "ReattachInformations"); + + migrationBuilder.AlterColumn( + name: "CompileJobId", + table: "ReattachInformations", + nullable: false, + oldClrType: typeof(long), + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + table: "ReattachInformations", + column: "CompileJobId", + principalTable: "CompileJobs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + /// Unapplies the migration + /// + /// The to use + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + table: "ReattachInformations"); + + migrationBuilder.AlterColumn( + name: "CompileJobId", + table: "ReattachInformations", + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AddForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + table: "ReattachInformations", + column: "CompileJobId", + principalTable: "CompileJobs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180926034014_MYReattachCompileJobRequired.Designer.cs b/src/Tgstation.Server.Host/Models/Migrations/20180926034014_MYReattachCompileJobRequired.Designer.cs new file mode 100644 index 0000000000..600d1dd972 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/20180926034014_MYReattachCompileJobRequired.Designer.cs @@ -0,0 +1,653 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Models.Migrations +{ + [DbContext(typeof(MySqlDatabaseContext))] + [Migration("20180926034014_MYReattachCompileJobRequired")] + partial class MYReattachCompileJobRequired + { + /// + /// Builds the target model + /// + /// The to use + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConnectionString") + .IsRequired(); + + b.Property("Enabled"); + + b.Property("InstanceId"); + + b.Property("Name") + .IsRequired(); + + b.Property("Provider"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChatSettingsId"); + + b.Property("DiscordChannelId"); + + b.Property("IrcChannel"); + + b.Property("IsAdminChannel") + .IsRequired(); + + b.Property("IsUpdatesChannel") + .IsRequired(); + + b.Property("IsWatchdogChannel") + .IsRequired(); + + b.Property("Tag"); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique(); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique(); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ByondVersion") + .IsRequired(); + + b.Property("DirectoryName") + .IsRequired(); + + b.Property("DmeName") + .IsRequired(); + + b.Property("JobId"); + + b.Property("MinimumSecurityLevel"); + + b.Property("Output") + .IsRequired(); + + b.Property("RevisionInformationId"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessToken"); + + b.Property("AllowWebClient") + .IsRequired(); + + b.Property("AutoStart") + .IsRequired(); + + b.Property("InstanceId"); + + b.Property("PrimaryPort") + .IsRequired(); + + b.Property("ProcessId"); + + b.Property("SecondaryPort") + .IsRequired(); + + b.Property("SecurityLevel"); + + b.Property("SoftRestart") + .IsRequired(); + + b.Property("SoftShutdown") + .IsRequired(); + + b.Property("StartupTimeout") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApiValidationPort") + .IsRequired(); + + b.Property("ApiValidationSecurityLevel"); + + b.Property("InstanceId"); + + b.Property("ProjectName"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AutoUpdateInterval") + .IsRequired(); + + b.Property("ConfigurationType"); + + b.Property("Name") + .IsRequired(); + + b.Property("Online") + .IsRequired(); + + b.Property("Path") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ByondRights"); + + b.Property("ChatBotRights"); + + b.Property("ConfigurationRights"); + + b.Property("DreamDaemonRights"); + + b.Property("DreamMakerRights"); + + b.Property("InstanceId"); + + b.Property("InstanceUserRights"); + + b.Property("RepositoryRights"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("UserId", "InstanceId") + .IsUnique(); + + b.ToTable("InstanceUsers"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CancelRight"); + + b.Property("CancelRightsType"); + + b.Property("Cancelled") + .IsRequired(); + + b.Property("CancelledById"); + + b.Property("Description") + .IsRequired(); + + b.Property("ExceptionDetails"); + + b.Property("InstanceId"); + + b.Property("StartedAt") + .IsRequired(); + + b.Property("StartedById"); + + b.Property("StoppedAt"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessIdentifier") + .IsRequired(); + + b.Property("ChatChannelsJson") + .IsRequired(); + + b.Property("ChatCommandsJson") + .IsRequired(); + + b.Property("CompileJobId"); + + b.Property("IsPrimary"); + + b.Property("Port"); + + b.Property("ProcessId"); + + b.Property("RebootState"); + + b.Property("ServerCommandsJson") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessToken"); + + b.Property("AccessUser"); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired(); + + b.Property("AutoUpdatesSynchronize") + .IsRequired(); + + b.Property("CommitterEmail") + .IsRequired(); + + b.Property("CommitterName") + .IsRequired(); + + b.Property("InstanceId"); + + b.Property("PushTestMergeCommits") + .IsRequired(); + + b.Property("ShowTestMergeCommitters") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RevisionInformationId"); + + b.Property("TestMergeId"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CommitSha") + .IsRequired() + .HasMaxLength(40); + + b.Property("InstanceId"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasMaxLength(40); + + b.HasKey("Id"); + + b.HasIndex("CommitSha") + .IsUnique(); + + b.HasIndex("InstanceId"); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author") + .IsRequired(); + + b.Property("BodyAtMerge") + .IsRequired(); + + b.Property("Comment"); + + b.Property("MergedAt"); + + b.Property("MergedById"); + + b.Property("Number") + .IsRequired(); + + b.Property("PrimaryRevisionInformationId") + .IsRequired(); + + b.Property("PullRequestRevision") + .IsRequired(); + + b.Property("TitleAtMerge") + .IsRequired(); + + b.Property("Url") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AdministrationRights"); + + b.Property("CanonicalName") + .IsRequired(); + + b.Property("CreatedAt") + .IsRequired(); + + b.Property("CreatedById"); + + b.Property("Enabled") + .IsRequired(); + + b.Property("InstanceManagerRights"); + + b.Property("LastPasswordUpdate"); + + b.Property("Name") + .IsRequired(); + + b.Property("PasswordHash"); + + b.Property("SystemIdentifier"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AlphaId"); + + b.Property("AlphaIsActive"); + + b.Property("BravoId"); + + b.Property("InstanceId"); + + b.HasKey("Id"); + + b.HasIndex("AlphaId"); + + b.HasIndex("BravoId"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("WatchdogReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstanceUsers") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User") + .WithMany("InstanceUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha") + .WithMany() + .HasForeignKey("AlphaId"); + + b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo") + .WithMany() + .HasForeignKey("BravoId"); + + b.HasOne("Tgstation.Server.Host.Models.Instance") + .WithOne("WatchdogReattachInformation") + .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180926034014_MYReattachCompileJobRequired.cs b/src/Tgstation.Server.Host/Models/Migrations/20180926034014_MYReattachCompileJobRequired.cs new file mode 100644 index 0000000000..6eb1596cc8 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/20180926034014_MYReattachCompileJobRequired.cs @@ -0,0 +1,61 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Models.Migrations +{ + /// + /// Marks the s of s as non-nullable for MySQL/MariaDB + /// + public partial class MYReattachCompileJobRequired : Migration + { + /// + /// Applies the migration + /// + /// The to use + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + table: "ReattachInformations"); + + migrationBuilder.AlterColumn( + name: "CompileJobId", + table: "ReattachInformations", + nullable: false, + oldClrType: typeof(long), + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + table: "ReattachInformations", + column: "CompileJobId", + principalTable: "CompileJobs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + /// Unapplies the migration + /// + /// The to use + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + table: "ReattachInformations"); + + migrationBuilder.AlterColumn( + name: "CompileJobId", + table: "ReattachInformations", + nullable: true, + oldClrType: typeof(long)); + + migrationBuilder.AddForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + table: "ReattachInformations", + column: "CompileJobId", + principalTable: "CompileJobs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs index e273129437..81726e7732 100644 --- a/src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs @@ -290,7 +290,7 @@ namespace Tgstation.Server.Host.Models.Migrations b.Property("ChatCommandsJson") .IsRequired(); - b.Property("CompileJobId"); + b.Property("CompileJobId"); b.Property("IsPrimary"); @@ -575,7 +575,8 @@ namespace Tgstation.Server.Host.Models.Migrations { b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") .WithMany() - .HasForeignKey("CompileJobId"); + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => diff --git a/src/Tgstation.Server.Host/Models/Migrations/SqlServerDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Models/Migrations/SqlServerDatabaseContextModelSnapshot.cs index 0174e55a09..c0f70463fe 100644 --- a/src/Tgstation.Server.Host/Models/Migrations/SqlServerDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Models/Migrations/SqlServerDatabaseContextModelSnapshot.cs @@ -308,7 +308,7 @@ namespace Tgstation.Server.Host.Models.Migrations b.Property("ChatCommandsJson") .IsRequired(); - b.Property("CompileJobId"); + b.Property("CompileJobId"); b.Property("IsPrimary"); @@ -601,7 +601,8 @@ namespace Tgstation.Server.Host.Models.Migrations { b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") .WithMany() - .HasForeignKey("CompileJobId"); + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>