diff --git a/src/Tgstation.Server.Api/Models/CompilerStatus.cs b/src/Tgstation.Server.Api/Models/CompilerStatus.cs
index dc71d316f9..9cb0a7a5ed 100644
--- a/src/Tgstation.Server.Api/Models/CompilerStatus.cs
+++ b/src/Tgstation.Server.Api/Models/CompilerStatus.cs
@@ -32,6 +32,10 @@
///
Verifying,
///
+ /// Post-compile scripts are running
+ ///
+ PostCompile,
+ ///
/// The compile results are being duplicated
///
Duplicating,
@@ -40,10 +44,6 @@
///
Symlinking,
///
- /// Post-compile scripts are running
- ///
- PostCompile,
- ///
/// A failed compile job is being erased
///
Cleanup
diff --git a/src/Tgstation.Server.Api/Models/DreamDaemonSecurity.cs b/src/Tgstation.Server.Api/Models/DreamDaemonSecurity.cs
index 094332d1e4..68804af41b 100644
--- a/src/Tgstation.Server.Api/Models/DreamDaemonSecurity.cs
+++ b/src/Tgstation.Server.Api/Models/DreamDaemonSecurity.cs
@@ -14,7 +14,7 @@
///
Safe,
///
- /// Server will not be able to run shell commands or access anything but temporary files
+ /// Server will not be able to run shell commands or access anything but temporary files. Currently unsupported!
///
Ultrasafe
}
diff --git a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs
index 858bc3792b..4a62f8130e 100644
--- a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs
+++ b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs
@@ -100,22 +100,24 @@ namespace Tgstation.Server.Host.Components.Compiler
/// Run a quick DD instance to test the DMAPI is installed on the target code
///
/// The timeout in seconds for validation
+ /// The level to use to validate the API
/// The for the operation
/// The current
/// The for the operation
/// A resulting in if the DMAPI was successfully validated, otherwise
- async Task VerifyApi(uint timeout, Models.CompileJob job, IByondExecutableLock byondLock, CancellationToken cancellationToken)
+ async Task VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, CancellationToken cancellationToken)
{
+ logger.LogTrace("Verifying DMAPI...");
var launchParameters = new DreamDaemonLaunchParameters
{
AllowWebClient = false,
PrimaryPort = 0, //pick any port
- SecurityLevel = DreamDaemonSecurity.Safe, //all it needs to read the file and exit
+ SecurityLevel = securityLevel, //all it needs to read the file and exit
StartupTimeout = timeout
};
var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
- var provider = new TemporaryDmbProvider(ioManager.ResolvePath(dirA), String.Join('.', job.DmeName, DmbExtension), job);
+ var provider = new TemporaryDmbProvider(ioManager.ResolvePath(dirA), String.Concat(job.DmeName, DmbExtension), job);
var timeoutAt = DateTimeOffset.Now.AddSeconds(timeout);
using (var controller = await sessionControllerFactory.LaunchNew(launchParameters, provider, byondLock, true, true, true, cancellationToken).ConfigureAwait(false))
@@ -129,9 +131,14 @@ namespace Tgstation.Server.Host.Components.Compiler
cancellationToken.ThrowIfCancellationRequested();
}
if (!controller.Lifetime.IsCompleted)
+ {
+ logger.LogDebug("API validation timed out!");
return false;
+ }
- return controller.ApiValidated;
+ var validated = controller.ApiValidated;
+ logger.LogTrace("API valid: {0}", validated);
+ return validated;
}
}
@@ -167,6 +174,7 @@ namespace Tgstation.Server.Host.Components.Compiler
var dmTcs = new TaskCompletionSource();
dm.Exited += (a, b) => dmTcs.TrySetResult(null);
+ logger.LogTrace("Running DreamMaker...");
dm.Start();
dm.BeginOutputReadLine();
dm.BeginErrorReadLine();
@@ -184,8 +192,10 @@ namespace Tgstation.Server.Host.Components.Compiler
}
}
- job.Output = OutputList.ToString();
job.ExitCode = dm.ExitCode;
+ logger.LogDebug("DreamMaker exit code: {0}", job.ExitCode);
+ job.Output = OutputList.ToString();
+ logger.LogTrace("DreamMaker output: {0}", job.Output);
}
}
@@ -209,7 +219,18 @@ namespace Tgstation.Server.Host.Components.Compiler
var dmeModifications = await dmeModificationsTask.ConfigureAwait(false);
if (dmeModifications == null || dmeModifications.TotalDmeOverwrite)
+ {
+ if (dmeModifications != null)
+ logger.LogDebug(".dme replacement configured!");
+ else
+ logger.LogTrace("No .dme modifications required.");
return;
+ }
+
+ if (dmeModifications.HeadIncludeLine != null)
+ logger.LogDebug("Head .dme include line: {0}", dmeModifications.HeadIncludeLine);
+ if (dmeModifications.TailIncludeLine != null)
+ logger.LogDebug("Tail .dme include line: {0}", dmeModifications.TailIncludeLine);
var dmeLines = new List(dme.Split(new[] { Environment.NewLine }, StringSplitOptions.None));
for (var I = 0; I < dmeLines.Count; ++I)
@@ -232,8 +253,14 @@ namespace Tgstation.Server.Host.Components.Compiler
}
///
- public async Task Compile(string projectName, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken)
+ public async Task Compile(string projectName, DreamDaemonSecurity securityLevel, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken)
{
+ if (repository == null)
+ throw new ArgumentNullException(nameof(repository));
+
+ if (securityLevel == DreamDaemonSecurity.Ultrasafe)
+ throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, "Cannot compile with ultrasafe security!");
+
logger.LogTrace("Begin Compile");
var job = new Models.CompileJob
@@ -242,11 +269,14 @@ namespace Tgstation.Server.Host.Components.Compiler
DmeName = projectName
};
+ logger.LogTrace("Compile output GUID: {0}", job.DirectoryName);
+
lock (this)
{
if(Status != CompilerStatus.Idle)
{
job.Output = "There is already a compile in progress!";
+ logger.LogInformation(job.Output);
return job;
}
@@ -260,6 +290,7 @@ namespace Tgstation.Server.Host.Components.Compiler
async Task CleanupFailedCompile()
{
+ logger.LogTrace("Cleaning compile directory...");
Status = CompilerStatus.Cleanup;
try
{
@@ -273,6 +304,7 @@ namespace Tgstation.Server.Host.Components.Compiler
try
{
+ logger.LogTrace("Copying repository to game directory...");
//copy the repository
var fullDirA = ioManager.ResolvePath(dirA);
var repoOrigin = repository.Origin;
@@ -287,16 +319,20 @@ namespace Tgstation.Server.Host.Components.Compiler
if (job.DmeName == null)
{
+ logger.LogTrace("Searching for available .dmes...");
var path = (await ioManager.GetFilesWithExtension(dirA, DmeExtension, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
if (path == default)
{
job.Output = "Unable to find any .dme!";
+ logger.LogWarning(job.Output);
return job;
}
var dmeWithExtension = ioManager.GetFileName(path);
job.DmeName = dmeWithExtension.Substring(0, dmeWithExtension.Length - DmeExtension.Length - 1);
}
+ logger.LogDebug("Selected {0}.dme for compilation!", job.DmeName);
+
await ModifyDme(job, cancellationToken).ConfigureAwait(false);
Status = CompilerStatus.Compiling;
@@ -311,7 +347,7 @@ namespace Tgstation.Server.Host.Components.Compiler
Status = CompilerStatus.Verifying;
- ddVerified = job.ExitCode == 0 && await VerifyApi(apiValidateTimeout, job, byondLock, cancellationToken).ConfigureAwait(false);
+ ddVerified = job.ExitCode == 0 && await VerifyApi(apiValidateTimeout, securityLevel, job, byondLock, cancellationToken).ConfigureAwait(false);
}
if (!ddVerified)
@@ -324,11 +360,17 @@ namespace Tgstation.Server.Host.Components.Compiler
{
job.DMApiValidated = true;
+ logger.LogTrace("Running post compile event...");
+ Status = CompilerStatus.PostCompile;
+ await eventConsumer.HandleEvent(EventType.CompileComplete, new List { ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName)) }, cancellationToken).ConfigureAwait(false);
+
+ logger.LogTrace("Duplicating compiled game...");
Status = CompilerStatus.Duplicating;
//duplicate the dmb et al
await ioManager.CopyDirectory(dirA, dirB, null, cancellationToken).ConfigureAwait(false);
+ logger.LogTrace("Applying static game file symlinks...");
Status = CompilerStatus.Symlinking;
//symlink in the static data
@@ -336,8 +378,8 @@ namespace Tgstation.Server.Host.Components.Compiler
var symBTask = configuration.SymlinkStaticFilesTo(ioManager.ResolvePath(dirB), cancellationToken);
await Task.WhenAll(symATask, symBTask).ConfigureAwait(false);
- Status = CompilerStatus.PostCompile;
- await eventConsumer.HandleEvent(EventType.CompileComplete, null, cancellationToken).ConfigureAwait(false);
+
+ logger.LogDebug("Compile complete!");
}
return job;
}
diff --git a/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs
index da6878c667..d1e31dad33 100644
--- a/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs
+++ b/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs
@@ -1,7 +1,7 @@
using System.Threading;
using System.Threading.Tasks;
+using Tgstation.Server.Api.Models;
using Tgstation.Server.Host.Components.Repository;
-using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Components.Compiler
{
@@ -19,10 +19,11 @@ namespace Tgstation.Server.Host.Components.Compiler
/// Starts a compile
///
/// The optional name of the .dme to compile without the extension if not pre
+ /// The level allowed for API validation
/// The time in seconds to wait while validating the API
/// The to copy from
/// The for the operation
/// A resulting in the partially populated for the operation. In particular, note the field will only have it's field populated
- Task Compile(string projectName, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken);
+ Task Compile(string projectName, DreamDaemonSecurity securityLevel, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Components/EventType.cs b/src/Tgstation.Server.Host/Components/EventType.cs
index 89e5076fd6..57de1c2bac 100644
--- a/src/Tgstation.Server.Host/Components/EventType.cs
+++ b/src/Tgstation.Server.Host/Components/EventType.cs
@@ -51,7 +51,7 @@
///
CompileFailure = 10,
///
- /// No parameters
+ /// Parameters: Game directory path
///
CompileComplete = 11,
diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs
index a47476351f..f5baaa0b6d 100644
--- a/src/Tgstation.Server.Host/Components/Instance.cs
+++ b/src/Tgstation.Server.Host/Components/Instance.cs
@@ -122,15 +122,19 @@ namespace Tgstation.Server.Host.Components
RepositorySettings repositorySettings = null;
string projectName = null;
- uint timeout = 0;
+ DreamDaemonSettings ddSettings = null;
var dbTask = databaseContextFactory.UseContext(async (db) =>
{
var instanceQuery = db.Instances.Where(x => x.Id == metadata.Id);
- var timeoutTask = instanceQuery.Select(x => x.DreamDaemonSettings.StartupTimeout).FirstAsync(cancellationToken);
+ var ddSettingsTask = instanceQuery.Select(x => x.DreamDaemonSettings).Select(x => new DreamDaemonSettings
+ {
+ StartupTimeout = x.StartupTimeout,
+ SecurityLevel = x.SecurityLevel
+ }).FirstAsync(cancellationToken);
var projectNameTask = instanceQuery.Select(x => x.DreamMakerSettings.ProjectName).FirstOrDefaultAsync(cancellationToken);
repositorySettings = await instanceQuery.Select(x => x.RepositorySettings).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
projectName = await projectNameTask.ConfigureAwait(false);
- timeout = (await timeoutTask.ConfigureAwait(false)).Value;
+ ddSettings = await ddSettingsTask.ConfigureAwait(false);
});
using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
{
@@ -170,7 +174,7 @@ namespace Tgstation.Server.Host.Components
if (repositorySettings.AutoUpdatesSynchronize.Value && startSha != repo.Head)
await repo.Sychronize(repositorySettings.AccessUser, repositorySettings.AccessToken, shouldSyncTracked, cancellationToken).ConfigureAwait(false);
- var job = await DreamMaker.Compile(projectName, timeout, repo, cancellationToken).ConfigureAwait(false);
+ var job = await DreamMaker.Compile(projectName, ddSettings.SecurityLevel.Value, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException) { }
diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs
index 5e9cf98168..91b434a737 100644
--- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs
+++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs
@@ -169,6 +169,9 @@ namespace Tgstation.Server.Host.Controllers
|| !CheckModified(x => x.SoftShutdown, DreamDaemonRights.SoftShutdown)
|| !CheckModified(x => x.StartupTimeout, DreamDaemonRights.SetStartupTimeout))
return Forbid();
+
+ if (current.SecurityLevel == DreamDaemonSecurity.Ultrasafe)
+ return BadRequest(new ErrorMessage { Message = "TGS does not support the ultrasafe DreamDaemon configuration!" });
var wd = instanceManager.GetInstance(Instance).Watchdog;
diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
index 8b666c2a8b..4c7502d025 100644
--- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
+++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs
@@ -101,9 +101,12 @@ namespace Tgstation.Server.Host.Controllers
var instanceManager = serviceProvider.GetRequiredService();
var databaseContext = serviceProvider.GetRequiredService();
- var timeoutTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == instanceModel.Id).Select(x => x.StartupTimeout).FirstOrDefaultAsync(cancellationToken);
+ var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == instanceModel.Id).Select(x => new DreamDaemonSettings{
+ StartupTimeout = x.StartupTimeout,
+ SecurityLevel = x.SecurityLevel
+ }).FirstOrDefaultAsync(cancellationToken);
var projectName = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == instanceModel.Id).Select(x => x.ProjectName).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
- var timeout = await timeoutTask.ConfigureAwait(false);
+ var ddSettings = await ddSettingsTask.ConfigureAwait(false);
var instance = instanceManager.GetInstance(instanceModel);
@@ -119,7 +122,7 @@ namespace Tgstation.Server.Host.Controllers
}
repoSha = repo.Head;
revInfoTask = databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha).Select(x => new RevisionInformation { Id = x.Id }).FirstOrDefaultAsync();
- compileJob = await instance.DreamMaker.Compile(projectName, timeout.Value, repo, cancellationToken).ConfigureAwait(false);
+ compileJob = await instance.DreamMaker.Compile(projectName, ddSettings.SecurityLevel.Value, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
}
if (compileJob.DMApiValidated != true)
diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs
index b1430a4fd1..a25768c465 100644
--- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs
+++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs
@@ -117,7 +117,7 @@ namespace Tgstation.Server.Host.Controllers
AutoStart = false,
PrimaryPort = 1337,
SecondaryPort = 1338,
- SecurityLevel = DreamDaemonSecurity.Ultrasafe,
+ SecurityLevel = DreamDaemonSecurity.Safe,
SoftRestart = false,
SoftShutdown = false,
StartupTimeout = 20