diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index 1238284bf9..aad11715e0 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -250,13 +250,14 @@ namespace Tgstation.Server.Api.Models RepoWhitespaceCommitterEmail, /// - /// Attempted to set and to the same value. + /// Deprecated. /// - [Description("Primary and secondary ports cannot be the same!")] + [Description("Deprecated error code.")] + [Obsolete("With API v7 ", true)] DreamDaemonDuplicatePorts, /// - /// was used where it is not supported. + /// Deprecated. /// [Description("Deprecated error code.")] [Obsolete("With DMAPI-5.0.0, ultrasafe security is now supported.", true)] diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs index f30f4b296d..2261940f55 100644 --- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs @@ -26,14 +26,7 @@ namespace Tgstation.Server.Api.Models.Internal /// [Required] [Range(1, UInt16.MaxValue)] - public ushort? PrimaryPort { get; set; } - - /// - /// The second port uses - /// - [Required] - [Range(1, UInt16.MaxValue)] - public ushort? SecondaryPort { get; set; } + public ushort? Port { get; set; } /// /// The DreamDaemon startup timeout in seconds @@ -63,8 +56,7 @@ namespace Tgstation.Server.Api.Models.Internal public bool CanApplyWithoutReboot(DreamDaemonLaunchParameters otherParameters) => AllowWebClient == (otherParameters?.AllowWebClient ?? throw new ArgumentNullException(nameof(otherParameters))) && SecurityLevel == otherParameters.SecurityLevel - && PrimaryPort == otherParameters.PrimaryPort - && SecondaryPort == otherParameters.SecondaryPort + && Port == otherParameters.Port && TopicRequestTimeout == otherParameters.TopicRequestTimeout; // We intentionally don't check StartupTimeout or heartbeat seconds as it doesn't matter in terms of the watchdog } } \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs index 089cd810c8..ad97d7dec5 100644 --- a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs +++ b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs @@ -19,9 +19,9 @@ namespace Tgstation.Server.Api.Rights ReadRevision = 1, /// - /// User can change both primary and secondary ports + /// User can change the port DreamDaemon runs on. /// - SetPorts = 2, + SetPort = 2, /// /// User can change diff --git a/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs index 113dbaa3ad..41a532235c 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -222,21 +223,51 @@ namespace Tgstation.Server.Host.Components.Deployment } var providerSubmitted = false; - var newProvider = new DmbProvider(compileJob, ioManager, () => + + void CleanupAction() { if (providerSubmitted) CleanJob(compileJob); - }); + } + var newProvider = new DmbProvider(compileJob, ioManager, CleanupAction); try { - var primaryCheckTask = ioManager.FileExists(ioManager.ConcatPath(newProvider.PrimaryDirectory, newProvider.DmbName), cancellationToken); - var secondaryCheckTask = ioManager.FileExists(ioManager.ConcatPath(newProvider.PrimaryDirectory, newProvider.DmbName), cancellationToken); + const string LegacyADirectoryName = "A"; + const string LegacyBDirectoryName = "B"; - if (!(await primaryCheckTask.ConfigureAwait(false) && await secondaryCheckTask.ConfigureAwait(false))) + var dmbExistsAtRoot = await ioManager.FileExists( + ioManager.ConcatPath( + newProvider.Directory, + newProvider.DmbName), + cancellationToken) + .ConfigureAwait(false); + + if (!dmbExistsAtRoot) { - logger.LogWarning("Error loading compile job, .dmb missing!"); - return null; // omae wa mou shinderu + var primaryCheckTask = ioManager.FileExists( + ioManager.ConcatPath( + newProvider.Directory, + LegacyADirectoryName, + newProvider.DmbName), + cancellationToken); + var secondaryCheckTask = ioManager.FileExists( + ioManager.ConcatPath( + newProvider.Directory, + LegacyBDirectoryName, + newProvider.DmbName), + cancellationToken); + + if (!(await primaryCheckTask.ConfigureAwait(false) && await secondaryCheckTask.ConfigureAwait(false))) + { + logger.LogWarning("Error loading compile job, .dmb missing!"); + return null; // omae wa mou shinderu + } + + // rebuild the provider because it's using the legacy style directories + // Don't dispose it + logger.LogDebug("Creating legacy two folder .dmb provider targeting {0} directory...", LegacyADirectoryName); + newProvider = new DmbProvider(compileJob, ioManager, CleanupAction, Path.DirectorySeparatorChar + LegacyADirectoryName); } lock (jobLockCounts) diff --git a/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs index dd5c498d5e..621ea757a4 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DmbProvider.cs @@ -11,10 +11,7 @@ namespace Tgstation.Server.Host.Components.Deployment public string DmbName => String.Concat(CompileJob.DmeName, DreamMaker.DmbExtension); /// - public string PrimaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.ADirectoryName)); - - /// - public string SecondaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.BDirectoryName)); + public string Directory => ioManager.ResolvePath(CompileJob.DirectoryName.ToString() + directoryAppend); /// /// The for the @@ -26,6 +23,11 @@ namespace Tgstation.Server.Host.Components.Deployment /// readonly IIOManager ioManager; + /// + /// Extra path to add to the end of + /// + readonly string directoryAppend; + /// /// The to run when is called /// @@ -37,11 +39,13 @@ namespace Tgstation.Server.Host.Components.Deployment /// The value of /// The value of /// The value of - public DmbProvider(CompileJob compileJob, IIOManager ioManager, Action onDispose) + /// The optional value of + public DmbProvider(CompileJob compileJob, IIOManager ioManager, Action onDispose, string directoryAppend = null) { CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob)); this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose)); + this.directoryAppend = directoryAppend ?? String.Empty; } /// diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs index fd981dfdb7..f8321e4ec3 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs @@ -27,16 +27,6 @@ namespace Tgstation.Server.Host.Components.Deployment /// sealed class DreamMaker : IDreamMaker { - /// - /// Name of the primary directory used for compilation - /// - public const string ADirectoryName = "A"; - - /// - /// Name of the secondary directory used for compilation - /// - public const string BDirectoryName = "B"; - /// /// Extension for .dmbs /// @@ -206,19 +196,17 @@ namespace Tgstation.Server.Host.Components.Deployment var launchParameters = new DreamDaemonLaunchParameters { AllowWebClient = false, - PrimaryPort = portToUse, + Port = portToUse, SecurityLevel = securityLevel, StartupTimeout = timeout, TopicRequestTimeout = 0 // not used }; - var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName); - job.MinimumSecurityLevel = securityLevel; // needed for the TempDmbProvider var timeoutAt = DateTimeOffset.Now.AddSeconds(timeout); - using var provider = new TemporaryDmbProvider(ioManager.ResolvePath(dirA), String.Concat(job.DmeName, DmbExtension), job); - using var controller = await sessionControllerFactory.LaunchNew(provider, byondLock, launchParameters, true, true, true, cancellationToken).ConfigureAwait(false); + using var provider = new TemporaryDmbProvider(ioManager.ResolvePath(job.DirectoryName.ToString()), String.Concat(job.DmeName, DmbExtension), job); + using var controller = await sessionControllerFactory.LaunchNew(provider, byondLock, launchParameters, true, cancellationToken).ConfigureAwait(false); var launchResult = await controller.LaunchResult.ConfigureAwait(false); var now = DateTimeOffset.Now; @@ -273,9 +261,7 @@ namespace Tgstation.Server.Host.Components.Deployment using var dm = processExecutor.LaunchProcess( dreamMakerPath, ioManager.ResolvePath( - ioManager.ConcatPath( - job.DirectoryName.ToString(), - ADirectoryName)), + job.DirectoryName.ToString()), $"-clean {job.DmeName}.{DmeExtension}", true, true, @@ -299,12 +285,11 @@ namespace Tgstation.Server.Host.Components.Deployment /// A representing the running operation async Task ModifyDme(Models.CompileJob job, CancellationToken cancellationToken) { - var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName); var dmeFileName = String.Join('.', job.DmeName, DmeExtension); - var dmePath = ioManager.ConcatPath(dirA, dmeFileName); + var dmePath = ioManager.ConcatPath(job.DirectoryName.ToString(), dmeFileName); var dmeReadTask = ioManager.ReadAllBytes(dmePath, cancellationToken); - var dmeModificationsTask = configuration.CopyDMFilesTo(dmeFileName, ioManager.ResolvePath(dirA), cancellationToken); + var dmeModificationsTask = configuration.CopyDMFilesTo(dmeFileName, ioManager.ResolvePath(job.DirectoryName.ToString()), cancellationToken); var dmeBytes = await dmeReadTask.ConfigureAwait(false); var dme = Encoding.UTF8.GetString(dmeBytes); @@ -376,31 +361,28 @@ namespace Tgstation.Server.Host.Components.Deployment /// A representing the running operation async Task RunCompileJob(Models.CompileJob job, Api.Models.DreamMaker dreamMakerSettings, IByondExecutableLock byondLock, IRepository repository, uint apiValidateTimeout, CancellationToken cancellationToken) { - var jobPath = job.DirectoryName.ToString(); - logger.LogTrace("Compile output GUID: {0}", jobPath); + var outputDirectory = job.DirectoryName.ToString(); + logger.LogTrace("Compile output GUID: {0}", outputDirectory); try { - var dirA = ioManager.ConcatPath(jobPath, ADirectoryName); - var dirB = ioManager.ConcatPath(jobPath, BDirectoryName); - // copy the repository logger.LogTrace("Copying repository to game directory..."); - var resolvedADirectory = ioManager.ResolvePath(dirA); + var resolvedOutputDirectory = ioManager.ResolvePath(outputDirectory); var repoOrigin = repository.Origin; using (repository) - await repository.CopyTo(resolvedADirectory, cancellationToken).ConfigureAwait(false); + await repository.CopyTo(resolvedOutputDirectory, cancellationToken).ConfigureAwait(false); // repository closed now // run precompile scripts - await eventConsumer.HandleEvent(EventType.CompileStart, new List { resolvedADirectory, repoOrigin }, cancellationToken).ConfigureAwait(false); + await eventConsumer.HandleEvent(EventType.CompileStart, new List { resolvedOutputDirectory, repoOrigin }, cancellationToken).ConfigureAwait(false); // determine the dme if (job.DmeName == null) { logger.LogTrace("Searching for available .dmes..."); - var foundPaths = await ioManager.GetFilesWithExtension(dirA, DmeExtension, true, cancellationToken).ConfigureAwait(false); + var foundPaths = await ioManager.GetFilesWithExtension(outputDirectory, DmeExtension, true, cancellationToken).ConfigureAwait(false); var foundPath = foundPaths.FirstOrDefault(); if (foundPath == default) throw new JobException(ErrorCode.DreamMakerNoDme); @@ -409,7 +391,7 @@ namespace Tgstation.Server.Host.Components.Deployment } else { - var targetDme = ioManager.ConcatPath(dirA, String.Join('.', job.DmeName, DmeExtension)); + var targetDme = ioManager.ConcatPath(outputDirectory, String.Join('.', job.DmeName, DmeExtension)); var targetDmeExists = await ioManager.FileExists(targetDme, cancellationToken).ConfigureAwait(false); if (!targetDmeExists) throw new JobException(ErrorCode.DreamMakerMissingDme); @@ -435,25 +417,17 @@ namespace Tgstation.Server.Host.Components.Deployment catch (JobException) { // DD never validated or compile failed - await eventConsumer.HandleEvent(EventType.CompileFailure, new List { resolvedADirectory, exitCode == 0 ? "1" : "0" }, cancellationToken).ConfigureAwait(false); + await eventConsumer.HandleEvent(EventType.CompileFailure, new List { resolvedOutputDirectory, exitCode == 0 ? "1" : "0" }, cancellationToken).ConfigureAwait(false); throw; } logger.LogTrace("Running post compile event..."); - await eventConsumer.HandleEvent(EventType.CompileComplete, new List { resolvedADirectory }, cancellationToken).ConfigureAwait(false); - - logger.LogTrace("Duplicating compiled game..."); - - // duplicate the dmb et al - await ioManager.CopyDirectory(dirA, dirB, null, cancellationToken).ConfigureAwait(false); + await eventConsumer.HandleEvent(EventType.CompileComplete, new List { resolvedOutputDirectory }, cancellationToken).ConfigureAwait(false); logger.LogTrace("Applying static game file symlinks..."); // symlink in the static data - var symATask = configuration.SymlinkStaticFilesTo(resolvedADirectory, cancellationToken); - var symBTask = configuration.SymlinkStaticFilesTo(ioManager.ResolvePath(dirB), cancellationToken); - - await Task.WhenAll(symATask, symBTask).ConfigureAwait(false); + await configuration.SymlinkStaticFilesTo(resolvedOutputDirectory, cancellationToken).ConfigureAwait(false); logger.LogDebug("Compile complete!"); } diff --git a/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs index ea282ca37e..da27442bc9 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/IDmbProvider.cs @@ -16,12 +16,7 @@ namespace Tgstation.Server.Host.Components.Deployment /// /// The primary game directory with a trailing directory separator /// - string PrimaryDirectory { get; } - - /// - /// The secondary game directory with a trailing directory separator - /// - string SecondaryDirectory { get; } + string Directory { get; } /// /// The of the .dmb diff --git a/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs index 7eed521f5c..7ee060ab39 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/TemporaryDmbProvider.cs @@ -12,10 +12,7 @@ namespace Tgstation.Server.Host.Components.Deployment public string DmbName { get; } /// - public string PrimaryDirectory { get; } - - /// - public string SecondaryDirectory => throw new NotSupportedException(); + public string Directory { get; } /// public CompileJob CompileJob { get; } @@ -23,13 +20,13 @@ namespace Tgstation.Server.Host.Components.Deployment /// /// Construct a /// - /// The value of + /// The value of /// The value of /// The value of public TemporaryDmbProvider(string directory, string dmb, CompileJob compileJob) { DmbName = dmb ?? throw new ArgumentNullException(nameof(dmb)); - PrimaryDirectory = directory ?? throw new ArgumentNullException(nameof(directory)); + Directory = directory ?? throw new ArgumentNullException(nameof(directory)); CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob)); } diff --git a/src/Tgstation.Server.Host/Components/Deployment/WindowsSwappableDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/WindowsSwappableDmbProvider.cs index 597333c54c..eeab223e50 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/WindowsSwappableDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/WindowsSwappableDmbProvider.cs @@ -20,10 +20,7 @@ namespace Tgstation.Server.Host.Components.Deployment public string DmbName => baseProvider.DmbName; /// - public string PrimaryDirectory => ioManager.ResolvePath(LiveGameDirectory); - - /// - public string SecondaryDirectory => throw new NotSupportedException(); + public string Directory => ioManager.ResolvePath(LiveGameDirectory); /// public CompileJob CompileJob => baseProvider.CompileJob; @@ -73,7 +70,7 @@ namespace Tgstation.Server.Host.Components.Deployment // These next two lines should be atomic but this is the best we can do await ioManager.DeleteDirectory(LiveGameDirectory, cancellationToken).ConfigureAwait(false); await symlinkFactory.CreateSymbolicLink( - ioManager.ResolvePath(baseProvider.PrimaryDirectory), + ioManager.ResolvePath(baseProvider.Directory), ioManager.ResolvePath(LiveGameDirectory), cancellationToken) .ConfigureAwait(false); diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index 4e170b7b9a..d2824861af 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -241,7 +241,12 @@ namespace Tgstation.Server.Host.Components var dmbFactory = new DmbFactory(databaseContextFactory, gameIoManager, loggerFactory.CreateLogger(), metadata.CloneMetadata()); try { - var reattachInfoHandler = new ReattachInfoHandler(databaseContextFactory, dmbFactory, loggerFactory.CreateLogger(), metadata.CloneMetadata()); + var reattachInfoHandler = new ReattachInfoHandler( + databaseContextFactory, + dmbFactory, + processExecutor, + loggerFactory.CreateLogger(), + metadata.CloneMetadata()); var watchdog = watchdogFactory.CreateWatchdog( chatManager, dmbFactory, diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index 4e67615cb7..b6909e91e2 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -348,10 +347,6 @@ namespace Tgstation.Server.Host.Components /// private void CheckSystemCompatibility() { - if (generalConfiguration.UseExperimentalWatchdog && !Debugger.IsAttached) - throw new InvalidOperationException( - "The experimental watchdog is currently non-functional! Please disable the '{nameof(generalConfiguration.UseExperimentalWatchdog)}' option in your configuration!"); - using var systemIdentity = systemIdentityFactory.GetCurrent(); if (!systemIdentity.CanCreateSymlinks) throw new InvalidOperationException("The user running tgstation-server cannot create symlinks! Please try running as an administrative user!"); diff --git a/src/Tgstation.Server.Host/Components/Session/DeadSessionController.cs b/src/Tgstation.Server.Host/Components/Session/DeadSessionController.cs index a425b5aac3..2ba357cbb9 100644 --- a/src/Tgstation.Server.Host/Components/Session/DeadSessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/DeadSessionController.cs @@ -14,9 +14,6 @@ namespace Tgstation.Server.Host.Components.Session /// public Task LaunchResult { get; } - /// - public bool IsPrimary => false; - /// public bool TerminationWasRequested => false; diff --git a/src/Tgstation.Server.Host/Components/Session/DualReattachInformation.cs b/src/Tgstation.Server.Host/Components/Session/DualReattachInformation.cs deleted file mode 100644 index 486bc1e85c..0000000000 --- a/src/Tgstation.Server.Host/Components/Session/DualReattachInformation.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Globalization; -using Tgstation.Server.Host.Components.Deployment; -using Tgstation.Server.Host.Models; - -namespace Tgstation.Server.Host.Components.Session -{ - /// - /// Reattach information for two - /// - public sealed class DualReattachInformation : DualReattachInformationBase - { - /// - /// for the Alpha session - /// - public ReattachInformation Alpha { get; set; } - - /// - /// for the Bravo session - /// - public ReattachInformation Bravo { get; set; } - - /// - /// The timeout used for topic request. - /// - public TimeSpan TopicRequestTimeout { get; set; } - - /// - /// Construct a - /// - public DualReattachInformation() { } - - /// - /// Construct a from a given with a given and - /// - /// The to copy information from - /// The used to build - /// The used to build - public DualReattachInformation(Models.DualReattachInformation copy, IDmbProvider dmbAlpha, IDmbProvider dmbBravo) : base(copy) - { - if (copy.Alpha != null) - Alpha = new ReattachInformation(copy.Alpha, dmbAlpha); - if (copy.Bravo != null) - Bravo = new ReattachInformation(copy.Bravo, dmbBravo); - } - - /// - public override string ToString() => String.Format( - CultureInfo.InvariantCulture, - "Alpha: {0}, Bravo {1}", - Alpha?.ToString() ?? "(null)", - Bravo?.ToString() ?? "(null)"); - } -} diff --git a/src/Tgstation.Server.Host/Components/Session/IReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/Session/IReattachInfoHandler.cs index d153cd713d..7aa25c41a9 100644 --- a/src/Tgstation.Server.Host/Components/Session/IReattachInfoHandler.cs +++ b/src/Tgstation.Server.Host/Components/Session/IReattachInfoHandler.cs @@ -4,23 +4,23 @@ using System.Threading.Tasks; namespace Tgstation.Server.Host.Components.Session { /// - /// Handles saving and loading + /// Handles saving and loading . /// public interface IReattachInfoHandler { /// /// Save some /// - /// The to save + /// The to save. /// The for the operation /// A representing the running operation - Task Save(DualReattachInformation reattachInformation, CancellationToken cancellationToken); + Task Save(ReattachInformation reattachInformation, CancellationToken cancellationToken); /// - /// Load a saved + /// Load a saved . /// /// The for the operation - /// A resulting in the stored if any - Task Load(CancellationToken cancellationToken); + /// A resulting in the stored if any. + Task Load(CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs index fdc9536ec5..e4386d4dd9 100644 --- a/src/Tgstation.Server.Host/Components/Session/ISessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/ISessionController.cs @@ -17,11 +17,6 @@ namespace Tgstation.Server.Host.Components.Session /// Task LaunchResult { get; } - /// - /// If the of is being used - /// - bool IsPrimary { get; } - /// /// If the DreamDaemon instance sent a /// diff --git a/src/Tgstation.Server.Host/Components/Session/ISessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Session/ISessionControllerFactory.cs index 5a9d7d479e..5b110fd8c3 100644 --- a/src/Tgstation.Server.Host/Components/Session/ISessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Session/ISessionControllerFactory.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models.Internal; using Tgstation.Server.Host.Components.Byond; @@ -18,8 +17,6 @@ namespace Tgstation.Server.Host.Components.Session /// The to use /// The current if any /// The to use. will be updated with the minumum required security level for the launch. - /// If the of should be used - /// If the of should be used /// If the should only validate the DMAPI then exit /// The for the operation /// A resulting in a new @@ -27,8 +24,6 @@ namespace Tgstation.Server.Host.Components.Session IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, DreamDaemonLaunchParameters launchParameters, - bool primaryPort, - bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken); @@ -36,12 +31,10 @@ namespace Tgstation.Server.Host.Components.Session /// Create a from an existing DreamDaemon instance /// /// The to use - /// The timeout for sending topic requests. /// The for the operation /// A resulting in a new on success or on failure to reattach Task Reattach( ReattachInformation reattachInformation, - TimeSpan topicRequestTimeout, CancellationToken cancellationToken); /// diff --git a/src/Tgstation.Server.Host/Components/Session/ReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/Session/ReattachInfoHandler.cs index 9185132b7c..76631de5f2 100644 --- a/src/Tgstation.Server.Host/Components/Session/ReattachInfoHandler.cs +++ b/src/Tgstation.Server.Host/Components/Session/ReattachInfoHandler.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Host.Components.Deployment; using Tgstation.Server.Host.Database; +using Tgstation.Server.Host.System; using Z.EntityFramework.Plus; namespace Tgstation.Server.Host.Components.Session @@ -23,6 +24,11 @@ namespace Tgstation.Server.Host.Components.Session /// readonly IDmbFactory dmbFactory; + /// + /// The for the . + /// + readonly IProcessExecutor processExecutor; + /// /// The for the /// @@ -38,18 +44,25 @@ namespace Tgstation.Server.Host.Components.Session /// /// The value of /// The value of + /// The value of /// The value of /// The value of - public ReattachInfoHandler(IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, ILogger logger, Api.Models.Instance metadata) + public ReattachInfoHandler( + IDatabaseContextFactory databaseContextFactory, + IDmbFactory dmbFactory, + IProcessExecutor processExecutor, + ILogger logger, + Api.Models.Instance metadata) { this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory)); + this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); } /// - public Task Save(DualReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) => + public Task Save(ReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) => { if (reattachInformation == null) throw new ArgumentNullException(nameof(reattachInformation)); @@ -57,51 +70,38 @@ namespace Tgstation.Server.Host.Components.Session logger.LogDebug("Saving reattach information: {0}...", reattachInformation); await db - .WatchdogReattachInformations + .ReattachInformations .AsQueryable() - .Where(x => x.InstanceId == metadata.Id) + .Where(x => x.CompileJob.Job.Instance.Id == metadata.Id) .DeleteAsync(cancellationToken) .ConfigureAwait(false); - Models.ReattachInformation ConvertReattachInfo(ReattachInformation wdInfo) + db.CompileJobs.Attach(reattachInformation.Dmb.CompileJob); + var dbReattachInfo = new Models.ReattachInformation { - if (wdInfo == null) - return null; - db.CompileJobs.Attach(wdInfo.Dmb.CompileJob); - return new Models.ReattachInformation - { - AccessIdentifier = wdInfo.AccessIdentifier, - CompileJob = wdInfo.Dmb.CompileJob, - IsPrimary = wdInfo.IsPrimary, - Port = wdInfo.Port, - ProcessId = wdInfo.ProcessId, - RebootState = wdInfo.RebootState, - LaunchSecurityLevel = wdInfo.LaunchSecurityLevel - }; - } + AccessIdentifier = reattachInformation.AccessIdentifier, + CompileJob = reattachInformation.Dmb.CompileJob, + Port = reattachInformation.Port, + ProcessId = reattachInformation.ProcessId, + RebootState = reattachInformation.RebootState, + LaunchSecurityLevel = reattachInformation.LaunchSecurityLevel + }; - db.WatchdogReattachInformations.Add(new Models.DualReattachInformation - { - Alpha = ConvertReattachInfo(reattachInformation.Alpha), - Bravo = ConvertReattachInfo(reattachInformation.Bravo), - AlphaIsActive = reattachInformation.AlphaIsActive, - InstanceId = metadata.Id - }); + db.ReattachInformations.Add(dbReattachInfo); await db.Save(cancellationToken).ConfigureAwait(false); }); /// - public async Task Load(CancellationToken cancellationToken) + public async Task Load(CancellationToken cancellationToken) { - Models.DualReattachInformation result = null; + Models.ReattachInformation result = null; TimeSpan? topicTimeout = null; await databaseContextFactory.UseContext(async (db) => { - IQueryable InstanceQuery() => db.Instances + var timeoutMilliseconds = await db + .Instances .AsQueryable() - .Where(x => x.Id == metadata.Id); - - var timeoutMilliseconds = await InstanceQuery() + .Where(x => x.Id == metadata.Id) .Select(x => x.DreamDaemonSettings.TopicRequestTimeout) .FirstOrDefaultAsync(cancellationToken) .ConfigureAwait(false); @@ -111,15 +111,37 @@ namespace Tgstation.Server.Host.Components.Session topicTimeout = TimeSpan.FromMilliseconds(timeoutMilliseconds.Value); - var instance = await InstanceQuery() - .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; + var dbReattachInfos = await db + .ReattachInformations + .AsQueryable() + .Where(x => x.CompileJob.Job.Instance.Id == metadata.Id) + .Include(x => x.CompileJob) + .ToListAsync(cancellationToken).ConfigureAwait(false); + result = dbReattachInfos.FirstOrDefault(); if (result == default) return; - instance.WatchdogReattachInformation = null; - db.WatchdogReattachInformations.Remove(result); + + bool first = true; + foreach (var reattachInfo in dbReattachInfos) + { + if (!first) + { + logger.LogWarning("Killing PID {0} associated with extra reattach information...", reattachInfo.ProcessId); + try + { + using var process = processExecutor.GetProcess(reattachInfo.ProcessId); + process.Terminate(); + } + catch (Exception ex) + { + logger.LogWarning("Failed to kill process! Exception: {0}", ex); + } + } + + db.ReattachInformations.Remove(reattachInfo); + first = false; + } + await db.Save(cancellationToken).ConfigureAwait(false); }).ConfigureAwait(false); @@ -129,20 +151,13 @@ namespace Tgstation.Server.Host.Components.Session return null; } - Task GetDmbForReattachInfo(Models.ReattachInformation reattachInformation) => reattachInformation != null - ? dmbFactory.FromCompileJob(reattachInformation.CompileJob, cancellationToken) - : Task.FromResult(null); - - var bravoDmbTask = GetDmbForReattachInfo(result.Bravo); - var info = new DualReattachInformation( + var info = new ReattachInformation( result, - await GetDmbForReattachInfo(result.Alpha).ConfigureAwait(false), - await bravoDmbTask.ConfigureAwait(false)) - { - TopicRequestTimeout = topicTimeout.Value - }; + await dmbFactory.FromCompileJob(result.CompileJob, cancellationToken).ConfigureAwait(false), + topicTimeout.Value); logger.LogDebug("Reattach information loaded: {0}", info); + return info; } } diff --git a/src/Tgstation.Server.Host/Components/Session/ReattachInformation.cs b/src/Tgstation.Server.Host/Components/Session/ReattachInformation.cs index 80492893f7..ecfb542c55 100644 --- a/src/Tgstation.Server.Host/Components/Session/ReattachInformation.cs +++ b/src/Tgstation.Server.Host/Components/Session/ReattachInformation.cs @@ -21,6 +21,11 @@ namespace Tgstation.Server.Host.Components.Session /// public RuntimeInformation RuntimeInformation { get; private set; } + /// + /// The which indicates when topic requests should timeout. + /// + public TimeSpan TopicRequestTimeout { get; } + /// /// for accessing . /// @@ -34,14 +39,12 @@ namespace Tgstation.Server.Host.Components.Session /// The value of . /// The value of . /// The value of . - /// The value of . internal ReattachInformation( IDmbProvider dmb, IProcess process, RuntimeInformation runtimeInformation, string accessIdentifier, - ushort port, - bool isPrimary) + ushort port) { Dmb = dmb ?? throw new ArgumentNullException(nameof(dmb)); ProcessId = process?.Id ?? throw new ArgumentNullException(nameof(process)); @@ -53,7 +56,6 @@ namespace Tgstation.Server.Host.Components.Session LaunchSecurityLevel = runtimeInformation.SecurityLevel.Value; Port = port; - IsPrimary = isPrimary; runtimeInformationLock = new object(); } @@ -63,9 +65,14 @@ namespace Tgstation.Server.Host.Components.Session /// /// The to copy values from /// The value of - public ReattachInformation(Models.ReattachInformation copy, IDmbProvider dmb) : base(copy) + /// The value of . + public ReattachInformation( + Models.ReattachInformation copy, + IDmbProvider dmb, + TimeSpan topicRequestTimeout) : base(copy) { Dmb = dmb ?? throw new ArgumentNullException(nameof(dmb)); + TopicRequestTimeout = topicRequestTimeout; runtimeInformationLock = new object(); } diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs index 47cbac6bd8..601c952136 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs @@ -27,16 +27,6 @@ namespace Tgstation.Server.Host.Components.Session /// public DMApiParameters DMApiParameters => reattachInformation; - /// - public bool IsPrimary - { - get - { - CheckDisposed(); - return reattachInformation.IsPrimary; - } - } - /// public ApiValidationStatus ApiValidationStatus { @@ -262,7 +252,7 @@ namespace Tgstation.Server.Host.Components.Session startupTimeout, reattached); - logger.LogDebug("Created session controller. Primary: {0}, CommsKey: {1}, Port: {2}", IsPrimary, reattachInformation.AccessIdentifier, Port); + logger.LogDebug("Created session controller. CommsKey: {0}, Port: {1}", reattachInformation.AccessIdentifier, Port); } /// diff --git a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs index b03439f38b..aa74d8600c 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionControllerFactory.cs @@ -185,17 +185,11 @@ namespace Tgstation.Server.Host.Components.Session IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, DreamDaemonLaunchParameters launchParameters, - bool primaryPort, - bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken) { - var portToUse = primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort; - if (!portToUse.HasValue) + if (!launchParameters.Port.HasValue) throw new InvalidOperationException("Given port is null!"); - - var basePath = primaryDirectory ? dmbProvider.PrimaryDirectory : dmbProvider.SecondaryDirectory; - switch (dmbProvider.CompileJob.MinimumSecurityLevel) { case DreamDaemonSecurity.Ultrasafe: @@ -219,9 +213,9 @@ namespace Tgstation.Server.Host.Components.Session try { if (launchParameters.SecurityLevel == DreamDaemonSecurity.Trusted) - await byondLock.TrustDmbPath(ioManager.ConcatPath(basePath, dmbProvider.DmbName), cancellationToken).ConfigureAwait(false); + await byondLock.TrustDmbPath(ioManager.ConcatPath(dmbProvider.Directory, dmbProvider.DmbName), cancellationToken).ConfigureAwait(false); - PortBindTest(portToUse.Value); + PortBindTest(launchParameters.Port.Value); await CheckPagerIsNotRunning(cancellationToken).ConfigureAwait(false); var accessIdentifier = cryptographySuite.GetSecureString(); @@ -242,7 +236,7 @@ namespace Tgstation.Server.Host.Components.Session CultureInfo.InvariantCulture, "{0} -port {1} -ports 1-65535 {2}-close -{3} -{4}{5} -public -params \"{6}\"", dmbProvider.DmbName, - portToUse, + launchParameters.Port.Value, launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty, SecurityWord(launchParameters.SecurityLevel.Value), visibility, @@ -257,7 +251,7 @@ namespace Tgstation.Server.Host.Components.Session // launch dd var process = processExecutor.LaunchProcess( byondLock.DreamDaemonPath, - basePath, + dmbProvider.Directory, arguments, noShellExecute, noShellExecute, @@ -268,7 +262,7 @@ namespace Tgstation.Server.Host.Components.Session if (!platformIdentifier.IsWindows) return process.GetCombinedOutput(); - var logFilePath = ioManager.ConcatPath(basePath, logFileGuid.ToString()); + var logFilePath = ioManager.ConcatPath(dmbProvider.Directory, logFileGuid.ToString()); try { var dreamDaemonLogBytes = await ioManager.ReadAllBytes( @@ -324,8 +318,7 @@ namespace Tgstation.Server.Host.Components.Session process, runtimeInformation, accessIdentifier, - portToUse.Value, - primaryDirectory); + launchParameters.Port.Value); var sessionController = new SessionController( reattachInformation, @@ -368,13 +361,12 @@ namespace Tgstation.Server.Host.Components.Session /// public async Task Reattach( ReattachInformation reattachInformation, - TimeSpan topicRequestTimeout, CancellationToken cancellationToken) { if (reattachInformation == null) throw new ArgumentNullException(nameof(reattachInformation)); - var byondTopicSender = topicClientFactory.CreateTopicClient(topicRequestTimeout); + var byondTopicSender = topicClientFactory.CreateTopicClient(reattachInformation.TopicRequestTimeout); var chatTrackingContext = chat.CreateTrackingContext(); try { diff --git a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs index 4fc0000179..c26ea86502 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/BasicWatchdog.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; using System.Globalization; using System.Threading; using System.Threading.Tasks; @@ -90,25 +89,7 @@ namespace Tgstation.Server.Host.Components.Watchdog { } /// - protected override IReadOnlyDictionary GetMonitoredServerTasks(MonitorState monitorState) - { - if (Server == null) - return null; - - monitorState.ActiveServer = Server; - - return new Dictionary - { - { MonitorActivationReason.ActiveServerCrashed, Server.Lifetime }, - { MonitorActivationReason.ActiveServerRebooted, Server.OnReboot }, - { MonitorActivationReason.InactiveServerCrashed, Extensions.TaskExtensions.InfiniteTask() }, - { MonitorActivationReason.InactiveServerRebooted, Extensions.TaskExtensions.InfiniteTask() }, - { MonitorActivationReason.InactiveServerStartupComplete, Extensions.TaskExtensions.InfiniteTask() } - }; - } - - /// - protected override async Task HandleMonitorWakeup(MonitorActivationReason reason, MonitorState monitorState, CancellationToken cancellationToken) + protected override async Task HandleMonitorWakeup(MonitorActivationReason reason, CancellationToken cancellationToken) { switch (reason) { @@ -125,22 +106,18 @@ namespace Tgstation.Server.Host.Components.Watchdog false, cancellationToken) .ConfigureAwait(false); - monitorState.NextAction = MonitorAction.Exit; - } - else - { - await Chat.SendWatchdogMessage( - String.Format( - CultureInfo.InvariantCulture, - "Server {0}! Rebooting...", - exitWord), - false, - cancellationToken) - .ConfigureAwait(false); - monitorState.NextAction = MonitorAction.Restart; + return MonitorAction.Exit; } - break; + await Chat.SendWatchdogMessage( + String.Format( + CultureInfo.InvariantCulture, + "Server {0}! Rebooting...", + exitWord), + false, + cancellationToken) + .ConfigureAwait(false); + return MonitorAction.Restart; case MonitorActivationReason.ActiveServerRebooted: var rebootState = Server.RebootState; if (gracefulRebootRequired && rebootState == Session.RebootState.Normal) @@ -155,11 +132,9 @@ namespace Tgstation.Server.Host.Components.Watchdog switch (rebootState) { case Session.RebootState.Normal: - monitorState.NextAction = HandleNormalReboot(); - break; + return HandleNormalReboot(); case Session.RebootState.Restart: - monitorState.NextAction = MonitorAction.Restart; - break; + return MonitorAction.Restart; case Session.RebootState.Shutdown: // graceful shutdown time await Chat.SendWatchdogMessage( @@ -167,13 +142,11 @@ namespace Tgstation.Server.Host.Components.Watchdog false, cancellationToken) .ConfigureAwait(false); - monitorState.NextAction = MonitorAction.Exit; - break; + return MonitorAction.Exit; default: throw new InvalidOperationException($"Invalid reboot state: {rebootState}"); } - break; case MonitorActivationReason.ActiveLaunchParametersUpdated: await Server.SetRebootState(Session.RebootState.Restart, cancellationToken).ConfigureAwait(false); gracefulRebootRequired = true; @@ -181,23 +154,13 @@ namespace Tgstation.Server.Host.Components.Watchdog case MonitorActivationReason.NewDmbAvailable: await HandleNewDmbAvailable(cancellationToken).ConfigureAwait(false); break; - case MonitorActivationReason.InactiveServerCrashed: - case MonitorActivationReason.InactiveServerRebooted: - case MonitorActivationReason.InactiveServerStartupComplete: - throw new NotSupportedException($"Unsupported activation reason: {reason}"); case MonitorActivationReason.Heartbeat: default: throw new InvalidOperationException($"Invalid activation reason: {reason}"); } - } - /// - protected sealed override DualReattachInformation CreateReattachInformation() - => new DualReattachInformation - { - AlphaIsActive = true, - Alpha = Server?.Release() - }; + return MonitorAction.Continue; + } /// protected override void DisposeAndNullControllersImpl() @@ -213,55 +176,32 @@ namespace Tgstation.Server.Host.Components.Watchdog /// protected sealed override async Task InitControllers( Task chatTask, - DualReattachInformation reattachInfo, + ReattachInformation reattachInfo, CancellationToken cancellationToken) { - var serverToReattach = reattachInfo?.Alpha ?? reattachInfo?.Bravo; - var serverToKill = reattachInfo?.Bravo ?? reattachInfo?.Alpha; - - // vice versa - if (serverToKill == serverToReattach) - serverToKill = null; - - if (reattachInfo?.AlphaIsActive == false) - { - var temp = serverToReattach; - serverToReattach = serverToKill; - serverToKill = temp; - } - // don't need a new dmb if reattaching - var doesntNeedNewDmb = serverToReattach != null; - var dmbToUse = doesntNeedNewDmb ? null : DmbFactory.LockNextDmb(1); + var reattachInProgress = reattachInfo != null; + var dmbToUse = reattachInProgress ? null : DmbFactory.LockNextDmb(1); // if this try catches something, both servers are killed - bool inactiveServerWasKilled = false; try { // start the alpha server task, either by launch a new process or attaching to an existing one // The tasks returned are mainly for writing interop files to the directories among other things and should generally never fail // The tasks pertaining to server startup times are in the ISessionControllers - Task serverLaunchTask, inactiveReattachTask; - if (!doesntNeedNewDmb) + Task serverLaunchTask; + if (!reattachInProgress) { dmbToUse = await PrepServerForLaunch(dmbToUse, cancellationToken).ConfigureAwait(false); serverLaunchTask = SessionControllerFactory.LaunchNew( dmbToUse, null, ActiveLaunchParameters, - true, - true, false, cancellationToken); } else - serverLaunchTask = SessionControllerFactory.Reattach(serverToReattach, reattachInfo.TopicRequestTimeout, cancellationToken); - - bool thereIsAnInactiveServerToKill = serverToKill != null; - if (thereIsAnInactiveServerToKill) - inactiveReattachTask = SessionControllerFactory.Reattach(serverToKill, reattachInfo.TopicRequestTimeout, cancellationToken); - else - inactiveReattachTask = Task.FromResult(null); + serverLaunchTask = SessionControllerFactory.Reattach(reattachInfo, cancellationToken); // retrieve the session controller Server = await serverLaunchTask.ConfigureAwait(false); @@ -269,16 +209,11 @@ namespace Tgstation.Server.Host.Components.Watchdog // failed reattaches will return null Server?.SetHighPriority(); - var inactiveServerController = await inactiveReattachTask.ConfigureAwait(false); - inactiveServerController?.Dispose(); - inactiveServerWasKilled = inactiveServerController != null; - // possiblity of null servers due to failed reattaches if (Server == null) { await ReattachFailure( chatTask, - thereIsAnInactiveServerToKill && !inactiveServerWasKilled, cancellationToken) .ConfigureAwait(false); return; @@ -298,8 +233,6 @@ namespace Tgstation.Server.Host.Components.Watchdog if (dmbToUse != null && !serverWasActive) dmbToUse.Dispose(); - if (serverToKill != null && !inactiveServerWasKilled) - serverToKill.Dmb.Dispose(); throw; } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ExperimentalWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/ExperimentalWatchdog.cs deleted file mode 100644 index e547e00568..0000000000 --- a/src/Tgstation.Server.Host/Components/Watchdog/ExperimentalWatchdog.cs +++ /dev/null @@ -1,600 +0,0 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; -using Tgstation.Server.Api.Models; -using Tgstation.Server.Api.Models.Internal; -using Tgstation.Server.Host.Components.Chat; -using Tgstation.Server.Host.Components.Deployment; -using Tgstation.Server.Host.Components.Events; -using Tgstation.Server.Host.Components.Session; -using Tgstation.Server.Host.Core; -using Tgstation.Server.Host.Database; -using Tgstation.Server.Host.IO; -using Tgstation.Server.Host.Jobs; - -namespace Tgstation.Server.Host.Components.Watchdog -{ - /// - /// A that tries to manage 2 servers at once for maximum uptime. - /// - sealed class ExperimentalWatchdog : WatchdogBase - { - /// - /// The time in seconds to wait from starting to start . Does not take responsiveness into account - /// - const int AlphaBravoStartupSeperationInterval = 10; // TODO: Make this configurable - - /// - public override bool AlphaIsActive => alphaIsActive; - - /// - public override Models.CompileJob ActiveCompileJob => (AlphaIsActive ? alphaServer : bravoServer)?.Dmb.CompileJob; - - /// - public override RebootState? RebootState => Status != WatchdogStatus.Offline ? (AlphaIsActive ? alphaServer?.RebootState : bravoServer?.RebootState) : null; - - /// - /// Server designation alpha - /// - ISessionController alphaServer; - - /// - /// Server designation bravo - /// - ISessionController bravoServer; - - /// - /// Backing field for . - /// - bool alphaIsActive; - - /// - /// Initializes a new instance of the . - /// - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The for the . - /// The autostart value for the . - public ExperimentalWatchdog( - IChatManager chat, - ISessionControllerFactory sessionControllerFactory, - IDmbFactory dmbFactory, - IReattachInfoHandler reattachInfoHandler, - IDatabaseContextFactory databaseContextFactory, - IJobManager jobManager, - IServerControl serverControl, - IAsyncDelayer asyncDelayer, - IIOManager diagnosticsIOManager, - IEventConsumer eventConsumer, - ILogger logger, - DreamDaemonLaunchParameters initialLaunchParameters, - Api.Models.Instance instance, bool autoStart) - : base( - chat, - sessionControllerFactory, - dmbFactory, - reattachInfoHandler, - databaseContextFactory, - jobManager, - serverControl, - asyncDelayer, - diagnosticsIOManager, - eventConsumer, - logger, - initialLaunchParameters, - instance, - autoStart) - { - alphaIsActive = true; - } - - /// - #pragma warning disable CA1502 // TODO: Decomplexify - protected override async Task HandleMonitorWakeup(MonitorActivationReason activationReason, MonitorState monitorState, CancellationToken cancellationToken) - { - Logger.LogDebug("Monitor activation. Reason: {0}", activationReason); - - // this is where the bulk of the watchdog handling code lives and is fraught with lambdas, sorry not sorry - // I'll do my best to walk you through it - - // returns true if the inactive server can't be used immediately - // also sets monitor to restart if the above holds - bool FullRestartDeadInactive() - { - if (monitorState.RebootingInactiveServer || monitorState.InactiveServerCritFail) - { - Logger.LogInformation("Inactive server is {0}! Restarting monitor...", monitorState.InactiveServerCritFail ? "critically failed" : "still rebooting"); - monitorState.NextAction = MonitorAction.Restart; // will dispose server - return true; - } - - return false; - } - - // trys to set inactive server's port to the public game port - // doesn't handle closing active server's port - // returns true on success and swaps inactiveserver and activeserver also sets LastLaunchParameters to ActiveLaunchParameters - // on failure, sets monitor to restart - async Task MakeInactiveActive() - { - Logger.LogDebug("Setting inactive server to port {0}...", ActiveLaunchParameters.PrimaryPort.Value); - var result = await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.PrimaryPort.Value, cancellationToken).ConfigureAwait(false); - - if (!result) - { - Logger.LogWarning("Failed to activate inactive server! Restarting monitor..."); - monitorState.NextAction = MonitorAction.Restart; // will dispose server - return false; - } - - // inactive server should always be using active launch parameters - LastLaunchParameters = ActiveLaunchParameters; - - var tmp = monitorState.ActiveServer; - monitorState.ActiveServer = monitorState.InactiveServer; - monitorState.InactiveServer = tmp; - alphaIsActive = !AlphaIsActive; - monitorState.ActiveServer.EnableCustomChatCommands(); - return true; - } - - // Kills and tries to launch inactive server with the latest dmb - // falls back to current dmb on failure - // Sets critfail on inactive server failing that - // returns false if the backup dmb was used successfully, true otherwise - async Task UpdateAndRestartInactiveServer(bool breakAfter) - { - ActiveParametersUpdated = new TaskCompletionSource(); - monitorState.InactiveServer.Dispose(); // kill or recycle it - var desiredNextAction = breakAfter ? MonitorAction.Break : MonitorAction.Continue; - monitorState.NextAction = desiredNextAction; - - Logger.LogInformation("Rebooting inactive server..."); - var newDmb = DmbFactory.LockNextDmb(1); - try - { - monitorState.InactiveServer = await SessionControllerFactory.LaunchNew( - newDmb, - null, - ActiveLaunchParameters, - false, - !monitorState.ActiveServer.IsPrimary, - false, - cancellationToken) - .ConfigureAwait(false); - monitorState.InactiveServer.SetHighPriority(); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception e) - { - Logger.LogError("Error occurred while recreating server! Attempting backup strategy of running DMB of running server! Exception: {0}", e.ToString()); - - // ahh jeez, what do we do here? - // this is our fault, so it should never happen but - // idk maybe a database error while handling the newest dmb? - // either way try to start it using the active server's dmb as a backup - try - { - var dmbBackup = await DmbFactory.FromCompileJob(monitorState.ActiveServer.Dmb.CompileJob, cancellationToken).ConfigureAwait(false); - - if (dmbBackup == null) // NANI!? - throw new InvalidOperationException("Watchdog double crit-fail!"); // just give up, if THAT compile job is failing then the ActiveServer is gonna crash soon too or already has - - monitorState.InactiveServer = await SessionControllerFactory.LaunchNew( - dmbBackup, - null, - ActiveLaunchParameters, - false, - !monitorState.ActiveServer.IsPrimary, - false, - cancellationToken) - .ConfigureAwait(false); - monitorState.InactiveServer.SetHighPriority(); - await Chat.SendWatchdogMessage( - "Staging newest DMB on inactive server failed: {0} Falling back to previous dmb...", - false, - cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception e2) - { - // fuuuuucckkk - Logger.LogError("Backup strategy failed! Monitor will restart when active server reboots! Exception: {0}", e2.ToString()); - monitorState.InactiveServerCritFail = true; - await Chat.SendWatchdogMessage( - "Attempted reboot of inactive server failed. Watchdog will reset when active server fails or exits", - false, - cancellationToken).ConfigureAwait(false); - return; - } - } - - Logger.LogInformation("Successfully relaunched inactive server!"); - monitorState.RebootingInactiveServer = true; - } - - static string ExitWord(ISessionController controller) => controller.TerminationWasRequested ? "exited" : "crashed"; - - // reason handling - switch (activationReason) - { - case MonitorActivationReason.ActiveServerCrashed: - if (monitorState.ActiveServer.RebootState == Session.RebootState.Shutdown) - { - // the time for graceful shutdown is now - await Chat.SendWatchdogMessage( - String.Format( - CultureInfo.InvariantCulture, - "Active server {0}! Shutting down due to graceful termination request...", - ExitWord(monitorState.ActiveServer)), - false, - cancellationToken).ConfigureAwait(false); - monitorState.NextAction = MonitorAction.Exit; - break; - } - - if (FullRestartDeadInactive()) - { - // tell chat about it and go ahead - await Chat.SendWatchdogMessage( - String.Format( - CultureInfo.InvariantCulture, - "Active server {0}! Inactive server unable to online!", - ExitWord(monitorState.ActiveServer)), - false, - cancellationToken).ConfigureAwait(false); - - // we've already been set to restart - break; - } - - // tell chat about it - await Chat.SendWatchdogMessage( - String.Format( - CultureInfo.InvariantCulture, - "Active server {0}! Onlining inactive server...", - ExitWord(monitorState.ActiveServer)), - false, - cancellationToken) - .ConfigureAwait(false); - - // try to activate the inactive server - if (!await MakeInactiveActive().ConfigureAwait(false)) - break; // failing that, we've already been set to restart - - // bring up another inactive server - await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); - break; - case MonitorActivationReason.InactiveServerCrashed: - // just announce and try to bring it back - await Chat.SendWatchdogMessage( - String.Format( - CultureInfo.InvariantCulture, - "Inactive server {0}! Rebooting...", - ExitWord(monitorState.InactiveServer)), - false, - cancellationToken) - .ConfigureAwait(false); - await UpdateAndRestartInactiveServer(false).ConfigureAwait(false); - break; - case MonitorActivationReason.ActiveServerRebooted: - // ideal goal: active server just closed its port - // tell inactive server to open it's port and that's now the active server - var rebootState = monitorState.ActiveServer.RebootState; - monitorState.ActiveServer.ResetRebootState(); // the DMAPI has already done this internally - - if (FullRestartDeadInactive() && rebootState != Session.RebootState.Shutdown) - break; // full restart if the inactive server is being fucky - - // what matters here is the RebootState - var restartOnceSwapped = false; - - switch (rebootState) - { - case Session.RebootState.Normal: - // life as normal - break; - case Session.RebootState.Restart: - // reboot the current active server once the inactive one activates - restartOnceSwapped = true; - break; - case Session.RebootState.Shutdown: - // graceful shutdown time - await Chat.SendWatchdogMessage( - "Active server rebooted! Shutting down due to graceful termination request...", - false, - cancellationToken) - .ConfigureAwait(false); - monitorState.NextAction = MonitorAction.Exit; - return; - default: - throw new InvalidOperationException($"Invalid reboot state: {rebootState}"); - } - - // are both servers now running the same CompileJob? - var sameCompileJob = monitorState.InactiveServer.Dmb.CompileJob.Id == monitorState.ActiveServer.Dmb.CompileJob.Id; - - if (!sameCompileJob || ActiveLaunchParameters != LastLaunchParameters) - restartOnceSwapped = true; // need a new launch to update either settings or compile job - - if (restartOnceSwapped) - /* - * we need to manually restart active server - * just kill it here, easier that way - */ - monitorState.ActiveServer.Dispose(); - - var activeServerStillHasPortOpen = !restartOnceSwapped && !monitorState.ActiveServer.ClosePortOnReboot; - - if (activeServerStillHasPortOpen) - /* we didn't want active server to swap for some reason and it still has it's port open - * just continue as normal - */ - break; - - if (!await MakeInactiveActive().ConfigureAwait(false)) - break; // monitor will restart - - // servers now swapped - // enable this now if inactive server is not still valid - monitorState.ActiveServer.ClosePortOnReboot = restartOnceSwapped; - - if (!restartOnceSwapped) - /* - * now try to reopen it on the private port - * failing that, just reboot it - */ - restartOnceSwapped = !await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.SecondaryPort.Value, cancellationToken).ConfigureAwait(false); - - // break either way because any issues past this point would be solved by the reboot - if (restartOnceSwapped) // for one reason or another - await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); // update and reboot - else - monitorState.NextAction = MonitorAction.Skip; // only skip checking inactive server rebooted, it's guaranteed InactiveServerStartup complete wouldn't fire this iteration - break; - case MonitorActivationReason.InactiveServerRebooted: - // just don't let the active server close it's port if the inactive server isn't ready - monitorState.RebootingInactiveServer = true; - monitorState.InactiveServer.ResetRebootState(); - monitorState.ActiveServer.ClosePortOnReboot = false; - monitorState.NextAction = MonitorAction.Continue; - break; - case MonitorActivationReason.InactiveServerStartupComplete: - // opposite of above case - monitorState.RebootingInactiveServer = false; - monitorState.ActiveServer.ClosePortOnReboot = true; - monitorState.NextAction = MonitorAction.Continue; - break; - case MonitorActivationReason.NewDmbAvailable: - case MonitorActivationReason.ActiveLaunchParametersUpdated: - // just reload the inactive server and wait for a swap to apply the changes - await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); - break; - case MonitorActivationReason.Heartbeat: - default: - throw new InvalidOperationException( - String.Format( - CultureInfo.InvariantCulture, - "Invalid monitor activation reason: {0}!", - activationReason)); - } - } -#pragma warning restore CA1502 - - /// - protected override void DisposeAndNullControllersImpl() - { - alphaServer?.Dispose(); - alphaServer = null; - bravoServer?.Dispose(); - bravoServer = null; - } - - /// - protected override IReadOnlyDictionary GetMonitoredServerTasks(MonitorState monitorState) - { - if (AlphaIsActive) - Logger.LogDebug("Alpha is the active server"); - else - Logger.LogDebug("Bravo is the active server"); - - if (monitorState.RebootingInactiveServer) - Logger.LogDebug("Inactive server is rebooting"); - - // update the monitor state with the inactive/active servers - monitorState.ActiveServer = AlphaIsActive ? alphaServer : bravoServer; - monitorState.InactiveServer = AlphaIsActive ? bravoServer : alphaServer; - - if (monitorState.ActiveServer.ClosePortOnReboot) - Logger.LogDebug("Active server will close port on reboot"); - if (monitorState.InactiveServer.ClosePortOnReboot) - Logger.LogDebug("Inactive server will close port on reboot"); - - Logger.LogDebug("Active server Compile Job ID: {0}", monitorState.ActiveServer.Dmb.CompileJob.Id); - Logger.LogDebug("Inactive server Compile Job ID: {0}", monitorState.InactiveServer.Dmb.CompileJob.Id); - - return new Dictionary - { - { MonitorActivationReason.ActiveServerCrashed, monitorState.ActiveServer.Lifetime }, - { MonitorActivationReason.ActiveServerRebooted, monitorState.ActiveServer.OnReboot }, - { MonitorActivationReason.InactiveServerCrashed, monitorState.InactiveServer.Lifetime }, - { MonitorActivationReason.InactiveServerRebooted, monitorState.InactiveServer.OnReboot }, - { MonitorActivationReason.InactiveServerStartupComplete, monitorState.InactiveServer.OnPrime } - }; - } - - /// - #pragma warning disable CA1502 // TODO: Decomplexify - protected override async Task InitControllers( - Task chatTask, - DualReattachInformation reattachInfo, - CancellationToken cancellationToken) - { - Debug.Assert(alphaServer == null && bravoServer == null, "Entered LaunchNoLock with one or more of the servers not being null!"); - - // don't need a new dmb if reattaching - var doesntNeedNewDmb = reattachInfo?.Alpha != null && reattachInfo?.Bravo != null; - var dmbToUse = doesntNeedNewDmb ? null : DmbFactory.LockNextDmb(2); - - // if this try catches something, both servers are killed - try - { - // start the alpha server task, either by launch a new process or attaching to an existing one - // The tasks returned are mainly for writing interop files to the directories among other things and should generally never fail - // The tasks pertaining to server startup times are in the ISessionControllers - Task alphaServerTask; - if (!doesntNeedNewDmb) - alphaServerTask = SessionControllerFactory.LaunchNew( - dmbToUse, - null, - ActiveLaunchParameters, - true, - true, - false, - cancellationToken); - else - alphaServerTask = SessionControllerFactory.Reattach(reattachInfo.Alpha, reattachInfo.TopicRequestTimeout, cancellationToken); - - // retrieve the session controller - var startTime = DateTimeOffset.Now; - alphaServer = await alphaServerTask.ConfigureAwait(false); - - // failed reattaches will return null - alphaServer?.SetHighPriority(); - - // extra delay for total ordering - var now = DateTimeOffset.Now; - var delay = now - startTime; - - // definitely not if reattaching though - if (reattachInfo == null && delay.TotalSeconds < AlphaBravoStartupSeperationInterval) - await AsyncDelayer.Delay(startTime.AddSeconds(AlphaBravoStartupSeperationInterval) - now, cancellationToken).ConfigureAwait(false); - - // now bring bravo up - if (!doesntNeedNewDmb) - bravoServer = await SessionControllerFactory.LaunchNew( - dmbToUse, - null, - ActiveLaunchParameters, - false, - false, - false, - cancellationToken) - .ConfigureAwait(false); - else - bravoServer = await SessionControllerFactory.Reattach( - reattachInfo.Bravo, - reattachInfo.TopicRequestTimeout, - cancellationToken).ConfigureAwait(false); - - // failed reattaches will return null - 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 entirely - await ReattachFailure(chatTask, !bothServersDead, cancellationToken).ConfigureAwait(false); - return; - } - - // we still have the active server but the other one is dead to us, hand it off to the monitor to restart - const string InactiveReattachFailureMessage = "Unable to reattach to inactive server. Leaving for monitor to reboot..."; - chatTask = Chat.SendWatchdogMessage(InactiveReattachFailureMessage, false, cancellationToken); - Logger.LogWarning(InactiveReattachFailureMessage); - - if (reattachInfo.AlphaIsActive) - bravoServer = SessionControllerFactory.CreateDeadSession(reattachInfo.Bravo.Dmb); - else - alphaServer = SessionControllerFactory.CreateDeadSession(reattachInfo.Alpha.Dmb); - } - - var alphaLrt = CheckLaunchResult(alphaServer, "Alpha", cancellationToken); - var bravoLrt = CheckLaunchResult(bravoServer, "Bravo", cancellationToken); - - // this task completes when both serers have finished booting - var allTask = Task.WhenAll(alphaLrt, bravoLrt); - - await allTask.ConfigureAwait(false); - - // both servers are now running, alpha is the active server(unless reattach), huzzah - alphaIsActive = reattachInfo?.AlphaIsActive ?? true; - - var activeServer = AlphaIsActive ? alphaServer : bravoServer; - activeServer.EnableCustomChatCommands(); - activeServer.ClosePortOnReboot = true; - } - catch - { - if (dmbToUse != null) - { - // we locked 2 dmbs - if (bravoServer == null) - { - // bravo didn't get control of his - dmbToUse.Dispose(); - if (alphaServer == null) - dmbToUse.Dispose(); // alpha didn't get control of his - } - } - else if (doesntNeedNewDmb) // we have reattachInfo - if (bravoServer == null) - { - // bravo didn't get control of his - reattachInfo.Bravo?.Dmb.Dispose(); - if (alphaServer == null) - reattachInfo.Alpha?.Dmb.Dispose(); // alpha didn't get control of his - } - - // kill the controllers - DisposeAndNullControllers(); - throw; - } - } - #pragma warning restore CA1502 - - /// - protected override ISessionController GetActiveController() => AlphaIsActive ? alphaServer : bravoServer; - - /// - protected override DualReattachInformation CreateReattachInformation() - => new DualReattachInformation - { - AlphaIsActive = AlphaIsActive, - Alpha = alphaServer?.Release(), - Bravo = bravoServer?.Release() - }; - - /// - public override Task InstanceRenamed(string newInstanceName, CancellationToken cancellationToken) - => Task.WhenAll( - alphaServer?.InstanceRenamed(newInstanceName, cancellationToken) ?? Task.CompletedTask, - bravoServer?.InstanceRenamed(newInstanceName, cancellationToken) ?? Task.CompletedTask); - } -} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs index b21186b5ff..4b0d046561 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs @@ -10,26 +10,11 @@ /// ActiveServerCrashed, - /// - /// The inactive server crashed or exited - /// - InactiveServerCrashed, - /// /// The active server called /world/Reboot() /// ActiveServerRebooted, - /// - /// The inactive server called /world/Reboot() - /// - InactiveServerRebooted, - - /// - /// The inactive server is past that point where DD hangs when you press "Go" - /// - InactiveServerStartupComplete, - /// /// A new .dmb was deployed /// diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorState.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorState.cs deleted file mode 100644 index a312a6a1c4..0000000000 --- a/src/Tgstation.Server.Host/Components/Watchdog/MonitorState.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Newtonsoft.Json; -using Tgstation.Server.Host.Components.Session; - -namespace Tgstation.Server.Host.Components.Watchdog -{ - /// - /// The (absolute) state of the - /// - sealed class MonitorState - { - /// - /// If the inactive server is being rebooted - /// - public bool RebootingInactiveServer { get; set; } - - /// - /// If the inactive server is in an unrecoverable state - /// - public bool InactiveServerCritFail { get; set; } - - /// - /// The next to take in - /// - public MonitorAction NextAction { get; set; } - - /// - /// The active - /// - [JsonIgnore] - public ISessionController ActiveServer { get; set; } - - /// - /// The inactive - /// - [JsonIgnore] - public ISessionController InactiveServer { get; set; } - } -} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/README.md b/src/Tgstation.Server.Host/Components/Watchdog/README.md index 8927a46a83..f9846a0db7 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/README.md +++ b/src/Tgstation.Server.Host/Components/Watchdog/README.md @@ -5,8 +5,7 @@ The watchdog is what keeps DreamDaemon alive and well. It's responsible for star There are three main implementations of the interface [IWatchdog](./IWatchdog.cs) that deal with how it handles the last of those jobs. - The [BasicWatchdog](./BasicWatchdog.cs) kills DreamDaemon when the world reboots and immediately restarts it with the newly compiled .dmb. -- The [WindowsWatchdog](./WindowsWatchdog.cs) uses a quirk of the interaction of DD with the Windows file system. A symlink to the game folder is created. From here, we launch the game. When a new .dmb is available, this symlink is deleted and then immediately recreated pointing to the new game directory. When DreamDaemon reboots it will load the new game. -- The [ExperimentalWatchdog](./ExperimentalWatchdog) starts new servers when the new .dmb comes in. But it uses some, rather invasive, port trickery to reroute traffic from the old game server to the new one. The idea for this is to always have two servers running so one can act as a fallback for if the main one fails. This is currently a big work-in-progress as it has issues with BYOND bugs and static file sharing. +- The [WindowsWatchdog](./WindowsWatchdog.cs) uses a quirk of the interaction of DD with the Windows file system. A symlink to the game folder is created. From here, we launch the game. When a new .dmb is available, this symlink is deleted and then immediately recreated pointing to the new game directory. When DreamDaemon reboots it will load the new game. [WatchdogBase](./WatchdogBase.cs) is the parent of all these implementations and contains the bulk of the monitoring code. More on that later. @@ -23,8 +22,7 @@ From the perspective of `WatchdogBase` - `InitControllers()` is called, which abstractly creates the `ISessionController`(s) for the watchdog. - For the `BasicWatchdog`, the controller is created normally. - For the `WindowsWatchdog`, we replace the `IDmbProvider` with a `WindowsSwappableDmbProvider` and setup the symlinking before deferring to the `BasicWatchdog` - - For the `ExperimentalWatchdog` we launch two servers in tandem. - - For reattaching, all three work similarly in that they use `ISessionControllerFactory` to reattach their sessions. The `ExperimentalWatchdog` will insert a `DeadSessionController` in place of one if only one server could not reattach. + - For reattaching, all three work similarly in that they use `ISessionControllerFactory` to reattach their sessions. - `MonitorLifetimes()` is called which is the "main loop" of the watchdog. ## Monitoring diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index 01e34a45af..a429411b0d 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using Serilog.Context; using System; using System.Collections.Generic; @@ -131,14 +130,14 @@ namespace Tgstation.Server.Host.Components.Watchdog readonly object controllerDisposeLock; /// - /// If the should in + /// If the should in /// readonly bool autoStart; /// /// Used when detaching servers. /// - DualReattachInformation releasedReattachInformation; + ReattachInformation releasedReattachInformation; /// /// The for the monitor loop @@ -293,12 +292,12 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// Handles a watchdog heartbeat. /// - /// The active . /// The for the operation. /// A resulting in the next to take. - async Task HandleHeartbeat(ISessionController activeServer, CancellationToken cancellationToken) + async Task HandleHeartbeat(CancellationToken cancellationToken) { Logger.LogTrace("Sending heartbeat to active server..."); + var activeServer = GetActiveController(); var response = await activeServer.SendCommand(new TopicParameters(), cancellationToken).ConfigureAwait(false); var shouldShutdown = activeServer.RebootState == Session.RebootState.Shutdown; @@ -347,10 +346,10 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// If should be started by this function /// If the launch should be announced to chat by this function - /// to use, if any + /// to use, if any /// The for the operation /// A representing the running operation - protected async Task LaunchNoLock(bool startMonitor, bool announce, DualReattachInformation reattachInfo, CancellationToken cancellationToken) + protected async Task LaunchNoLock(bool startMonitor, bool announce, ReattachInformation reattachInfo, CancellationToken cancellationToken) { Logger.LogTrace("Begin LaunchImplNoLock"); @@ -461,22 +460,17 @@ namespace Tgstation.Server.Host.Components.Watchdog } /// - /// Call from when a reattach operation fails to attempt a fresh start. + /// Call from when a reattach operation fails to attempt a fresh start. /// /// A, possibly active, for an outgoing chat message. - /// If the inactive server was successfully reattached. /// The for the operation. /// A representing the running operation. - protected async Task ReattachFailure(Task chatTask, bool reattachedInactive, CancellationToken cancellationToken) + protected async Task ReattachFailure(Task chatTask, CancellationToken cancellationToken) { // we lost the server, just restart entirely DisposeAndNullControllers(); const string FailReattachMessage = "Unable to properly reattach to server! Restarting watchdog..."; Logger.LogWarning(FailReattachMessage); - Logger.LogDebug( - reattachedInactive - ? "Also could not reattach to inactive server!" - : "Inactive server was reattached successfully!"); async Task ChainChatTask() { @@ -508,38 +502,22 @@ namespace Tgstation.Server.Host.Components.Watchdog /// The active . protected abstract ISessionController GetActiveController(); - /// - /// Create the for the s. - /// - /// A new . - protected abstract DualReattachInformation CreateReattachInformation(); - - /// - /// Gets the tasks for the following s: , , , , . - /// - /// The current . - /// A of the s keyed by their . - /// This function should not assume the servers are not . - protected abstract IReadOnlyDictionary GetMonitoredServerTasks(MonitorState monitorState); - /// /// Handles the actions to take when the monitor has to "wake up" /// /// The that caused the invocation. Will never be . - /// The current . /// The for the operation. - /// A representing the running operation. - protected abstract Task HandleMonitorWakeup( + /// A resulting in the to take. + protected abstract Task HandleMonitorWakeup( MonitorActivationReason activationReason, - MonitorState monitorState, CancellationToken cancellationToken); /// /// Attempt to restart the monitor from scratch. /// /// The for the operation. - /// A resulting in the new . - private async Task MonitorRestart(CancellationToken cancellationToken) + /// A representing the running operation. + private async Task MonitorRestart(CancellationToken cancellationToken) { Logger.LogTrace("Monitor restart!"); DisposeAndNullControllers(); @@ -554,8 +532,8 @@ namespace Tgstation.Server.Host.Components.Watchdog { // use LaunchImplNoLock without announcements or restarting the monitor await LaunchNoLock(false, false, null, cancellationToken).ConfigureAwait(false); - Logger.LogDebug("Relaunch successful, resetting monitor state..."); - return new MonitorState(); + Logger.LogDebug("Relaunch successful, resuming monitor..."); + return; } catch (OperationCanceledException) { @@ -603,25 +581,19 @@ namespace Tgstation.Server.Host.Components.Watchdog using var _ = cancellationToken.Register(() => Logger.LogTrace("Monitor cancellationToken triggered")); // this function is responsible for calling HandlerMonitorWakeup when necessary and manitaining the MonitorState - var iteration = 1; try { - for (var monitorState = new MonitorState(); monitorState.NextAction != MonitorAction.Exit; ++iteration) + MonitorAction nextAction = MonitorAction.Continue; + for (ulong iteration = 1; nextAction != MonitorAction.Exit; ++iteration) using (LogContext.PushProperty("Monitor", iteration)) try { Logger.LogTrace("Iteration {0} of monitor loop", iteration); + nextAction = MonitorAction.Continue; - // load the activation tasks into local variables - var serverTasks = GetMonitoredServerTasks(monitorState); - if (serverTasks.Count != 5) - throw new InvalidOperationException("Expected 5 monitored server tasks!"); - - var activeServerLifetime = serverTasks[MonitorActivationReason.ActiveServerCrashed]; - var activeServerReboot = serverTasks[MonitorActivationReason.ActiveServerRebooted]; - var inactiveServerLifetime = serverTasks[MonitorActivationReason.InactiveServerCrashed]; - var inactiveServerReboot = serverTasks[MonitorActivationReason.InactiveServerRebooted]; - var inactiveStartupComplete = serverTasks[MonitorActivationReason.InactiveServerStartupComplete]; + var controller = GetActiveController(); + Task activeServerLifetime = controller.Lifetime; + var activeServerReboot = controller.OnReboot; Task activeLaunchParametersChanged = ActiveParametersUpdated.Task; var newDmbAvailable = DmbFactory.OnNewerDmb; @@ -636,9 +608,6 @@ namespace Tgstation.Server.Host.Components.Watchdog var toWaitOn = Task.WhenAny( activeServerLifetime, activeServerReboot, - inactiveServerLifetime, - inactiveServerReboot, - inactiveStartupComplete, heartbeat, newDmbAvailable, cancelTcs.Task, @@ -655,7 +624,7 @@ namespace Tgstation.Server.Host.Components.Watchdog using (await SemaphoreSlimContext.Lock(Semaphore, cancellationToken).ConfigureAwait(false)) { // multiple things may have happened, handle them one at a time - for (var moreActivationsToProcess = true; moreActivationsToProcess && (monitorState.NextAction == MonitorAction.Continue || monitorState.NextAction == MonitorAction.Skip);) + for (var moreActivationsToProcess = true; moreActivationsToProcess && (nextAction == MonitorAction.Continue || nextAction == MonitorAction.Skip);) { MonitorActivationReason activationReason = default; // this will always be assigned before being used @@ -663,8 +632,8 @@ namespace Tgstation.Server.Host.Components.Watchdog { var taskCompleted = task?.IsCompleted == true; task = null; - if (monitorState.NextAction == MonitorAction.Skip) - monitorState.NextAction = MonitorAction.Continue; + if (nextAction == MonitorAction.Skip) + nextAction = MonitorAction.Continue; else if (taskCompleted) { activationReason = testActivationReason; @@ -678,9 +647,6 @@ namespace Tgstation.Server.Host.Components.Watchdog var anyActivation = CheckActivationReason(ref activeServerLifetime, MonitorActivationReason.ActiveServerCrashed) || CheckActivationReason(ref activeServerReboot, MonitorActivationReason.ActiveServerRebooted) || CheckActivationReason(ref newDmbAvailable, MonitorActivationReason.NewDmbAvailable) - || CheckActivationReason(ref inactiveServerLifetime, MonitorActivationReason.InactiveServerCrashed) - || CheckActivationReason(ref inactiveServerReboot, MonitorActivationReason.InactiveServerRebooted) - || CheckActivationReason(ref inactiveStartupComplete, MonitorActivationReason.InactiveServerStartupComplete) || CheckActivationReason(ref activeLaunchParametersChanged, MonitorActivationReason.ActiveLaunchParametersUpdated) || CheckActivationReason(ref heartbeat, MonitorActivationReason.Heartbeat); @@ -690,25 +656,26 @@ namespace Tgstation.Server.Host.Components.Watchdog { Logger.LogTrace("Reason: {0}", activationReason); if (activationReason == MonitorActivationReason.Heartbeat) - monitorState.NextAction = await HandleHeartbeat( - monitorState.ActiveServer, + nextAction = await HandleHeartbeat( cancellationToken) .ConfigureAwait(false); else - await HandleMonitorWakeup( + nextAction = await HandleMonitorWakeup( activationReason, - monitorState, cancellationToken) .ConfigureAwait(false); } } } - Logger.LogTrace("Next monitor action is to {0}", monitorState.NextAction); + Logger.LogTrace("Next monitor action is to {0}", nextAction); // Restart if requested - if (monitorState.NextAction == MonitorAction.Restart) - monitorState = await MonitorRestart(cancellationToken).ConfigureAwait(false); + if (nextAction == MonitorAction.Restart) + { + await MonitorRestart(cancellationToken).ConfigureAwait(false); + nextAction = MonitorAction.Continue; + } } catch (OperationCanceledException) { @@ -719,12 +686,11 @@ namespace Tgstation.Server.Host.Components.Watchdog { // really, this should NEVER happen Logger.LogError( - "Monitor crashed! Iteration: {0}, Monitor State: {1}, Exception: {2}", + "Monitor crashed! Iteration: {0}, Exception: {1}", iteration, - JsonConvert.SerializeObject(monitorState), e); - var nextActionMessage = monitorState.NextAction != MonitorAction.Exit + var nextActionMessage = nextAction != MonitorAction.Exit ? "Restarting" : "Shutting down"; var chatTask = Chat.SendWatchdogMessage( @@ -733,9 +699,12 @@ namespace Tgstation.Server.Host.Components.Watchdog cancellationToken); if (disposed) - monitorState.NextAction = MonitorAction.Exit; - else if (monitorState.NextAction != MonitorAction.Exit) - monitorState = await MonitorRestart(cancellationToken).ConfigureAwait(false); + nextAction = MonitorAction.Exit; + else if (nextAction != MonitorAction.Exit) + { + await MonitorRestart(cancellationToken).ConfigureAwait(false); + nextAction = MonitorAction.Continue; + } await chatTask.ConfigureAwait(false); } @@ -748,7 +717,7 @@ namespace Tgstation.Server.Host.Components.Watchdog if (releaseServers) { Logger.LogTrace("Detaching servers..."); - releasedReattachInformation = CreateReattachInformation(); + releasedReattachInformation = GetActiveController().Release(); } } @@ -762,10 +731,10 @@ namespace Tgstation.Server.Host.Components.Watchdog /// Starts all s. /// /// A, possibly active, for an outgoing chat message. - /// to use, if any + /// to use, if any /// The for the operation /// A representing the running operation - protected abstract Task InitControllers(Task chatTask, DualReattachInformation reattachInfo, CancellationToken cancellationToken); + protected abstract Task InitControllers(Task chatTask, ReattachInformation reattachInfo, CancellationToken cancellationToken); /// public async Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs index abbe180c96..08db4b64e9 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs @@ -73,60 +73,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } /// - public IWatchdog CreateWatchdog( - IChatManager chat, - IDmbFactory dmbFactory, - IReattachInfoHandler reattachInfoHandler, - ISessionControllerFactory sessionControllerFactory, - IIOManager gameIOManager, - IIOManager diagnosticsIOManager, - IEventConsumer eventConsumer, - Api.Models.Instance instance, - DreamDaemonSettings settings) - { - if (GeneralConfiguration.UseExperimentalWatchdog) - return new ExperimentalWatchdog( - chat, - sessionControllerFactory, - dmbFactory, - reattachInfoHandler, - DatabaseContextFactory, - JobManager, - ServerControl, - AsyncDelayer, - diagnosticsIOManager, - eventConsumer, - LoggerFactory.CreateLogger(), - settings, - instance, - settings.AutoStart.Value); - - return CreateNonExperimentalWatchdog( - chat, - dmbFactory, - reattachInfoHandler, - sessionControllerFactory, - gameIOManager, - diagnosticsIOManager, - eventConsumer, - instance, - settings); - } - - /// - /// Create a that isn't the . - /// - /// The for the - /// The for the with - /// The for the - /// The for the - /// The pointing to the Game directory for the . - /// The pointing to the Diagnostics directory for the . - /// The for the . - /// The for the - /// The initial for the - /// A new - protected virtual IWatchdog CreateNonExperimentalWatchdog( + public virtual IWatchdog CreateWatchdog( IChatManager chat, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs index f1f23e22f6..df48c507a5 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WindowsWatchdogFactory.cs @@ -54,7 +54,7 @@ namespace Tgstation.Server.Host.Components.Watchdog } /// - protected override IWatchdog CreateNonExperimentalWatchdog( + public override IWatchdog CreateWatchdog( IChatManager chat, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, diff --git a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs index 7c116a791e..0ed129618a 100644 --- a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs @@ -82,11 +82,6 @@ namespace Tgstation.Server.Host.Configuration /// public uint RestartTimeout { get; set; } = DefaultRestartTimeout; - /// - /// If the should be used. - /// - public bool UseExperimentalWatchdog { get; set; } - /// /// If the should not be used if it is available. /// diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index a9d6ec4d32..c33f04e713 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -125,13 +125,12 @@ namespace Tgstation.Server.Host.Controllers var llp = dd.LastLaunchParameters; var rstate = dd.RebootState; result.AutoStart = settings.AutoStart.Value; - result.CurrentPort = alphaActive ? llp?.PrimaryPort.Value : llp?.SecondaryPort.Value; + result.CurrentPort = llp?.Port.Value; result.CurrentSecurity = llp?.SecurityLevel.Value; result.CurrentAllowWebclient = llp?.AllowWebClient.Value; - result.PrimaryPort = settings.PrimaryPort.Value; + result.Port = settings.Port.Value; result.AllowWebClient = settings.AllowWebClient.Value; result.Status = dd.Status; - result.SecondaryPort = settings.SecondaryPort.Value; result.SecurityLevel = settings.SecurityLevel.Value; result.SoftRestart = rstate == RebootState.Restart; result.SoftShutdown = rstate == RebootState.Shutdown; @@ -181,7 +180,7 @@ namespace Tgstation.Server.Host.Controllers [HttpPost] [TgsAuthorize( DreamDaemonRights.SetAutoStart - | DreamDaemonRights.SetPorts + | DreamDaemonRights.SetPort | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart @@ -199,12 +198,6 @@ namespace Tgstation.Server.Host.Controllers if (model == null) throw new ArgumentNullException(nameof(model)); - if (model.PrimaryPort == 0) - throw new InvalidOperationException("Primary port cannot be 0!"); - - if (model.SecondaryPort == 0) - throw new InvalidOperationException("Secondary port cannot be 0!"); - if (model.SoftShutdown == true && model.SoftRestart == true) return BadRequest(new ErrorMessage(ErrorCode.DreamDaemonDoubleSoft)); @@ -245,8 +238,7 @@ namespace Tgstation.Server.Host.Controllers if (CheckModified(x => x.AllowWebClient, DreamDaemonRights.SetWebClient) || CheckModified(x => x.AutoStart, DreamDaemonRights.SetAutoStart) - || CheckModified(x => x.PrimaryPort, DreamDaemonRights.SetPorts) - || CheckModified(x => x.SecondaryPort, DreamDaemonRights.SetPorts) + || CheckModified(x => x.Port, DreamDaemonRights.SetPort) || CheckModified(x => x.SecurityLevel, DreamDaemonRights.SetSecurity) || (model.SoftRestart.HasValue && !AuthenticationContext.InstanceUser.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.SoftRestart)) || (model.SoftShutdown.HasValue && !AuthenticationContext.InstanceUser.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.SoftShutdown)) @@ -255,9 +247,6 @@ namespace Tgstation.Server.Host.Controllers || CheckModified(x => x.TopicRequestTimeout, DreamDaemonRights.SetTopicTimeout)) return Forbid(); - if (current.PrimaryPort == current.SecondaryPort) - return BadRequest(new ErrorMessage(ErrorCode.DreamDaemonDuplicatePorts)); - var wd = instanceManager.GetInstance(Instance).Watchdog; await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 0b93c494e4..db738f96c6 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -244,8 +244,7 @@ namespace Tgstation.Server.Host.Controllers { AllowWebClient = false, AutoStart = false, - PrimaryPort = 1337, - SecondaryPort = 1338, + Port = 1337, SecurityLevel = DreamDaemonSecurity.Safe, StartupTimeout = 60, HeartbeatSeconds = 60, @@ -329,24 +328,12 @@ namespace Tgstation.Server.Host.Controllers .Instances .AsQueryable() .Where(x => x.Id == id) - .Include(x => x.WatchdogReattachInformation) - .Include(x => x.WatchdogReattachInformation.Alpha) - .Include(x => x.WatchdogReattachInformation.Bravo) .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (originalModel == default) return Gone(); if (originalModel.Online.Value) return Conflict(new ErrorMessage(ErrorCode.InstanceDetachOnline)); - if (originalModel.WatchdogReattachInformation != null) - { - DatabaseContext.WatchdogReattachInformations.Remove(originalModel.WatchdogReattachInformation); - if (originalModel.WatchdogReattachInformation.Alpha != null) - DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Alpha); - if (originalModel.WatchdogReattachInformation.Bravo != null) - DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Bravo); - } - DatabaseContext.Instances.Remove(originalModel); var attachFileName = ioManager.ConcatPath(originalModel.Path, InstanceAttachFileName); diff --git a/src/Tgstation.Server.Host/Database/DatabaseContext.cs b/src/Tgstation.Server.Host/Database/DatabaseContext.cs index c9452aece5..d910d5c4d9 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseContext.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseContext.cs @@ -81,11 +81,6 @@ namespace Tgstation.Server.Host.Database /// public DbSet ReattachInformations { get; set; } - /// - /// The s in the . - /// - public DbSet WatchdogReattachInformations { get; set; } - /// /// The s in the /// @@ -137,9 +132,6 @@ namespace Tgstation.Server.Host.Database /// IDatabaseCollection IDatabaseContext.ReattachInformations => reattachInformationsCollection; - /// - IDatabaseCollection IDatabaseContext.WatchdogReattachInformations => watchdogReattachInformationsCollection; - /// /// Backing field for . /// @@ -200,11 +192,6 @@ namespace Tgstation.Server.Host.Database /// readonly IDatabaseCollection reattachInformationsCollection; - /// - /// Backing field for . - /// - readonly IDatabaseCollection watchdogReattachInformationsCollection; - /// /// Gets the configure action for a given . /// @@ -243,7 +230,6 @@ namespace Tgstation.Server.Host.Database revisionInformationsCollection = new DatabaseCollection(RevisionInformations); jobsCollection = new DatabaseCollection(Jobs); reattachInformationsCollection = new DatabaseCollection(ReattachInformations); - watchdogReattachInformationsCollection = new DatabaseCollection(WatchdogReattachInformations); } /// @@ -282,6 +268,8 @@ namespace Tgstation.Server.Host.Database compileJob.HasIndex(x => x.DirectoryName); compileJob.HasOne(x => x.Job).WithOne().OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne(x => x.CompileJob).WithMany().OnDelete(DeleteBehavior.Cascade); + var chatChannel = modelBuilder.Entity(); chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique(); chatChannel.HasIndex(x => new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique(); @@ -298,7 +286,6 @@ namespace Tgstation.Server.Host.Database instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); instanceModel.HasMany(x => x.InstanceUsers).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); - instanceModel.HasOne(x => x.WatchdogReattachInformation).WithOne().OnDelete(DeleteBehavior.Cascade); } /// diff --git a/src/Tgstation.Server.Host/Database/IDatabaseContext.cs b/src/Tgstation.Server.Host/Database/IDatabaseContext.cs index 36ce44583b..b7679599cc 100644 --- a/src/Tgstation.Server.Host/Database/IDatabaseContext.cs +++ b/src/Tgstation.Server.Host/Database/IDatabaseContext.cs @@ -73,11 +73,6 @@ namespace Tgstation.Server.Host.Database /// IDatabaseCollection ReattachInformations { get; } - /// - /// The for s - /// - IDatabaseCollection WatchdogReattachInformations { get; } - /// /// Saves changes made to the /// diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200629184803_MSRemoveExperimentalWatchdog.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20200629184803_MSRemoveExperimentalWatchdog.Designer.cs new file mode 100644 index 0000000000..2f6e0b3d8e --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200629184803_MSRemoveExperimentalWatchdog.Designer.cs @@ -0,0 +1,768 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(SqlServerDatabaseContext))] + [Migration("20200629184803_MSRemoveExperimentalWatchdog")] + partial class MSRemoveExperimentalWatchdog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.5") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ChannelLimit") + .HasColumnType("int"); + + b.Property("ConnectionString") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ReconnectionInterval") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("IrcChannel") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("Tag") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + 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() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ByondVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DMApiMajorVersion") + .HasColumnType("int"); + + b.Property("DMApiMinorVersion") + .HasColumnType("int"); + + b.Property("DMApiPatchVersion") + .HasColumnType("int"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("uniqueidentifier"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("int"); + + b.Property("Output") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + 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() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("bit"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("bit"); + + b.Property("HeartbeatSeconds") + .HasColumnType("bigint"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("SecurityLevel") + .HasColumnType("int"); + + b.Property("StartupTimeout") + .HasColumnType("bigint"); + + b.Property("TopicRequestTimeout") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ApiValidationPort") + .HasColumnType("int"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("int"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AutoUpdateInterval") + .HasColumnType("bigint"); + + b.Property("ChatBotLimit") + .HasColumnType("int"); + + b.Property("ConfigurationType") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("Online") + .IsRequired() + .HasColumnType("bit"); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ByondRights") + .HasColumnType("decimal(20,0)"); + + b.Property("ChatBotRights") + .HasColumnType("decimal(20,0)"); + + b.Property("ConfigurationRights") + .HasColumnType("decimal(20,0)"); + + b.Property("DreamDaemonRights") + .HasColumnType("decimal(20,0)"); + + b.Property("DreamMakerRights") + .HasColumnType("decimal(20,0)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstanceUserRights") + .HasColumnType("decimal(20,0)"); + + b.Property("RepositoryRights") + .HasColumnType("decimal(20,0)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("bigint"); + + 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() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CancelRight") + .HasColumnType("decimal(20,0)"); + + b.Property("CancelRightsType") + .HasColumnType("decimal(20,0)"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("bit"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ErrorCode") + .HasColumnType("bigint"); + + b.Property("ExceptionDetails") + .HasColumnType("nvarchar(max)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("datetimeoffset"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("datetimeoffset"); + + 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() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("RebootState") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessToken") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("AccessUser") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("bit"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("bit"); + + b.Property("CommitterEmail") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("CommitterName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("bit"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("bit"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CommitSha") + .IsRequired() + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Author") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Comment") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("MergedAt") + .HasColumnType("datetimeoffset"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("int"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("PullRequestRevision") + .IsRequired() + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + 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() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AdministrationRights") + .HasColumnType("decimal(20,0)"); + + b.Property("CanonicalName") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("datetimeoffset"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("bit"); + + b.Property("InstanceManagerRights") + .HasColumnType("decimal(20,0)"); + + b.Property("LastPasswordUpdate") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("SystemIdentifier") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("SystemIdentifier") + .IsUnique() + .HasFilter("[SystemIdentifier] IS NOT NULL"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstanceUsers") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", null) + .WithMany("InstanceUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200629184803_MSRemoveExperimentalWatchdog.cs b/src/Tgstation.Server.Host/Database/Migrations/20200629184803_MSRemoveExperimentalWatchdog.cs new file mode 100644 index 0000000000..80625db4ea --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200629184803_MSRemoveExperimentalWatchdog.cs @@ -0,0 +1,110 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + /// Removes the WatchdogReattachInformations table, SecondaryPort column, and renames the primary port column for MSSQL. + /// + public partial class MSRemoveExperimentalWatchdog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.DropTable( + name: "WatchdogReattachInformations"); + + migrationBuilder.DropColumn( + name: "IsPrimary", + table: "ReattachInformations"); + + migrationBuilder.DropColumn( + name: "SecondaryPort", + table: "DreamDaemonSettings"); + + migrationBuilder.RenameColumn( + name: "PrimaryPort", + table: "DreamDaemonSettings", + newName: "Port"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.RenameColumn( + name: "Port", + table: "DreamDaemonSettings", + newName: "PrimaryPort"); + + migrationBuilder.AddColumn( + name: "SecondaryPort", + table: "DreamDaemonSettings", + type: "int", + nullable: false, + defaultValue: 1338); + + migrationBuilder.AddColumn( + name: "IsPrimary", + table: "ReattachInformations", + type: "bit", + nullable: false, + defaultValue: true); + + migrationBuilder.CreateTable( + name: "WatchdogReattachInformations", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + AlphaId = table.Column(type: "bigint", nullable: true), + AlphaIsActive = table.Column(type: "bit", nullable: false), + BravoId = table.Column(type: "bigint", nullable: true), + InstanceId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId", + column: x => x.AlphaId, + principalTable: "ReattachInformations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId", + column: x => x.BravoId, + principalTable: "ReattachInformations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_Instances_InstanceId", + column: x => x.InstanceId, + principalTable: "Instances", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_AlphaId", + table: "WatchdogReattachInformations", + column: "AlphaId"); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_BravoId", + table: "WatchdogReattachInformations", + column: "BravoId"); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_InstanceId", + table: "WatchdogReattachInformations", + column: "InstanceId", + unique: true); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200629184843_MYRemoveExperimentalWatchdog.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20200629184843_MYRemoveExperimentalWatchdog.Designer.cs new file mode 100644 index 0000000000..3ecb71f0a3 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200629184843_MYRemoveExperimentalWatchdog.Designer.cs @@ -0,0 +1,758 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(MySqlDatabaseContext))] + [Migration("20200629184843_MYRemoveExperimentalWatchdog")] + partial class MYRemoveExperimentalWatchdog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.5") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ChannelLimit") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ConnectionString") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ReconnectionInterval") + .IsRequired() + .HasColumnType("int unsigned"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("bigint unsigned"); + + b.Property("IrcChannel") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("Tag") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + 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() + .HasColumnType("bigint"); + + b.Property("ByondVersion") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("DMApiMajorVersion") + .HasColumnType("int"); + + b.Property("DMApiMinorVersion") + .HasColumnType("int"); + + b.Property("DMApiPatchVersion") + .HasColumnType("int"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("char(36)"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("int"); + + b.Property("Output") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + 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() + .HasColumnType("bigint"); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("HeartbeatSeconds") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Port") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("SecurityLevel") + .HasColumnType("int"); + + b.Property("StartupTimeout") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("TopicRequestTimeout") + .IsRequired() + .HasColumnType("int unsigned"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ApiValidationPort") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("int"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AutoUpdateInterval") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("ChatBotLimit") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ConfigurationType") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("Online") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("Path") + .IsRequired() + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ByondRights") + .HasColumnType("bigint unsigned"); + + b.Property("ChatBotRights") + .HasColumnType("bigint unsigned"); + + b.Property("ConfigurationRights") + .HasColumnType("bigint unsigned"); + + b.Property("DreamDaemonRights") + .HasColumnType("bigint unsigned"); + + b.Property("DreamMakerRights") + .HasColumnType("bigint unsigned"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstanceUserRights") + .HasColumnType("bigint unsigned"); + + b.Property("RepositoryRights") + .HasColumnType("bigint unsigned"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("bigint"); + + 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() + .HasColumnType("bigint"); + + b.Property("CancelRight") + .HasColumnType("bigint unsigned"); + + b.Property("CancelRightsType") + .HasColumnType("bigint unsigned"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("ErrorCode") + .HasColumnType("int unsigned"); + + b.Property("ExceptionDetails") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("datetime(6)"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("datetime(6)"); + + 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() + .HasColumnType("bigint"); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("smallint unsigned"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("RebootState") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AccessToken") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("AccessUser") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("CommitterEmail") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("CommitterName") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CommitSha") + .IsRequired() + .HasColumnType("varchar(40) CHARACTER SET utf8mb4") + .HasMaxLength(40); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasColumnType("varchar(40) CHARACTER SET utf8mb4") + .HasMaxLength(40); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Author") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Comment") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("MergedAt") + .HasColumnType("datetime(6)"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("int"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("PullRequestRevision") + .IsRequired() + .HasColumnType("varchar(40) CHARACTER SET utf8mb4") + .HasMaxLength(40); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + 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() + .HasColumnType("bigint"); + + b.Property("AdministrationRights") + .HasColumnType("bigint unsigned"); + + b.Property("CanonicalName") + .IsRequired() + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("datetime(6)"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("InstanceManagerRights") + .HasColumnType("bigint unsigned"); + + b.Property("LastPasswordUpdate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("PasswordHash") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("SystemIdentifier") + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstanceUsers") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", null) + .WithMany("InstanceUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200629184843_MYRemoveExperimentalWatchdog.cs b/src/Tgstation.Server.Host/Database/Migrations/20200629184843_MYRemoveExperimentalWatchdog.cs new file mode 100644 index 0000000000..eae8c0c185 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200629184843_MYRemoveExperimentalWatchdog.cs @@ -0,0 +1,111 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + /// Removes the WatchdogReattachInformations table, SecondaryPort column, and renames the primary port column for MYSQL. + /// + public partial class MYRemoveExperimentalWatchdog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.DropTable( + name: "WatchdogReattachInformations"); + + migrationBuilder.DropColumn( + name: "IsPrimary", + table: "ReattachInformations"); + + migrationBuilder.DropColumn( + name: "SecondaryPort", + table: "DreamDaemonSettings"); + + migrationBuilder.RenameColumn( + name: "PrimaryPort", + table: "DreamDaemonSettings", + newName: "Port"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.RenameColumn( + name: "Port", + table: "DreamDaemonSettings", + newName: "PrimaryPort"); + + migrationBuilder.AddColumn( + name: "IsPrimary", + table: "ReattachInformations", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "SecondaryPort", + table: "DreamDaemonSettings", + type: "smallint unsigned", + nullable: false, + defaultValue: (ushort)1338); + + migrationBuilder.CreateTable( + name: "WatchdogReattachInformations", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + AlphaId = table.Column(type: "bigint", nullable: true), + AlphaIsActive = table.Column(type: "tinyint(1)", nullable: false), + BravoId = table.Column(type: "bigint", nullable: true), + InstanceId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId", + column: x => x.AlphaId, + principalTable: "ReattachInformations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId", + column: x => x.BravoId, + principalTable: "ReattachInformations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_Instances_InstanceId", + column: x => x.InstanceId, + principalTable: "Instances", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_AlphaId", + table: "WatchdogReattachInformations", + column: "AlphaId"); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_BravoId", + table: "WatchdogReattachInformations", + column: "BravoId"); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_InstanceId", + table: "WatchdogReattachInformations", + column: "InstanceId", + unique: true); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200629184928_PGRemoveExperimentalWatchdog.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20200629184928_PGRemoveExperimentalWatchdog.Designer.cs new file mode 100644 index 0000000000..bd149f4042 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200629184928_PGRemoveExperimentalWatchdog.Designer.cs @@ -0,0 +1,765 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(PostgresSqlDatabaseContext))] + [Migration("20200629184928_PGRemoveExperimentalWatchdog")] + partial class PGRemoveExperimentalWatchdog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ChannelLimit") + .HasColumnType("integer"); + + b.Property("ConnectionString") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("integer"); + + b.Property("ReconnectionInterval") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("numeric(20,0)"); + + b.Property("IrcChannel") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("Tag") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + 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() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ByondVersion") + .IsRequired() + .HasColumnType("text"); + + b.Property("DMApiMajorVersion") + .HasColumnType("integer"); + + b.Property("DMApiMinorVersion") + .HasColumnType("integer"); + + b.Property("DMApiPatchVersion") + .HasColumnType("integer"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("uuid"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("text"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("integer"); + + b.Property("Output") + .IsRequired() + .HasColumnType("text"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + 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() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("HeartbeatSeconds") + .HasColumnType("bigint"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Port") + .HasColumnType("integer"); + + b.Property("SecurityLevel") + .HasColumnType("integer"); + + b.Property("StartupTimeout") + .HasColumnType("bigint"); + + b.Property("TopicRequestTimeout") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ApiValidationPort") + .HasColumnType("integer"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("integer"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AutoUpdateInterval") + .HasColumnType("bigint"); + + b.Property("ChatBotLimit") + .HasColumnType("integer"); + + b.Property("ConfigurationType") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("Online") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ByondRights") + .HasColumnType("numeric(20,0)"); + + b.Property("ChatBotRights") + .HasColumnType("numeric(20,0)"); + + b.Property("ConfigurationRights") + .HasColumnType("numeric(20,0)"); + + b.Property("DreamDaemonRights") + .HasColumnType("numeric(20,0)"); + + b.Property("DreamMakerRights") + .HasColumnType("numeric(20,0)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstanceUserRights") + .HasColumnType("numeric(20,0)"); + + b.Property("RepositoryRights") + .HasColumnType("numeric(20,0)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("bigint"); + + 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() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CancelRight") + .HasColumnType("numeric(20,0)"); + + b.Property("CancelRightsType") + .HasColumnType("numeric(20,0)"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ErrorCode") + .HasColumnType("bigint"); + + b.Property("ExceptionDetails") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("timestamp with time zone"); + + 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() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("text"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("integer"); + + b.Property("Port") + .HasColumnType("integer"); + + b.Property("ProcessId") + .HasColumnType("integer"); + + b.Property("RebootState") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AccessToken") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("AccessUser") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("CommitterEmail") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("CommitterName") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CommitSha") + .IsRequired() + .HasColumnType("character varying(40)") + .HasMaxLength(40); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasColumnType("character varying(40)") + .HasMaxLength(40); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("text"); + + b.Property("Comment") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("MergedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("PullRequestRevision") + .IsRequired() + .HasColumnType("character varying(40)") + .HasMaxLength(40); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("text"); + + b.Property("Url") + .IsRequired() + .HasColumnType("text"); + + 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() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AdministrationRights") + .HasColumnType("numeric(20,0)"); + + b.Property("CanonicalName") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("InstanceManagerRights") + .HasColumnType("numeric(20,0)"); + + b.Property("LastPasswordUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("SystemIdentifier") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstanceUsers") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", null) + .WithMany("InstanceUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200629184928_PGRemoveExperimentalWatchdog.cs b/src/Tgstation.Server.Host/Database/Migrations/20200629184928_PGRemoveExperimentalWatchdog.cs new file mode 100644 index 0000000000..882ed08b62 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200629184928_PGRemoveExperimentalWatchdog.cs @@ -0,0 +1,111 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using System; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + /// Removes the WatchdogReattachInformations table, SecondaryPort column, and renames the primary port column for PostgresSQL. + /// + public partial class PGRemoveExperimentalWatchdog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.DropTable( + name: "WatchdogReattachInformations"); + + migrationBuilder.DropColumn( + name: "IsPrimary", + table: "ReattachInformations"); + + migrationBuilder.DropColumn( + name: "SecondaryPort", + table: "DreamDaemonSettings"); + + migrationBuilder.RenameColumn( + name: "PrimaryPort", + table: "DreamDaemonSettings", + newName: "Port"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.RenameColumn( + name: "Port", + table: "DreamDaemonSettings", + newName: "PrimaryPort"); + + migrationBuilder.AddColumn( + name: "IsPrimary", + table: "ReattachInformations", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "SecondaryPort", + table: "DreamDaemonSettings", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "WatchdogReattachInformations", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + AlphaId = table.Column(type: "bigint", nullable: true), + AlphaIsActive = table.Column(type: "boolean", nullable: false), + BravoId = table.Column(type: "bigint", nullable: true), + InstanceId = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId", + column: x => x.AlphaId, + principalTable: "ReattachInformations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId", + column: x => x.BravoId, + principalTable: "ReattachInformations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_Instances_InstanceId", + column: x => x.InstanceId, + principalTable: "Instances", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_AlphaId", + table: "WatchdogReattachInformations", + column: "AlphaId"); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_BravoId", + table: "WatchdogReattachInformations", + column: "BravoId"); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_InstanceId", + table: "WatchdogReattachInformations", + column: "InstanceId", + unique: true); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200629185014_SLRemoveExperimentalWatchdog.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20200629185014_SLRemoveExperimentalWatchdog.Designer.cs new file mode 100644 index 0000000000..df792241aa --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200629185014_SLRemoveExperimentalWatchdog.Designer.cs @@ -0,0 +1,757 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(SqliteDatabaseContext))] + [Migration("20200629185014_SLRemoveExperimentalWatchdog")] + partial class SLRemoveExperimentalWatchdog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.5"); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChannelLimit") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ConnectionString") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("INTEGER"); + + b.Property("ReconnectionInterval") + .IsRequired() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChatSettingsId") + .HasColumnType("INTEGER"); + + b.Property("DiscordChannelId") + .HasColumnType("INTEGER"); + + b.Property("IrcChannel") + .HasColumnType("TEXT") + .HasMaxLength(100); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("Tag") + .HasColumnType("TEXT") + .HasMaxLength(10000); + + 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() + .HasColumnType("INTEGER"); + + b.Property("ByondVersion") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DMApiMajorVersion") + .HasColumnType("INTEGER"); + + b.Property("DMApiMinorVersion") + .HasColumnType("INTEGER"); + + b.Property("DMApiPatchVersion") + .HasColumnType("INTEGER"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("JobId") + .HasColumnType("INTEGER"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("INTEGER"); + + b.Property("Output") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RevisionInformationId") + .HasColumnType("INTEGER"); + + 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() + .HasColumnType("INTEGER"); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("HeartbeatSeconds") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("Port") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("SecurityLevel") + .HasColumnType("INTEGER"); + + b.Property("StartupTimeout") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("TopicRequestTimeout") + .IsRequired() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ApiValidationPort") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("INTEGER"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("ProjectName") + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AutoUpdateInterval") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ChatBotLimit") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ConfigurationType") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.Property("Online") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ByondRights") + .HasColumnType("INTEGER"); + + b.Property("ChatBotRights") + .HasColumnType("INTEGER"); + + b.Property("ConfigurationRights") + .HasColumnType("INTEGER"); + + b.Property("DreamDaemonRights") + .HasColumnType("INTEGER"); + + b.Property("DreamMakerRights") + .HasColumnType("INTEGER"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("InstanceUserRights") + .HasColumnType("INTEGER"); + + b.Property("RepositoryRights") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("INTEGER"); + + 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() + .HasColumnType("INTEGER"); + + b.Property("CancelRight") + .HasColumnType("INTEGER"); + + b.Property("CancelRightsType") + .HasColumnType("INTEGER"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("CancelledById") + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ErrorCode") + .HasColumnType("INTEGER"); + + b.Property("ExceptionDetails") + .HasColumnType("TEXT"); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StartedById") + .HasColumnType("INTEGER"); + + b.Property("StoppedAt") + .HasColumnType("TEXT"); + + 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() + .HasColumnType("INTEGER"); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CompileJobId") + .HasColumnType("INTEGER"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("INTEGER"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("ProcessId") + .HasColumnType("INTEGER"); + + b.Property("RebootState") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AccessToken") + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.Property("AccessUser") + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("CommitterEmail") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.Property("CommitterName") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RevisionInformationId") + .HasColumnType("INTEGER"); + + b.Property("TestMergeId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CommitSha") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(40); + + b.Property("InstanceId") + .HasColumnType("INTEGER"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(40); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Author") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Comment") + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.Property("MergedAt") + .HasColumnType("TEXT"); + + b.Property("MergedById") + .HasColumnType("INTEGER"); + + b.Property("Number") + .HasColumnType("INTEGER"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("PullRequestRevision") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(40); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT"); + + 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() + .HasColumnType("INTEGER"); + + b.Property("AdministrationRights") + .HasColumnType("INTEGER"); + + b.Property("CanonicalName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedById") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("INTEGER"); + + b.Property("InstanceManagerRights") + .HasColumnType("INTEGER"); + + b.Property("LastPasswordUpdate") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(10000); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("SystemIdentifier") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstanceUsers") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", null) + .WithMany("InstanceUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200629185014_SLRemoveExperimentalWatchdog.cs b/src/Tgstation.Server.Host/Database/Migrations/20200629185014_SLRemoveExperimentalWatchdog.cs new file mode 100644 index 0000000000..c5ac6036a2 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20200629185014_SLRemoveExperimentalWatchdog.cs @@ -0,0 +1,166 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + /// Removes the WatchdogReattachInformations table, SecondaryPort column, and renames the primary port column for SQLite. + /// + public partial class SLRemoveExperimentalWatchdog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.DropTable( + name: "WatchdogReattachInformations"); + + migrationBuilder.RenameTable( + name: "ReattachInformations", + newName: "ReattachInformations_up"); + + migrationBuilder.CreateTable( + name: "ReattachInformations", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AccessIdentifier = table.Column(nullable: false), + ProcessId = table.Column(nullable: false), + Port = table.Column(nullable: false), + RebootState = table.Column(nullable: false), + LaunchSecurityLevel = table.Column(nullable: false), + CompileJobId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ReattachInformations", x => x.Id); + table.ForeignKey( + name: "FK_ReattachInformations_CompileJobs_CompileJobId", + column: x => x.CompileJobId, + principalTable: "CompileJobs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.Sql( + $"INSERT INTO ReattachInformations SELECT Id,AccessIdentifier,ProcessId,Port,RebootState,LaunchSecurityLevel,CompileJobId FROM DreamDaemonSettings_up"); + + migrationBuilder.DropTable( + name: "ReattachInformations_up"); + + migrationBuilder.RenameTable( + name: "DreamDaemonSettings", + newName: "DreamDaemonSettings_up"); + + migrationBuilder.CreateTable( + name: "DreamDaemonSettings", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AllowWebClient = table.Column(nullable: false), + SecurityLevel = table.Column(nullable: false), + Port = table.Column(nullable: false), + StartupTimeout = table.Column(nullable: false), + HeartbeatSeconds = table.Column(nullable: false), + AutoStart = table.Column(nullable: false), + InstanceId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DreamDaemonSettings", x => x.Id); + table.ForeignKey( + name: "FK_DreamDaemonSettings_Instances_InstanceId", + column: x => x.InstanceId, + principalTable: "Instances", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.Sql( + $"INSERT INTO DreamDaemonSettings SELECT Id,AllowWebClient,SecurityLevel,PrimaryPort,AutoStart,HeartbeatSeconds,StartupTimeout,InstanceId FROM DreamDaemonSettings_up"); + + migrationBuilder.DropTable( + name: "DreamDaemonSettings_up"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.RenameColumn( + name: "Port", + table: "DreamDaemonSettings", + newName: "PrimaryPort"); + + migrationBuilder.AddColumn( + name: "SecondaryPort", + table: "DreamDaemonSettings", + type: "INTEGER", + nullable: false, + defaultValue: (ushort)0); + + migrationBuilder.AddColumn( + name: "IsPrimary", + table: "ReattachInformations", + type: "INTEGER", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "WatchdogReattachInformations", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AlphaId = table.Column(type: "INTEGER", nullable: true), + AlphaIsActive = table.Column(type: "INTEGER", nullable: false), + BravoId = table.Column(type: "INTEGER", nullable: true), + InstanceId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId", + column: x => x.AlphaId, + principalTable: "ReattachInformations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId", + column: x => x.BravoId, + principalTable: "ReattachInformations", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_WatchdogReattachInformations_Instances_InstanceId", + column: x => x.InstanceId, + principalTable: "Instances", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_AlphaId", + table: "WatchdogReattachInformations", + column: "AlphaId"); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_BravoId", + table: "WatchdogReattachInformations", + column: "BravoId"); + + migrationBuilder.CreateIndex( + name: "IX_WatchdogReattachInformations_InstanceId", + table: "WatchdogReattachInformations", + column: "InstanceId", + unique: true); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs index 576eab3ec8..f840b89dfb 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs @@ -5,15 +5,15 @@ using Microsoft.EntityFrameworkCore.Infrastructure; namespace Tgstation.Server.Host.Database.Migrations { + /// [DbContext(typeof(MySqlDatabaseContext))] partial class MySqlDatabaseContextModelSnapshot : ModelSnapshot { - /// protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => @@ -173,11 +173,7 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("InstanceId") .HasColumnType("bigint"); - b.Property("PrimaryPort") - .IsRequired() - .HasColumnType("smallint unsigned"); - - b.Property("SecondaryPort") + b.Property("Port") .IsRequired() .HasColumnType("smallint unsigned"); @@ -228,36 +224,6 @@ namespace Tgstation.Server.Host.Database.Migrations b.ToTable("DreamMakerSettings"); }); - modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - b.Property("AlphaId") - .HasColumnType("bigint"); - - b.Property("AlphaIsActive") - .HasColumnType("tinyint(1)"); - - b.Property("BravoId") - .HasColumnType("bigint"); - - b.Property("InstanceId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("AlphaId"); - - b.HasIndex("BravoId"); - - b.HasIndex("InstanceId") - .IsUnique(); - - b.ToTable("WatchdogReattachInformations"); - }); - modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => { b.Property("Id") @@ -406,9 +372,6 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("CompileJobId") .HasColumnType("bigint"); - b.Property("IsPrimary") - .HasColumnType("tinyint(1)"); - b.Property("LaunchSecurityLevel") .HasColumnType("int"); @@ -690,23 +653,6 @@ namespace Tgstation.Server.Host.Database.Migrations .IsRequired(); }); - modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", 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", null) - .WithOne("WatchdogReattachInformation") - .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => { b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") diff --git a/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs index 1d350f6548..69f89c1178 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Tgstation.Server.Host.Database.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) - .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 63); modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => @@ -176,10 +176,7 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("InstanceId") .HasColumnType("bigint"); - b.Property("PrimaryPort") - .HasColumnType("integer"); - - b.Property("SecondaryPort") + b.Property("Port") .HasColumnType("integer"); b.Property("SecurityLevel") @@ -227,37 +224,6 @@ namespace Tgstation.Server.Host.Database.Migrations b.ToTable("DreamMakerSettings"); }); - modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("AlphaId") - .HasColumnType("bigint"); - - b.Property("AlphaIsActive") - .HasColumnType("boolean"); - - b.Property("BravoId") - .HasColumnType("bigint"); - - b.Property("InstanceId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("AlphaId"); - - b.HasIndex("BravoId"); - - b.HasIndex("InstanceId") - .IsUnique(); - - b.ToTable("WatchdogReattachInformations"); - }); - modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => { b.Property("Id") @@ -408,9 +374,6 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("CompileJobId") .HasColumnType("bigint"); - b.Property("IsPrimary") - .HasColumnType("boolean"); - b.Property("LaunchSecurityLevel") .HasColumnType("integer"); @@ -697,23 +660,6 @@ namespace Tgstation.Server.Host.Database.Migrations .IsRequired(); }); - modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", 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", null) - .WithOne("WatchdogReattachInformation") - .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => { b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") diff --git a/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs index 3e92e1d7e8..554095fd95 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Host.Database.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -178,10 +178,7 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("InstanceId") .HasColumnType("bigint"); - b.Property("PrimaryPort") - .HasColumnType("int"); - - b.Property("SecondaryPort") + b.Property("Port") .HasColumnType("int"); b.Property("SecurityLevel") @@ -229,37 +226,6 @@ namespace Tgstation.Server.Host.Database.Migrations b.ToTable("DreamMakerSettings"); }); - modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("AlphaId") - .HasColumnType("bigint"); - - b.Property("AlphaIsActive") - .HasColumnType("bit"); - - b.Property("BravoId") - .HasColumnType("bigint"); - - b.Property("InstanceId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("AlphaId"); - - b.HasIndex("BravoId"); - - b.HasIndex("InstanceId") - .IsUnique(); - - b.ToTable("WatchdogReattachInformations"); - }); - modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => { b.Property("Id") @@ -410,9 +376,6 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("CompileJobId") .HasColumnType("bigint"); - b.Property("IsPrimary") - .HasColumnType("bit"); - b.Property("LaunchSecurityLevel") .HasColumnType("int"); @@ -700,23 +663,6 @@ namespace Tgstation.Server.Host.Database.Migrations .IsRequired(); }); - modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", 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", null) - .WithOne("WatchdogReattachInformation") - .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => { b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") diff --git a/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs index 9985409c3c..4e48b24672 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/SqliteDatabaseContextModelSnapshot.cs @@ -13,7 +13,7 @@ namespace Tgstation.Server.Host.Database.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.4"); + .HasAnnotation("ProductVersion", "3.1.5"); modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => { @@ -172,11 +172,7 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("InstanceId") .HasColumnType("INTEGER"); - b.Property("PrimaryPort") - .IsRequired() - .HasColumnType("INTEGER"); - - b.Property("SecondaryPort") + b.Property("Port") .IsRequired() .HasColumnType("INTEGER"); @@ -227,36 +223,6 @@ namespace Tgstation.Server.Host.Database.Migrations b.ToTable("DreamMakerSettings"); }); - modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AlphaId") - .HasColumnType("INTEGER"); - - b.Property("AlphaIsActive") - .HasColumnType("INTEGER"); - - b.Property("BravoId") - .HasColumnType("INTEGER"); - - b.Property("InstanceId") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("AlphaId"); - - b.HasIndex("BravoId"); - - b.HasIndex("InstanceId") - .IsUnique(); - - b.ToTable("WatchdogReattachInformations"); - }); - modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => { b.Property("Id") @@ -405,9 +371,6 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("CompileJobId") .HasColumnType("INTEGER"); - b.Property("IsPrimary") - .HasColumnType("INTEGER"); - b.Property("LaunchSecurityLevel") .HasColumnType("INTEGER"); @@ -689,23 +652,6 @@ namespace Tgstation.Server.Host.Database.Migrations .IsRequired(); }); - modelBuilder.Entity("Tgstation.Server.Host.Models.DualReattachInformation", 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", null) - .WithOne("WatchdogReattachInformation") - .HasForeignKey("Tgstation.Server.Host.Models.DualReattachInformation", "InstanceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => { b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") diff --git a/src/Tgstation.Server.Host/Models/DualReattachInformation.cs b/src/Tgstation.Server.Host/Models/DualReattachInformation.cs deleted file mode 100644 index 0669e4f88c..0000000000 --- a/src/Tgstation.Server.Host/Models/DualReattachInformation.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Tgstation.Server.Host.Models -{ - /// - /// Database representation of - /// - public sealed class DualReattachInformation : DualReattachInformationBase - { - /// - /// The row Id - /// - public long Id { get; set; } - - /// - /// The of the the belongs to - /// - public long InstanceId { get; set; } - - /// - /// The of . - /// - public long? AlphaId { get; set; } - - /// - /// The for the Alpha server - /// - public ReattachInformation Alpha { get; set; } - - /// - /// The of . - /// - public long? BravoId { get; set; } - - /// - /// The for the Bravo server - /// - public ReattachInformation Bravo { get; set; } - } -} diff --git a/src/Tgstation.Server.Host/Models/DualReattachInformationBase.cs b/src/Tgstation.Server.Host/Models/DualReattachInformationBase.cs deleted file mode 100644 index 9e61fe1d83..0000000000 --- a/src/Tgstation.Server.Host/Models/DualReattachInformationBase.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace Tgstation.Server.Host.Models -{ - /// - /// Base class for - /// - public abstract class DualReattachInformationBase - { - /// - /// If the Alpha session is the active session - /// - public bool AlphaIsActive { get; set; } - - /// - /// Construct a - /// - public DualReattachInformationBase() { } - - /// - /// Construct a from a given - /// - /// The to copy values from - protected DualReattachInformationBase(DualReattachInformationBase copy) - { - AlphaIsActive = copy?.AlphaIsActive ?? throw new ArgumentNullException(nameof(copy)); - } - } -} diff --git a/src/Tgstation.Server.Host/Models/Instance.cs b/src/Tgstation.Server.Host/Models/Instance.cs index 0356bf0f2e..6a87d51bca 100644 --- a/src/Tgstation.Server.Host/Models/Instance.cs +++ b/src/Tgstation.Server.Host/Models/Instance.cs @@ -27,11 +27,6 @@ namespace Tgstation.Server.Host.Models /// public RepositorySettings RepositorySettings { get; set; } - /// - /// The for the - /// - public DualReattachInformation WatchdogReattachInformation { get; set; } - /// /// The s in the /// diff --git a/src/Tgstation.Server.Host/Models/ReattachInformationBase.cs b/src/Tgstation.Server.Host/Models/ReattachInformationBase.cs index 6fab1d26c0..893eed1710 100644 --- a/src/Tgstation.Server.Host/Models/ReattachInformationBase.cs +++ b/src/Tgstation.Server.Host/Models/ReattachInformationBase.cs @@ -17,11 +17,6 @@ namespace Tgstation.Server.Host.Models /// public int ProcessId { get; set; } - /// - /// If the of the associated dmb is being used - /// - public bool IsPrimary { get; set; } - /// /// The port DreamDaemon was last listening on /// @@ -52,7 +47,6 @@ namespace Tgstation.Server.Host.Models if (copy == null) throw new ArgumentNullException(nameof(copy)); AccessIdentifier = copy.AccessIdentifier; - IsPrimary = copy.IsPrimary; Port = copy.Port; ProcessId = copy.ProcessId; RebootState = copy.RebootState; @@ -60,6 +54,6 @@ namespace Tgstation.Server.Host.Models } /// - public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Process ID: {3}, Access Identifier {4}, Primary: {0}, RebootState: {1}, Port: {2}", IsPrimary, RebootState, Port, ProcessId, AccessIdentifier); + public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Process ID: {0}, Access Identifier {1}, RebootState: {2}, Port: {3}", ProcessId, AccessIdentifier, RebootState, Port); } } diff --git a/src/Tgstation.Server.Host/Setup/SetupWizard.cs b/src/Tgstation.Server.Host/Setup/SetupWizard.cs index 37984ab962..4d4942eef2 100644 --- a/src/Tgstation.Server.Host/Setup/SetupWizard.cs +++ b/src/Tgstation.Server.Host/Setup/SetupWizard.cs @@ -613,7 +613,6 @@ namespace Tgstation.Server.Host.Setup if (String.IsNullOrWhiteSpace(newGeneralConfiguration.GitHubAccessToken)) newGeneralConfiguration.GitHubAccessToken = null; - // newGeneralConfiguration.UseExperimentalWatchdog = await PromptYesNo("Use the experimental watchdog (NOT RECOMMENDED)? (y/n): ", cancellationToken).ConfigureAwait(false); return newGeneralConfiguration; } diff --git a/src/Tgstation.Server.Host/appsettings.json b/src/Tgstation.Server.Host/appsettings.json index 743ada3032..0802590406 100644 --- a/src/Tgstation.Server.Host/appsettings.json +++ b/src/Tgstation.Server.Host/appsettings.json @@ -6,7 +6,6 @@ "ByondTopicTimeout": 5000, "RestartTimeout": 60000, "ApiPort": 5000, - "UseExperimentalWatchdog": false, "UseBasicWatchdogOnWindows": false, "UserLimit": 100, "InstanceLimit": 10, diff --git a/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs index 4a8c12006f..a568f58a7c 100644 --- a/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs @@ -37,6 +37,11 @@ namespace Tgstation.Server.Tests.Instance HeartbeatSeconds = 0 }, cancellationToken); + await ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemon + { + Port = 0 + }, cancellationToken), ErrorCode.ModelValidationFailure); + await ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemon { SoftShutdown = true, diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs index df061b57bf..637a16ad9c 100644 --- a/tests/Tgstation.Server.Tests/IntegrationTest.cs +++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs @@ -62,7 +62,7 @@ namespace Tgstation.Server.Tests } } while (true); - var testUpdateVersion = new Version(4, 1, 4); + var testUpdateVersion = new Version(4, 3, 0); using (adminClient) //attempt to update to stable await adminClient.Administration.Update(new Administration