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
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