mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-22 12:37:24 +01:00
WIP OpenDream Support
This commit is contained in:
+8
-3
@@ -1,6 +1,6 @@
|
||||
// tgstation-server DMAPI
|
||||
|
||||
#define TGS_DMAPI_VERSION "6.5.3"
|
||||
#define TGS_DMAPI_VERSION "6.6.0"
|
||||
|
||||
// All functions and datums outside this document are subject to change with any version and should not be relied on.
|
||||
|
||||
@@ -73,11 +73,11 @@
|
||||
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
|
||||
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path.
|
||||
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
|
||||
/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND.
|
||||
/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND, engine type of the installing BYOND.
|
||||
#define TGS_EVENT_BYOND_INSTALL_START 5
|
||||
/// When a BYOND install operation fails. Parameters: Error message
|
||||
#define TGS_EVENT_BYOND_INSTALL_FAIL 6
|
||||
/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND.
|
||||
/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND, engine type of the current BYOND, engine type of the new BYOND.
|
||||
#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
|
||||
/// When the compiler starts running. Parameters: Game directory path, origin commit SHA.
|
||||
#define TGS_EVENT_COMPILE_START 8
|
||||
@@ -129,6 +129,11 @@
|
||||
/// DreamDaemon Ultrasafe security level.
|
||||
#define TGS_SECURITY_ULTRASAFE 2
|
||||
|
||||
/// The Build Your Own Net Dream engine.
|
||||
#define TGS_ENGINE_TYPE_BYOND 0
|
||||
/// The OpenDream engine.
|
||||
#define TGS_ENGINE_TYPE_OPENDREAM 1
|
||||
|
||||
//REQUIRED HOOKS
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Tgstation.Server.Api.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of engine the codebase is using.
|
||||
/// </summary>
|
||||
public enum EngineType
|
||||
{
|
||||
/// <summary>
|
||||
/// Build your own net dream,
|
||||
/// </summary>
|
||||
Byond,
|
||||
|
||||
/// <summary>
|
||||
/// The OpenDream BYOND reimplementation.
|
||||
/// </summary>
|
||||
OpenDream,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Tgstation.Server.Api.Models.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Information about a Byond installation.
|
||||
/// </summary>
|
||||
public class ByondVersion : IEquatable<ByondVersion>
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="EngineType"/>.
|
||||
/// </summary>
|
||||
[RequestOptions(FieldPresence.Required)]
|
||||
public EngineType? Engine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="System.Version"/> of the engine.
|
||||
/// </summary>
|
||||
[ResponseOptions]
|
||||
public Version? Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The git committish of the <see cref="Version"/>. On response, this will always be a commit SHA.
|
||||
/// </summary>
|
||||
[ResponseOptions]
|
||||
[StringLength(Limits.MaximumCommitShaLength)]
|
||||
public string? SourceCommittish { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parses a stringified <see cref="ByondVersion"/>.
|
||||
/// </summary>
|
||||
/// <param name="input">The input <see cref="string"/>.</param>
|
||||
/// <param name="byondVersion">The output <see cref="ByondVersion"/>.</param>
|
||||
/// <returns><see langword="true"/> if parsing was successful, <see langword="false"/> otherwise.</returns>
|
||||
public static bool TryParse(string input, out ByondVersion? byondVersion)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
var splits = input.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
byondVersion = null;
|
||||
|
||||
if (splits.Length > 2)
|
||||
return false;
|
||||
|
||||
EngineType engine;
|
||||
if (splits.Length > 1)
|
||||
{
|
||||
if (!Enum.TryParse(splits[0], out engine))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
engine = EngineType.Byond;
|
||||
|
||||
if (!Version.TryParse(splits.Last(), out var version))
|
||||
return false;
|
||||
|
||||
byondVersion = new ByondVersion
|
||||
{
|
||||
Engine = engine,
|
||||
Version = version,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ByondVersion"/> class.
|
||||
/// </summary>
|
||||
public ByondVersion()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ByondVersion"/> class.
|
||||
/// </summary>
|
||||
/// <param name="other">The <see cref="ByondVersion"/> to copy.</param>
|
||||
public ByondVersion(ByondVersion other)
|
||||
{
|
||||
if (other == null)
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
|
||||
Version = other.Version;
|
||||
Engine = other.Engine;
|
||||
SourceCommittish = other.SourceCommittish;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(ByondVersion other)
|
||||
{
|
||||
// https://github.com/dotnet/roslyn-analyzers/issues/2875
|
||||
#pragma warning disable CA1062 // Validate arguments of public methods
|
||||
return other!.Version == Version
|
||||
&& other.Engine == Engine;
|
||||
#pragma warning restore CA1062 // Validate arguments of public methods
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ByondVersion other && Equals(other);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"{(Engine != EngineType.Byond ? $"{Engine}-" : String.Empty)}{Version}"; // BYOND isn't display for backwards compatibility. SourceCommittish is not included
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => ToString().GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,13 @@
|
||||
using System;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Api.Models.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// A request to delete a specific <see cref="Version"/>.
|
||||
/// </summary>
|
||||
public class ByondVersionDeleteRequest
|
||||
public class ByondVersionDeleteRequest : ByondVersion
|
||||
{
|
||||
/// <summary>
|
||||
/// The BYOND version to install.
|
||||
/// </summary>
|
||||
[RequestOptions(FieldPresence.Required)]
|
||||
public Version? Version { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
namespace Tgstation.Server.Api.Models.Request
|
||||
using System;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Api.Models.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// A request to install a BYOND <see cref="ByondVersionDeleteRequest.Version"/>.
|
||||
/// A request to install a <see cref="ByondVersion"/>.
|
||||
/// </summary>
|
||||
public sealed class ByondVersionRequest : ByondVersionDeleteRequest
|
||||
public sealed class ByondVersionRequest : ByondVersion
|
||||
{
|
||||
/// <summary>
|
||||
/// If a custom BYOND version is to be uploaded.
|
||||
/// </summary>
|
||||
public bool? UploadCustomZip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The remote repository for non-<see cref="EngineType.Byond"/> <see cref="EngineType"/>s. By default, this is the original git repository of the target <see cref="EngineType"/>.
|
||||
/// </summary>
|
||||
public Uri? SourceRepository { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
using System;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Api.Models.Response
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an installed BYOND <see cref="Version"/>.
|
||||
/// Represents an installed <see cref="ByondVersion"/>.
|
||||
/// </summary>
|
||||
public sealed class ByondResponse
|
||||
public sealed class ByondResponse : ByondVersion
|
||||
{
|
||||
/// <summary>
|
||||
/// The installed BYOND <see cref="System.Version"/>. BYOND itself only considers the <see cref="Version.Major"/> and <see cref="Version.Minor"/> numbers. TGS uses the <see cref="Version.Build"/> number to represent installed custom versions.
|
||||
/// Initializes a new instance of the <see cref="ByondResponse"/> class.
|
||||
/// </summary>
|
||||
[ResponseOptions]
|
||||
public Version? Version { get; set; }
|
||||
/// <param name="byondVersion">The <see cref="ByondVersion"/> to copy.</param>
|
||||
public ByondResponse(ByondVersion byondVersion)
|
||||
: base(byondVersion)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Api.Models.Response
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class CompileJobResponse : Internal.CompileJob
|
||||
public sealed class CompileJobResponse : CompileJob
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Job"/> relating to this job.
|
||||
@@ -16,9 +18,14 @@ namespace Tgstation.Server.Api.Models.Response
|
||||
public RevisionInformation? RevisionInformation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ByondResponse.Version"/> the <see cref="CompileJobResponse"/> was made with.
|
||||
/// The <see cref="ByondVersion.Version"/> the <see cref="CompileJobResponse"/> was made with.
|
||||
/// </summary>
|
||||
public Version? ByondVersion { get; set; }
|
||||
public ByondVersion? ByondVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ByondVersion.Engine"/> the <see cref="CompileJobResponse"/> was made with.
|
||||
/// </summary>
|
||||
public EngineType? Engine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The origin <see cref="Uri"/> of the repository the compile job was built from.
|
||||
|
||||
@@ -14,33 +14,43 @@ namespace Tgstation.Server.Api.Rights
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// User may view the active installed BYOND version.
|
||||
/// User may view the active installed engine versions.
|
||||
/// </summary>
|
||||
ReadActive = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// User may list all installed BYOND versions.
|
||||
/// User may list all installed engine versions.
|
||||
/// </summary>
|
||||
ListInstalled = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// User may install official BYOND versions or change the active BYOND version.
|
||||
/// User may install official <see cref="Models.EngineType.Byond"/> versions or change the active <see cref="Models.EngineType.Byond"/> version.
|
||||
/// </summary>
|
||||
InstallOfficialOrChangeActiveVersion = 1 << 2,
|
||||
InstallOfficialOrChangeActiveByondVersion = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// User may cancel BYOND installation job.
|
||||
/// User may cancel an engine installation job.
|
||||
/// </summary>
|
||||
CancelInstall = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// User may upload and activate custom BYOND builds.
|
||||
/// User may upload and activate custom <see cref="Models.EngineType.Byond"/> builds.
|
||||
/// </summary>
|
||||
InstallCustomVersion = 1 << 4,
|
||||
InstallCustomByondVersion = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// User may delete non-active BYOND builds.
|
||||
/// User may delete non-active engine builds.
|
||||
/// </summary>
|
||||
DeleteInstall = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// User may install official <see cref="Models.EngineType.OpenDream"/> versions or change the active <see cref="Models.EngineType.OpenDream"/> version.
|
||||
/// </summary>
|
||||
InstallOfficialOrChangeActiveOpenDreamVersion = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// User may activate custom <see cref="Models.EngineType.OpenDream"/> builds via zip upload or custom git committish.
|
||||
/// </summary>
|
||||
InstallCustomOpenDreamVersion = 1 << 7,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
|
||||
using Tgstation.Server.Host.Utils;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
/// <inheritdoc cref="IByondExecutableLock" />
|
||||
sealed class ByondExecutableLock : ReferenceCounter<ByondInstallation>, IByondExecutableLock
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Version Version => Instance.Version;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DreamDaemonPath => Instance.DreamDaemonPath;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DreamMakerPath => Instance.DreamMakerPath;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SupportsCli => Instance.SupportsCli;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SupportsMapThreads => Instance.SupportsMapThreads;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DoNotDeleteThisSession() => DangerousDropReference();
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class ByondInstallation : IByondInstallation
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="IEngineInstallation"/> for <see cref="EngineType.Byond"/>.
|
||||
/// </summary>
|
||||
sealed class ByondInstallation : IEngineInstallation
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Version Version { get; }
|
||||
public ByondVersion Version { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DreamDaemonPath { get; }
|
||||
public string ServerExePath { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DreamMakerPath { get; }
|
||||
public string CompilerExePath { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SupportsCli { get; }
|
||||
public bool PromptsForNetworkAccess { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SupportsMapThreads { get; }
|
||||
public bool HasStandardOutput { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task InstallationTask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Task"/> that completes when the BYOND version finished installing.
|
||||
/// If map threads are supported by the <see cref="Version"/>.
|
||||
/// </summary>
|
||||
public Task InstallationTask { get; }
|
||||
readonly bool supportsMapThreads;
|
||||
|
||||
/// <summary>
|
||||
/// Change a given <paramref name="securityLevel"/> into the appropriate DreamDaemon command line word.
|
||||
/// </summary>
|
||||
/// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level to change.</param>
|
||||
/// <returns>A <see cref="string"/> representation of the command line parameter.</returns>
|
||||
static string SecurityWord(DreamDaemonSecurity securityLevel)
|
||||
{
|
||||
return securityLevel switch
|
||||
{
|
||||
DreamDaemonSecurity.Safe => "safe",
|
||||
DreamDaemonSecurity.Trusted => "trusted",
|
||||
DreamDaemonSecurity.Ultrasafe => "ultrasafe",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon security level: {0}", securityLevel)),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change a given <paramref name="visibility"/> into the appropriate DreamDaemon command line word.
|
||||
/// </summary>
|
||||
/// <param name="visibility">The <see cref="DreamDaemonVisibility"/> level to change.</param>
|
||||
/// <returns>A <see cref="string"/> representation of the command line parameter.</returns>
|
||||
static string VisibilityWord(DreamDaemonVisibility visibility)
|
||||
{
|
||||
return visibility switch
|
||||
{
|
||||
DreamDaemonVisibility.Public => "public",
|
||||
DreamDaemonVisibility.Private => "private",
|
||||
DreamDaemonVisibility.Invisible => "invisible",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(visibility), visibility, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon visibility level: {0}", visibility)),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ByondInstallation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="installationTask">The value of <see cref="InstallationTask"/>.</param>
|
||||
/// <param name="version">The value of <see cref="Version"/>.</param>
|
||||
/// <param name="dreamDaemonPath">The value of <see cref="DreamDaemonPath"/>.</param>
|
||||
/// <param name="dreamMakerPath">The value of <see cref="DreamMakerPath"/>.</param>
|
||||
/// <param name="supportsCli">The value of <see cref="SupportsCli"/>.</param>
|
||||
/// <param name="supportsMapThreads">The value of <see cref="SupportsMapThreads"/>.</param>
|
||||
/// <param name="dreamDaemonPath">The value of <see cref="ServerExePath"/>.</param>
|
||||
/// <param name="dreamMakerPath">The value of <see cref="CompilerExePath"/>.</param>
|
||||
/// <param name="supportsCli">If a CLI application is being used.</param>
|
||||
/// <param name="supportsMapThreads">The value of <see cref="supportsMapThreads"/>.</param>
|
||||
public ByondInstallation(
|
||||
Task installationTask,
|
||||
Version version,
|
||||
ByondVersion version,
|
||||
string dreamDaemonPath,
|
||||
string dreamMakerPath,
|
||||
bool supportsCli,
|
||||
bool supportsMapThreads)
|
||||
{
|
||||
InstallationTask = installationTask ?? throw new ArgumentNullException(nameof(installationTask));
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
if (version.Engine.Value != EngineType.Byond)
|
||||
throw new ArgumentException($"Invalid EngineType: {version.Engine.Value}", nameof(version));
|
||||
|
||||
Version = version ?? throw new ArgumentNullException(nameof(version));
|
||||
DreamDaemonPath = dreamDaemonPath ?? throw new ArgumentNullException(nameof(dreamDaemonPath));
|
||||
DreamMakerPath = dreamMakerPath ?? throw new ArgumentNullException(nameof(dreamMakerPath));
|
||||
SupportsCli = supportsCli;
|
||||
SupportsMapThreads = supportsMapThreads;
|
||||
ServerExePath = dreamDaemonPath ?? throw new ArgumentNullException(nameof(dreamDaemonPath));
|
||||
CompilerExePath = dreamMakerPath ?? throw new ArgumentNullException(nameof(dreamMakerPath));
|
||||
HasStandardOutput = supportsCli;
|
||||
PromptsForNetworkAccess = !supportsCli;
|
||||
this.supportsMapThreads = supportsMapThreads;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FormatServerArguments(
|
||||
IDmbProvider dmbProvider,
|
||||
IReadOnlyDictionary<string, string> parameters,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
string logFilePath)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dmbProvider);
|
||||
ArgumentNullException.ThrowIfNull(parameters);
|
||||
ArgumentNullException.ThrowIfNull(launchParameters);
|
||||
|
||||
var parametersString = String.Join('&', parameters.Select(kvp => $"{HttpUtility.UrlEncode(kvp.Key)}={HttpUtility.UrlEncode(kvp.Value)}"));
|
||||
|
||||
if (!String.IsNullOrEmpty(launchParameters.AdditionalParameters))
|
||||
parametersString = $"{parametersString}&{launchParameters.AdditionalParameters}";
|
||||
|
||||
var arguments = String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0} -port {1} -ports 1-65535 {2}-close -verbose -{3} -{4}{5}{6}{7} -params \"{8}\"",
|
||||
dmbProvider.DmbName,
|
||||
launchParameters.Port.Value,
|
||||
launchParameters.AllowWebClient.Value
|
||||
? "-webclient "
|
||||
: String.Empty,
|
||||
SecurityWord(launchParameters.SecurityLevel.Value),
|
||||
VisibilityWord(launchParameters.Visibility.Value),
|
||||
logFilePath != null
|
||||
? $" -logself -log {logFilePath}"
|
||||
: String.Empty, // DD doesn't output anything if -logself is set???
|
||||
launchParameters.StartProfiler.Value
|
||||
? " -profile"
|
||||
: String.Empty,
|
||||
supportsMapThreads && launchParameters.MapThreads.Value != 0
|
||||
? $" -map-threads {launchParameters.MapThreads.Value}"
|
||||
: String.Empty,
|
||||
parametersString);
|
||||
return arguments;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FormatCompilerArguments(string dmePath)
|
||||
=> $"-clean \"{dmePath ?? throw new ArgumentNullException(nameof(dmePath))}\"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
@@ -24,10 +25,10 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
public static Version MapThreadsVersion => new (515, 1609);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string DreamMakerName { get; }
|
||||
public abstract string CompilerName { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string PathToUserByondFolder { get; }
|
||||
public abstract string PathToUserFolder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL formatter string for downloading a byond version of {0:Major} {1:Minor}.
|
||||
@@ -63,7 +64,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string GetDreamDaemonName(Version version, out bool supportsCli, out bool supportsMapThreads);
|
||||
public abstract string GetDreamDaemonName(ByondVersion version, out bool supportsCli, out bool supportsMapThreads);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task CleanCache(CancellationToken cancellationToken)
|
||||
@@ -73,7 +74,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
Logger.LogDebug("Cleaning BYOND cache...");
|
||||
await IOManager.DeleteDirectory(
|
||||
IOManager.ConcatPath(
|
||||
PathToUserByondFolder,
|
||||
PathToUserFolder,
|
||||
CacheDirectoryName),
|
||||
cancellationToken);
|
||||
}
|
||||
@@ -84,18 +85,18 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract ValueTask InstallByond(Version version, string path, CancellationToken cancellationToken);
|
||||
public abstract ValueTask InstallByond(ByondVersion version, string path, CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract ValueTask UpgradeInstallation(Version version, string path, CancellationToken cancellationToken);
|
||||
public abstract ValueTask UpgradeInstallation(ByondVersion version, string path, CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<MemoryStream> DownloadVersion(Version version, CancellationToken cancellationToken)
|
||||
public async ValueTask<MemoryStream> DownloadVersion(ByondVersion version, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
Logger.LogTrace("Downloading BYOND version {major}.{minor}...", version.Major, version.Minor);
|
||||
var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsUrlTemplate, version.Major, version.Minor);
|
||||
Logger.LogTrace("Downloading BYOND version {major}.{minor}...", version.Version.Major, version.Version.Minor);
|
||||
var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsUrlTemplate, version.Version.Major, version.Version.Minor);
|
||||
|
||||
await using var download = fileDownloader.DownloadFile(new Uri(url), null);
|
||||
await using var buffer = new BufferedFileStreamProvider(
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Common.Extensions;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.IO;
|
||||
@@ -47,10 +48,10 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
const string ActiveVersionFileName = "ActiveVersion.txt";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Version ActiveVersion { get; private set; }
|
||||
public ByondVersion ActiveVersion { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<Version> InstalledVersions
|
||||
public IReadOnlyList<ByondVersion> InstalledVersions
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -87,7 +88,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// <summary>
|
||||
/// Map of byond <see cref="Version"/>s to <see cref="Task"/>s that complete when they are installed.
|
||||
/// </summary>
|
||||
readonly Dictionary<Version, ReferenceCountingContainer<ByondInstallation, ByondExecutableLock>> installedVersions;
|
||||
readonly Dictionary<ByondVersion, ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock>> installedVersions;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SemaphoreSlim"/> for changing or deleting the active BYOND version.
|
||||
@@ -103,11 +104,14 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// Validates a given <paramref name="version"/> parameter.
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="Version"/> to validate.</param>
|
||||
static void CheckVersionParameter(Version version)
|
||||
static void CheckVersionParameter(ByondVersion version)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
if (version.Build == 0)
|
||||
if (!version.Engine.HasValue)
|
||||
throw new InvalidOperationException("version.Engine cannot be null!");
|
||||
|
||||
if (version.Version.Build == 0)
|
||||
throw new InvalidOperationException("version.Build cannot be 0!");
|
||||
}
|
||||
|
||||
@@ -125,7 +129,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
installedVersions = new Dictionary<Version, ReferenceCountingContainer<ByondInstallation, ByondExecutableLock>>();
|
||||
installedVersions = new Dictionary<ByondVersion, ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock>>();
|
||||
changeDeleteSemaphore = new SemaphoreSlim(1);
|
||||
activeVersionChanged = new TaskCompletionSource();
|
||||
}
|
||||
@@ -136,7 +140,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ChangeVersion(
|
||||
JobProgressReporter progressReporter,
|
||||
Version version,
|
||||
ByondVersion version,
|
||||
Stream customVersionStream,
|
||||
bool allowInstallation,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -154,7 +158,10 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
cancellationToken);
|
||||
|
||||
// We reparse the version because it could be changed after a custom install.
|
||||
version = installLock.Version;
|
||||
version = new ByondVersion(version)
|
||||
{
|
||||
Version = installLock.Version.Version,
|
||||
};
|
||||
|
||||
var stringVersion = version.ToString();
|
||||
await ioManager.WriteAllBytes(ActiveVersionFileName, Encoding.UTF8.GetBytes(stringVersion), cancellationToken);
|
||||
@@ -177,7 +184,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<IByondExecutableLock> UseExecutables(Version requiredVersion, string trustDmbFullPath, CancellationToken cancellationToken)
|
||||
public async ValueTask<IEngineExecutableLock> UseExecutables(ByondVersion requiredVersion, string trustDmbFullPath, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace(
|
||||
"Acquiring lock on BYOND version {version}...",
|
||||
@@ -205,7 +212,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask DeleteVersion(JobProgressReporter progressReporter, Version version, CancellationToken cancellationToken)
|
||||
public async ValueTask DeleteVersion(JobProgressReporter progressReporter, ByondVersion version, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(progressReporter);
|
||||
|
||||
@@ -213,15 +220,18 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
|
||||
logger.LogTrace("DeleteVersion {version}", version);
|
||||
|
||||
if (version == ActiveVersion)
|
||||
if (version.Equals(ActiveVersion))
|
||||
throw new JobException(ErrorCode.ByondCannotDeleteActiveVersion);
|
||||
|
||||
ReferenceCountingContainer<ByondInstallation, ByondExecutableLock> container;
|
||||
ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock> container;
|
||||
lock (installedVersions)
|
||||
if (!installedVersions.TryGetValue(version, out container))
|
||||
return; // already "deleted"
|
||||
{
|
||||
logger.LogTrace("Version {version} already deleted.", version);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogInformation("Deleting BYOND version {version}...", version);
|
||||
logger.LogInformation("Deleting version {version}...", version);
|
||||
progressReporter.StageName = "Waiting for version to not be in use...";
|
||||
while (true)
|
||||
{
|
||||
@@ -238,7 +248,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
.WaitAsync(cancellationToken);
|
||||
|
||||
if (containerTask.IsCompleted)
|
||||
logger.LogTrace("All BYOND locks for {version} are gone", version);
|
||||
logger.LogTrace("All locks for version {version} are gone", version);
|
||||
|
||||
using (await SemaphoreSlimContext.Lock(changeDeleteSemaphore, cancellationToken))
|
||||
{
|
||||
@@ -252,7 +262,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
proceed = container.OnZeroReferences.IsCompleted;
|
||||
if (proceed)
|
||||
if (!installedVersions.TryGetValue(version, out var newerContainer))
|
||||
logger.LogWarning("Unable to remove BYOND installation {version} from list! Is there a duplicate job running?", version);
|
||||
logger.LogWarning("Unable to remove engine installation {version} from list! Is there a duplicate job running?", version);
|
||||
else
|
||||
{
|
||||
if (container != newerContainer)
|
||||
@@ -291,6 +301,13 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask EnsureEngineSource(Uri source, EngineType engine, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Yield();
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -302,7 +319,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
|
||||
var activeVersionBytesTask = GetActiveVersion();
|
||||
|
||||
var byondDir = byondInstaller.PathToUserByondFolder;
|
||||
var byondDir = byondInstaller.PathToUserFolder;
|
||||
if (byondDir != null)
|
||||
using (await SemaphoreSlimContext.Lock(UserFilesSemaphore, cancellationToken))
|
||||
{
|
||||
@@ -328,7 +345,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
await ioManager.CreateDirectory(DefaultIOManager.CurrentDirectory, cancellationToken);
|
||||
var directories = await ioManager.GetDirectories(DefaultIOManager.CurrentDirectory, cancellationToken);
|
||||
|
||||
var installedVersionPaths = new Dictionary<string, Version>();
|
||||
var installedVersionPaths = new Dictionary<string, ByondVersion>();
|
||||
|
||||
async ValueTask ReadVersion(string path)
|
||||
{
|
||||
@@ -342,7 +359,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
|
||||
var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken);
|
||||
var text = Encoding.UTF8.GetString(bytes);
|
||||
if (!Version.TryParse(text, out var version))
|
||||
if (!ByondVersion.TryParse(text, out var version))
|
||||
{
|
||||
logger.LogWarning("Cleaning path with unparsable version file: {versionPath}", ioManager.ResolvePath(path));
|
||||
await ioManager.DeleteDirectory(path, cancellationToken); // cleanup
|
||||
@@ -383,10 +400,10 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes);
|
||||
|
||||
Version activeVersion;
|
||||
ByondVersion activeVersion;
|
||||
bool hasRequestedActiveVersion;
|
||||
lock (installedVersions)
|
||||
hasRequestedActiveVersion = Version.TryParse(activeVersionString, out activeVersion)
|
||||
hasRequestedActiveVersion = ByondVersion.TryParse(activeVersionString, out activeVersion)
|
||||
&& installedVersions.ContainsKey(activeVersion);
|
||||
|
||||
if (hasRequestedActiveVersion)
|
||||
@@ -403,46 +420,47 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures a BYOND <paramref name="version"/> is installed if it isn't already.
|
||||
/// Ensures a BYOND <paramref name="byondVersion"/> is installed if it isn't already.
|
||||
/// </summary>
|
||||
/// <param name="progressReporter">The optional <see cref="JobProgressReporter"/> for the operation.</param>
|
||||
/// <param name="version">The BYOND <see cref="Version"/> to install.</param>
|
||||
/// <param name="byondVersion">The <see cref="ByondVersion"/> to install.</param>
|
||||
/// <param name="customVersionStream">Custom zip file <see cref="Stream"/> to use. Will cause a <see cref="Version.Build"/> number to be added.</param>
|
||||
/// <param name="neededForLock">If this BYOND version is required as part of a locking operation.</param>
|
||||
/// <param name="allowInstallation">If an installation should be performed if the <paramref name="version"/> is not installed. If <see langword="false"/> and an installation is required an <see cref="InvalidOperationException"/> will be thrown.</param>
|
||||
/// <param name="allowInstallation">If an installation should be performed if the <paramref name="byondVersion"/> is not installed. If <see langword="false"/> and an installation is required an <see cref="InvalidOperationException"/> will be thrown.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <see cref="ByondExecutableLock"/>.</returns>
|
||||
async ValueTask<ByondExecutableLock> AssertAndLockVersion(
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <see cref="EngineExecutableLock"/>.</returns>
|
||||
async ValueTask<EngineExecutableLock> AssertAndLockVersion(
|
||||
JobProgressReporter progressReporter,
|
||||
Version version,
|
||||
ByondVersion byondVersion,
|
||||
Stream customVersionStream,
|
||||
bool neededForLock,
|
||||
bool allowInstallation,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var ourTcs = new TaskCompletionSource();
|
||||
ByondInstallation installation;
|
||||
ByondExecutableLock installLock;
|
||||
IEngineInstallation installation;
|
||||
EngineExecutableLock installLock;
|
||||
bool installedOrInstalling;
|
||||
var byondEngine = byondVersion.Engine.Value == EngineType.Byond;
|
||||
lock (installedVersions)
|
||||
{
|
||||
if (customVersionStream != null)
|
||||
if (customVersionStream != null && byondEngine)
|
||||
{
|
||||
var customInstallationNumber = 1;
|
||||
do
|
||||
{
|
||||
version = new Version(version.Major, version.Minor, customInstallationNumber++);
|
||||
byondVersion.Version = new Version(byondVersion.Version.Major, byondVersion.Version.Minor, customInstallationNumber++);
|
||||
}
|
||||
while (installedVersions.ContainsKey(version));
|
||||
while (installedVersions.ContainsKey(byondVersion));
|
||||
}
|
||||
|
||||
installedOrInstalling = installedVersions.TryGetValue(version, out var installationContainer);
|
||||
installedOrInstalling = installedVersions.TryGetValue(byondVersion, out var installationContainer);
|
||||
if (!installedOrInstalling)
|
||||
{
|
||||
if (!allowInstallation)
|
||||
throw new InvalidOperationException($"BYOND version {version} not installed!");
|
||||
throw new InvalidOperationException($"BYOND version {byondVersion} not installed!");
|
||||
|
||||
installationContainer = AddInstallationContainer(version, ourTcs.Task);
|
||||
installationContainer = AddInstallationContainer(byondVersion, ourTcs.Task);
|
||||
}
|
||||
|
||||
installation = installationContainer.Instance;
|
||||
@@ -457,7 +475,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
progressReporter.StageName = "Waiting for existing installation job...";
|
||||
|
||||
if (neededForLock && !installation.InstallationTask.IsCompleted)
|
||||
logger.LogWarning("The required BYOND version ({version}) is not readily available! We will have to wait for it to install.", version);
|
||||
logger.LogWarning("The required BYOND version ({version}) is not readily available! We will have to wait for it to install.", byondVersion);
|
||||
|
||||
await installation.InstallationTask.WaitAsync(cancellationToken);
|
||||
return installLock;
|
||||
@@ -467,24 +485,24 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
try
|
||||
{
|
||||
if (customVersionStream != null)
|
||||
logger.LogInformation("Installing custom BYOND version as {version}...", version);
|
||||
logger.LogInformation("Installing custom BYOND version as {version}...", byondVersion);
|
||||
else if (neededForLock)
|
||||
{
|
||||
if (version.Build > 0)
|
||||
if (byondEngine && byondVersion.Version.Build > 0)
|
||||
throw new JobException(ErrorCode.ByondNonExistentCustomVersion);
|
||||
|
||||
logger.LogWarning("The required BYOND version ({version}) is not readily available! We will have to install it.", version);
|
||||
logger.LogWarning("The required BYOND version ({version}) is not readily available! We will have to install it.", byondVersion);
|
||||
}
|
||||
else
|
||||
logger.LogDebug("Requested BYOND version {version} not currently installed. Doing so now...", version);
|
||||
logger.LogDebug("Requested BYOND version {version} not currently installed. Doing so now...", byondVersion);
|
||||
|
||||
if (progressReporter != null)
|
||||
progressReporter.StageName = "Running event";
|
||||
|
||||
var versionString = version.ToString();
|
||||
var versionString = byondVersion.ToString();
|
||||
await eventConsumer.HandleEvent(EventType.ByondInstallStart, new List<string> { versionString }, false, cancellationToken);
|
||||
|
||||
await InstallVersionFiles(progressReporter, version, customVersionStream, cancellationToken);
|
||||
await InstallVersionFiles(progressReporter, byondVersion, customVersionStream, cancellationToken);
|
||||
|
||||
ourTcs.SetResult();
|
||||
}
|
||||
@@ -494,7 +512,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
await eventConsumer.HandleEvent(EventType.ByondInstallFail, new List<string> { ex.Message }, false, cancellationToken);
|
||||
|
||||
lock (installedVersions)
|
||||
installedVersions.Remove(version);
|
||||
installedVersions.Remove(byondVersion);
|
||||
|
||||
ourTcs.SetException(ex);
|
||||
throw;
|
||||
@@ -513,11 +531,11 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// Installs the files for a given BYOND <paramref name="version"/>.
|
||||
/// </summary>
|
||||
/// <param name="progressReporter">The optional <see cref="JobProgressReporter"/> for the operation.</param>
|
||||
/// <param name="version">The BYOND <see cref="Version"/> being installed with the <see cref="Version.Build"/> number set if appropriate.</param>
|
||||
/// <param name="version">The <see cref="ByondVersion"/> being installed with the <see cref="Version.Build"/> number set if appropriate.</param>
|
||||
/// <param name="customVersionStream">Custom zip file <see cref="Stream"/> to use. Will cause a <see cref="Version.Build"/> number to be added.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
async ValueTask InstallVersionFiles(JobProgressReporter progressReporter, Version version, Stream customVersionStream, CancellationToken cancellationToken)
|
||||
async ValueTask InstallVersionFiles(JobProgressReporter progressReporter, ByondVersion version, Stream customVersionStream, CancellationToken cancellationToken)
|
||||
{
|
||||
var installFullPath = ioManager.ResolvePath(version.ToString());
|
||||
async ValueTask DirectoryCleanup()
|
||||
@@ -585,32 +603,46 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and add a new <see cref="ByondInstallation"/> to <see cref="installedVersions"/>.
|
||||
/// Create and add a new <see cref="IEngineInstallation"/> to <see cref="installedVersions"/>.
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="Version"/> being added.</param>
|
||||
/// <param name="installationTask">The <see cref="ValueTask"/> representing the installation process.</param>
|
||||
/// <returns>The new <see cref="ReferenceCountingContainer{TWrapped, TReference}"/> containing the new <see cref="ByondInstallation"/>.</returns>
|
||||
ReferenceCountingContainer<ByondInstallation, ByondExecutableLock> AddInstallationContainer(Version version, Task installationTask)
|
||||
/// <returns>The new <see cref="ReferenceCountingContainer{TWrapped, TReference}"/> containing the new <see cref="IEngineInstallation"/>.</returns>
|
||||
ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock> AddInstallationContainer(ByondVersion version, Task installationTask)
|
||||
{
|
||||
var binPathForVersion = ioManager.ConcatPath(version.ToString(), BinPath);
|
||||
var installation = new ByondInstallation(
|
||||
installationTask,
|
||||
version,
|
||||
ioManager.ResolvePath(
|
||||
ioManager.ConcatPath(
|
||||
binPathForVersion,
|
||||
byondInstaller.GetDreamDaemonName(
|
||||
version,
|
||||
out var supportsCli,
|
||||
out var supportsMapThreads))),
|
||||
ioManager.ResolvePath(
|
||||
ioManager.ConcatPath(
|
||||
binPathForVersion,
|
||||
byondInstaller.DreamMakerName)),
|
||||
supportsCli,
|
||||
supportsMapThreads);
|
||||
IEngineInstallation installation;
|
||||
|
||||
var installationContainer = new ReferenceCountingContainer<ByondInstallation, ByondExecutableLock>(installation);
|
||||
switch (version.Engine.Value)
|
||||
{
|
||||
case EngineType.Byond:
|
||||
installation = new ByondInstallation(
|
||||
installationTask,
|
||||
version,
|
||||
ioManager.ResolvePath(
|
||||
ioManager.ConcatPath(
|
||||
binPathForVersion,
|
||||
byondInstaller.GetDreamDaemonName(
|
||||
version,
|
||||
out var supportsCli,
|
||||
out var supportsMapThreads))),
|
||||
ioManager.ResolvePath(
|
||||
ioManager.ConcatPath(
|
||||
binPathForVersion,
|
||||
byondInstaller.CompilerName)),
|
||||
supportsCli,
|
||||
supportsMapThreads);
|
||||
break;
|
||||
case EngineType.OpenDream:
|
||||
installation = new OpenDreamInstallation(
|
||||
installationTask,
|
||||
version);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid EngineType: {version.Engine.Value}");
|
||||
}
|
||||
|
||||
var installationContainer = new ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock>(installation);
|
||||
|
||||
lock (installedVersions)
|
||||
installedVersions.Add(version, installationContainer);
|
||||
@@ -626,7 +658,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
async ValueTask TrustDmbPath(string fullDmbPath, CancellationToken cancellationToken)
|
||||
{
|
||||
var byondDir = byondInstaller.PathToUserByondFolder;
|
||||
var byondDir = byondInstaller.PathToUserFolder;
|
||||
if (String.IsNullOrWhiteSpace(byondDir))
|
||||
{
|
||||
logger.LogTrace("No relevant user BYOND directory to install a \"{fileName}\" in", TrustedDmbFileName);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
/// <inheritdoc cref="IEngineExecutableLock" />
|
||||
sealed class EngineExecutableLock : ReferenceCounter<IEngineInstallation>, IEngineExecutableLock
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ByondVersion Version => Instance.Version;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ServerExePath => Instance.ServerExePath;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string CompilerExePath => Instance.CompilerExePath;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasStandardOutput => Instance.HasStandardOutput;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool PromptsForNetworkAccess => Instance.PromptsForNetworkAccess;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task InstallationTask => Instance.InstallationTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DoNotDeleteThisSession() => DangerousDropReference();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FormatServerArguments(
|
||||
IDmbProvider dmbProvider,
|
||||
IReadOnlyDictionary<string, string> parameters,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
string logFilePath)
|
||||
=> Instance.FormatServerArguments(
|
||||
dmbProvider,
|
||||
parameters,
|
||||
launchParameters,
|
||||
logFilePath);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FormatCompilerArguments(string dmePath) => Instance.FormatCompilerArguments(dmePath);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a BYOND installation.
|
||||
/// </summary>
|
||||
public interface IByondInstallation
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="global::System.Version"/> of the <see cref="IByondInstallation"/>.
|
||||
/// </summary>
|
||||
Version Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full path to the DreamDaemon executable.
|
||||
/// </summary>
|
||||
string DreamDaemonPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full path to the dm/DreamMaker executable.
|
||||
/// </summary>
|
||||
string DreamMakerPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="DreamDaemonPath"/> supports being run as a command-line application.
|
||||
/// </summary>
|
||||
bool SupportsCli { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="DreamDaemonPath"/> supports the -map-threads parameter.
|
||||
/// </summary>
|
||||
bool SupportsMapThreads { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
/// <summary>
|
||||
@@ -11,52 +12,52 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
interface IByondInstaller
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the file name of the DreamMaker executable.
|
||||
/// Get the file name of the compiler executable.
|
||||
/// </summary>
|
||||
string DreamMakerName { get; }
|
||||
string CompilerName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the BYOND folder for the user.
|
||||
/// The path to the folder for the user's data.
|
||||
/// </summary>
|
||||
string PathToUserByondFolder { get; }
|
||||
string PathToUserFolder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the file name of the DreamDaemon executable.
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="Version"/> of BYOND to select the executable name for.</param>
|
||||
/// <param name="version">The <see cref="ByondVersion"/> of BYOND to select the executable name for.</param>
|
||||
/// <param name="supportsCli">Whether or not the returned path supports being run as a command-line application.</param>
|
||||
/// <param name="supportsMapThreads">Whether or not the returned path supports the '-map-threads' parameter.</param>
|
||||
/// <returns>The file name of the DreamDaemon executable.</returns>
|
||||
string GetDreamDaemonName(Version version, out bool supportsCli, out bool supportsMapThreads);
|
||||
string GetDreamDaemonName(ByondVersion version, out bool supportsCli, out bool supportsMapThreads);
|
||||
|
||||
/// <summary>
|
||||
/// Download a given BYOND <paramref name="version"/>.
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="Version"/> of BYOND to download.</param>
|
||||
/// <param name="version">The <see cref="ByondVersion"/> of BYOND to download.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="MemoryStream"/> of the zipfile.</returns>
|
||||
ValueTask<MemoryStream> DownloadVersion(Version version, CancellationToken cancellationToken);
|
||||
ValueTask<MemoryStream> DownloadVersion(ByondVersion version, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Does actions necessary to get an extracted BYOND installation working.
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="Version"/> of BYOND being installed.</param>
|
||||
/// <param name="version">The <see cref="ByondVersion"/> being installed.</param>
|
||||
/// <param name="path">The path to the BYOND installation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
ValueTask InstallByond(Version version, string path, CancellationToken cancellationToken);
|
||||
ValueTask InstallByond(ByondVersion version, string path, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Does actions necessary to get upgrade a BYOND version installed by a previous version of TGS.
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="Version"/> of BYOND being installed.</param>
|
||||
/// <param name="version">The <see cref="ByondVersion"/> being installed.</param>
|
||||
/// <param name="path">The path to the BYOND installation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
ValueTask UpgradeInstallation(Version version, string path, CancellationToken cancellationToken);
|
||||
ValueTask UpgradeInstallation(ByondVersion version, string path, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to cleans the BYOND cache folder for the system.
|
||||
/// Attempts to cleans the engine's cache folder for the system.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
@@ -11,48 +13,62 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// <summary>
|
||||
/// For managing the BYOND installation.
|
||||
/// </summary>
|
||||
/// <remarks>When passing in <see cref="Version"/>s, ensure they are BYOND format versions unless referring to a custom version. This means <see cref="Version.Build"/> should NEVER be 0.</remarks>
|
||||
/// <remarks>When passing in <see cref="ByondVersion.Version"/>s for <see cref="EngineType.Byond"/>, ensure they are BYOND format versions unless referring to a custom version. This means <see cref="Version.Build"/> should NEVER be 0.</remarks>
|
||||
public interface IByondManager : IComponentService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The currently active BYOND version.
|
||||
/// The currently active <see cref="ByondVersion"/>.
|
||||
/// </summary>
|
||||
Version ActiveVersion { get; }
|
||||
ByondVersion ActiveVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The installed BYOND versions.
|
||||
/// The installed <see cref="ByondVersion"/>s.
|
||||
/// </summary>
|
||||
IReadOnlyList<Version> InstalledVersions { get; }
|
||||
IReadOnlyList<ByondVersion> InstalledVersions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Change the active BYOND version.
|
||||
/// Ensure that the given <paramref name="source"/> is registered for the given <paramref name="engine"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The <see cref="Uri"/> source of the <paramref name="engine"/>.</param>
|
||||
/// <param name="engine">The <see cref="EngineType"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
ValueTask EnsureEngineSource(Uri source, EngineType engine, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Change the active <see cref="ByondVersion"/>.
|
||||
/// </summary>
|
||||
/// <param name="progressReporter">The optional <see cref="JobProgressReporter"/> for the operation.</param>
|
||||
/// <param name="version">The new <see cref="Version"/>.</param>
|
||||
/// <param name="version">The new <see cref="ByondVersion"/>.</param>
|
||||
/// <param name="customVersionStream">Optional <see cref="Stream"/> of a custom BYOND version zip file.</param>
|
||||
/// <param name="allowInstallation">If an installation should be performed if the <paramref name="version"/> is not installed. If <see langword="false"/> and an installation is required an <see cref="InvalidOperationException"/> will be thrown.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
ValueTask ChangeVersion(JobProgressReporter progressReporter, Version version, Stream customVersionStream, bool allowInstallation, CancellationToken cancellationToken);
|
||||
ValueTask ChangeVersion(
|
||||
JobProgressReporter progressReporter,
|
||||
ByondVersion version,
|
||||
Stream customVersionStream,
|
||||
bool allowInstallation,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a given BYOND version from the disk.
|
||||
/// Deletes a given <paramref name="version"/> from the disk.
|
||||
/// </summary>
|
||||
/// <param name="progressReporter">The <see cref="JobProgressReporter"/> for the operation.</param>
|
||||
/// <param name="version">The <see cref="Version"/> to delete.</param>
|
||||
/// <param name="version">The <see cref="ByondVersion"/> to delete.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
ValueTask DeleteVersion(JobProgressReporter progressReporter, Version version, CancellationToken cancellationToken);
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
ValueTask DeleteVersion(JobProgressReporter progressReporter, ByondVersion version, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Lock the current installation's location and return a <see cref="IByondExecutableLock"/>.
|
||||
/// Lock the current installation's location and return a <see cref="IEngineExecutableLock"/>.
|
||||
/// </summary>
|
||||
/// <param name="requiredVersion">The BYOND <see cref="Version"/> required.</param>
|
||||
/// <param name="requiredVersion">The <see cref="ByondVersion"/> required.</param>
|
||||
/// <param name="trustDmbFullPath">The optional full path to .dmb to trust while using the executables.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the requested <see cref="IByondExecutableLock"/>.</returns>
|
||||
ValueTask<IByondExecutableLock> UseExecutables(
|
||||
Version requiredVersion,
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the requested <see cref="IEngineExecutableLock"/>.</returns>
|
||||
ValueTask<IEngineExecutableLock> UseExecutables(
|
||||
ByondVersion requiredVersion,
|
||||
string trustDmbFullPath,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// <summary>
|
||||
/// Represents usage of the two primary BYOND server executables.
|
||||
/// </summary>
|
||||
public interface IByondExecutableLock : IByondInstallation, IDisposable
|
||||
public interface IEngineExecutableLock : IEngineInstallation, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Call if, during a detach, this version should not be deleted.
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a BYOND installation.
|
||||
/// </summary>
|
||||
public interface IEngineInstallation
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="ByondVersion"/> of the <see cref="IEngineInstallation"/>.
|
||||
/// </summary>
|
||||
ByondVersion Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full path to the game server executable.
|
||||
/// </summary>
|
||||
string ServerExePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full path to the dm/DreamMaker executable.
|
||||
/// </summary>
|
||||
string CompilerExePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="ServerExePath"/> supports being run as a command-line application and outputs log information to be captured.
|
||||
/// </summary>
|
||||
bool HasStandardOutput { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="ServerExePath"/> may create network prompts.
|
||||
/// </summary>
|
||||
bool PromptsForNetworkAccess { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Task"/> that completes when the BYOND version finished installing.
|
||||
/// </summary>
|
||||
Task InstallationTask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Return the command line arguments for launching with given <paramref name="launchParameters"/>.
|
||||
/// </summary>
|
||||
/// <param name="dmbProvider">The <see cref="IDmbProvider"/>.</param>
|
||||
/// <param name="parameters">The map of parameter <see cref="string"/>s as a <see cref="IReadOnlyDictionary{TKey, TValue}"/>. Should NOT include the <see cref="DreamDaemonLaunchParameters.AdditionalParameters"/> of <paramref name="launchParameters"/>.</param>
|
||||
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/>.</param>
|
||||
/// <param name="logFilePath">The path to the log file, if any.</param>
|
||||
/// <returns>The formatted arguments <see cref="string"/>.</returns>
|
||||
string FormatServerArguments(
|
||||
IDmbProvider dmbProvider,
|
||||
IReadOnlyDictionary<string, string> parameters,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
string logFilePath);
|
||||
|
||||
/// <summary>
|
||||
/// Return the command line arguments for compiling a given <paramref name="dmePath"/> if compilation is necessary.
|
||||
/// </summary>
|
||||
/// <param name="dmePath">The full path to the .dme to compile.</param>
|
||||
/// <returns>An arguments <see cref="string"/> if compilation is required, <see langword="null"/> otherwise.</returns>
|
||||
string FormatCompilerArguments(string dmePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="IEngineInstallation"/> for <see cref="EngineType.OpenDream"/>.
|
||||
/// </summary>
|
||||
sealed class OpenDreamInstallation : IEngineInstallation
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ByondVersion Version { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ServerExePath { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string CompilerExePath { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool PromptsForNetworkAccess => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasStandardOutput => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task InstallationTask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OpenDreamInstallation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="installationTask">The value of <see cref="InstallationTask"/>.</param>
|
||||
/// <param name="version">The value of <see cref="Version"/>.</param>
|
||||
public OpenDreamInstallation(
|
||||
Task installationTask,
|
||||
ByondVersion version)
|
||||
{
|
||||
InstallationTask = installationTask ?? throw new ArgumentNullException(nameof(installationTask));
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
if (version.Engine.Value != EngineType.OpenDream)
|
||||
throw new ArgumentException($"Invalid EngineType: {version.Engine.Value}", nameof(version));
|
||||
|
||||
Version = version ?? throw new ArgumentNullException(nameof(version));
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FormatServerArguments(IDmbProvider dmbProvider, IReadOnlyDictionary<string, string> parameters, DreamDaemonLaunchParameters launchParameters, string logFilePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FormatCompilerArguments(string dmePath)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dmePath);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Common.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
@@ -32,10 +33,10 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
const string ShellScriptExtension = ".sh";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DreamMakerName => DreamMakerExecutableName + ShellScriptExtension;
|
||||
public override string CompilerName => DreamMakerExecutableName + ShellScriptExtension;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string PathToUserByondFolder { get; }
|
||||
public override string PathToUserFolder { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string ByondRevisionsUrlTemplate => "https://www.byond.com/download/build/{0}/{0}.{1}_byond_linux.zip";
|
||||
@@ -61,7 +62,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
{
|
||||
this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
|
||||
|
||||
PathToUserByondFolder = IOManager.ResolvePath(
|
||||
PathToUserFolder = IOManager.ResolvePath(
|
||||
IOManager.ConcatPath(
|
||||
Environment.GetFolderPath(
|
||||
Environment.SpecialFolder.UserProfile),
|
||||
@@ -69,17 +70,17 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetDreamDaemonName(Version version, out bool supportsCli, out bool supportsMapThreads)
|
||||
public override string GetDreamDaemonName(ByondVersion version, out bool supportsCli, out bool supportsMapThreads)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
supportsCli = true;
|
||||
supportsMapThreads = version >= MapThreadsVersion;
|
||||
supportsMapThreads = version.Version >= MapThreadsVersion;
|
||||
return DreamDaemonExecutableName + ShellScriptExtension;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ValueTask InstallByond(Version version, string path, CancellationToken cancellationToken)
|
||||
public override ValueTask InstallByond(ByondVersion version, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
ArgumentNullException.ThrowIfNull(path);
|
||||
@@ -105,7 +106,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
dreamDaemonScript);
|
||||
|
||||
var dmTask = WriteAndMakeExecutable(
|
||||
IOManager.ConcatPath(basePath, DreamMakerName),
|
||||
IOManager.ConcatPath(basePath, CompilerName),
|
||||
dreamMakerScript);
|
||||
|
||||
var task = ValueTaskExtensions.WhenAll(
|
||||
@@ -119,7 +120,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ValueTask UpgradeInstallation(Version version, string path, CancellationToken cancellationToken)
|
||||
public override ValueTask UpgradeInstallation(ByondVersion version, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
ArgumentNullException.ThrowIfNull(path);
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Common.Extensions;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.IO;
|
||||
@@ -53,10 +54,10 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
public static Version DDExeVersion => new (515, 1598);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DreamMakerName => "dm.exe";
|
||||
public override string CompilerName => "dm.exe";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string PathToUserByondFolder { get; }
|
||||
public override string PathToUserFolder { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string ByondRevisionsUrlTemplate => "https://www.byond.com/download/build/{0}/{0}.{1}_byond.zip";
|
||||
@@ -102,9 +103,9 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
|
||||
var documentsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
if (String.IsNullOrWhiteSpace(documentsDirectory))
|
||||
PathToUserByondFolder = null; // happens with the service account
|
||||
PathToUserFolder = null; // happens with the service account
|
||||
else
|
||||
PathToUserByondFolder = IOManager.ResolvePath(IOManager.ConcatPath(documentsDirectory, "BYOND"));
|
||||
PathToUserFolder = IOManager.ResolvePath(IOManager.ConcatPath(documentsDirectory, "BYOND"));
|
||||
|
||||
semaphore = new SemaphoreSlim(1);
|
||||
installedDirectX = false;
|
||||
@@ -114,17 +115,17 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
public void Dispose() => semaphore.Dispose();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetDreamDaemonName(Version version, out bool supportsCli, out bool supportsMapThreads)
|
||||
public override string GetDreamDaemonName(ByondVersion version, out bool supportsCli, out bool supportsMapThreads)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
supportsCli = version >= DDExeVersion;
|
||||
supportsMapThreads = version >= MapThreadsVersion;
|
||||
supportsCli = version.Version >= DDExeVersion;
|
||||
supportsMapThreads = version.Version >= MapThreadsVersion;
|
||||
return supportsCli ? "dd.exe" : "dreamdaemon.exe";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ValueTask InstallByond(Version version, string path, CancellationToken cancellationToken)
|
||||
public override ValueTask InstallByond(ByondVersion version, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
var tasks = new List<ValueTask>(3)
|
||||
{
|
||||
@@ -139,7 +140,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask UpgradeInstallation(Version version, string path, CancellationToken cancellationToken)
|
||||
public override async ValueTask UpgradeInstallation(ByondVersion version, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
ArgumentNullException.ThrowIfNull(path);
|
||||
@@ -147,7 +148,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
if (generalConfiguration.SkipAddingByondFirewallException)
|
||||
return;
|
||||
|
||||
if (version < DDExeVersion)
|
||||
if (version.Version < DDExeVersion)
|
||||
return;
|
||||
|
||||
if (await IOManager.FileExists(IOManager.ConcatPath(path, TgsFirewalledDDFile), cancellationToken))
|
||||
@@ -227,7 +228,7 @@ namespace Tgstation.Server.Host.Components.Byond
|
||||
/// <param name="path">The path to the BYOND installation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
async ValueTask AddDreamDaemonToFirewall(Version version, string path, CancellationToken cancellationToken)
|
||||
async ValueTask AddDreamDaemonToFirewall(ByondVersion version, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
var dreamDaemonName = GetDreamDaemonName(version, out var usesDDExe, out var _);
|
||||
|
||||
|
||||
@@ -359,7 +359,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
/// <inheritdoc />
|
||||
public Func<string, string, Action<bool>> QueueDeploymentMessage(
|
||||
Models.RevisionInformation revisionInformation,
|
||||
Version byondVersion,
|
||||
ByondVersion byondVersion,
|
||||
DateTimeOffset? estimatedCompletionTime,
|
||||
string gitHubOwner,
|
||||
string gitHubRepo,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -49,20 +48,45 @@ namespace Tgstation.Server.Host.Components.Chat.Commands
|
||||
/// <inheritdoc />
|
||||
public ValueTask<MessageContent> Invoke(string arguments, ChatUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
if (arguments.Split(' ').Any(x => x.ToUpperInvariant() == "--ACTIVE"))
|
||||
return ValueTask.FromResult(new MessageContent
|
||||
{
|
||||
Text = byondManager.ActiveVersion == null ? "None!" : String.Format(CultureInfo.InvariantCulture, "{0}.{1}", byondManager.ActiveVersion.Major, byondManager.ActiveVersion.Minor),
|
||||
});
|
||||
if (watchdog.Status == WatchdogStatus.Offline)
|
||||
return ValueTask.FromResult(new MessageContent
|
||||
{
|
||||
Text = "Server offline!",
|
||||
});
|
||||
return ValueTask.FromResult(new MessageContent
|
||||
if (arguments.Split(' ').Any(x => x.Equals("--active", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Text = watchdog.ActiveCompileJob?.ByondVersion ?? "None!",
|
||||
});
|
||||
string text;
|
||||
if (byondManager.ActiveVersion == null)
|
||||
text = "None!";
|
||||
else
|
||||
switch (byondManager.ActiveVersion.Engine.Value)
|
||||
{
|
||||
case EngineType.OpenDream:
|
||||
text = $"OpenDream: {byondManager.ActiveVersion.SourceCommittish}";
|
||||
break;
|
||||
case EngineType.Byond:
|
||||
text = $"BYOND {byondManager.ActiveVersion.Version.Major}.{byondManager.ActiveVersion.Version.Minor}";
|
||||
if (byondManager.ActiveVersion.Version.Build != -1)
|
||||
text += " (Custom Build)";
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid EngineType: {byondManager.ActiveVersion.Engine.Value}");
|
||||
}
|
||||
|
||||
return ValueTask.FromResult(
|
||||
new MessageContent
|
||||
{
|
||||
Text = text,
|
||||
});
|
||||
}
|
||||
|
||||
if (watchdog.Status == WatchdogStatus.Offline)
|
||||
return ValueTask.FromResult(
|
||||
new MessageContent
|
||||
{
|
||||
Text = "Server offline!",
|
||||
});
|
||||
return ValueTask.FromResult(
|
||||
new MessageContent
|
||||
{
|
||||
Text = watchdog.ActiveCompileJob?.ByondVersion ?? "None!",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
/// Send the message for a deployment to configured deployment channels.
|
||||
/// </summary>
|
||||
/// <param name="revisionInformation">The <see cref="RevisionInformation"/> of the deployment.</param>
|
||||
/// <param name="byondVersion">The BYOND <see cref="Version"/> of the deployment.</param>
|
||||
/// <param name="byondVersion">The <see cref="ByondVersion"/> of the deployment.</param>
|
||||
/// <param name="estimatedCompletionTime">The optional <see cref="DateTimeOffset"/> the deployment is expected to be completed at.</param>
|
||||
/// <param name="gitHubOwner">The repository GitHub owner, if any.</param>
|
||||
/// <param name="gitHubRepo">The repository GitHub name, if any.</param>
|
||||
@@ -69,7 +69,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
/// <returns>A <see cref="Func{T1, T2, TResult}"/> to call to update the message at the deployment's conclusion. Parameters: Error message if any, DreamMaker output if any. Returns an <see cref="Action"/> to call to mark the deployment as active/inactive. Parameter: If the deployment is being activated or inactivated.</returns>
|
||||
Func<string, string, Action<bool>> QueueDeploymentMessage(
|
||||
Models.RevisionInformation revisionInformation,
|
||||
Version byondVersion,
|
||||
ByondVersion byondVersion,
|
||||
DateTimeOffset? estimatedCompletionTime,
|
||||
string gitHubOwner,
|
||||
string gitHubRepo,
|
||||
|
||||
@@ -22,6 +22,7 @@ using Remora.Rest.Results;
|
||||
using Remora.Results;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Common.Extensions;
|
||||
using Tgstation.Server.Host.Components.Interop;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
@@ -129,25 +130,35 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// Create a <see cref="List{T}"/> of <see cref="IEmbedField"/>s for a discord update embed.
|
||||
/// </summary>
|
||||
/// <param name="revisionInformation">The <see cref="RevisionInformation"/> of the deployment.</param>
|
||||
/// <param name="byondVersion">The BYOND <see cref="Version"/> of the deployment.</param>
|
||||
/// <param name="byondVersion">The <see cref="ByondVersion"/> of the deployment.</param>
|
||||
/// <param name="gitHubOwner">The repository GitHub owner, if any.</param>
|
||||
/// <param name="gitHubRepo">The repository GitHub name, if any.</param>
|
||||
/// <param name="localCommitPushed"><see langword="true"/> if the local deployment commit was pushed to the remote repository.</param>
|
||||
/// <returns>A new <see cref="List{T}"/> of <see cref="IEmbedField"/>s to use.</returns>
|
||||
static List<IEmbedField> BuildUpdateEmbedFields(
|
||||
Models.RevisionInformation revisionInformation,
|
||||
Version byondVersion,
|
||||
ByondVersion byondVersion,
|
||||
string gitHubOwner,
|
||||
string gitHubRepo,
|
||||
bool localCommitPushed)
|
||||
{
|
||||
bool gitHub = gitHubOwner != null && gitHubRepo != null;
|
||||
var engineField = byondVersion.Engine.Value switch
|
||||
{
|
||||
EngineType.Byond => new EmbedField(
|
||||
"BYOND Version",
|
||||
$"{byondVersion.Version.Major}.{byondVersion.Version.Minor}{(byondVersion.Version.Build > 0 ? $".{byondVersion.Version.Build}" : String.Empty)}",
|
||||
true),
|
||||
EngineType.OpenDream => new EmbedField(
|
||||
"OpenDream Version",
|
||||
$"[{byondVersion.SourceCommittish[..7]}](https://github.com/OpenDreamProject/OpenDream/commit/{revisionInformation.CommitSha})",
|
||||
true),
|
||||
_ => throw new InvalidOperationException($"Invaild EngineType: {byondVersion.Engine.Value}"),
|
||||
};
|
||||
|
||||
var fields = new List<IEmbedField>
|
||||
{
|
||||
new EmbedField(
|
||||
"BYOND Version",
|
||||
$"{byondVersion.Major}.{byondVersion.Minor}{(byondVersion.Build > 0 ? $".{byondVersion.Build}" : String.Empty)}",
|
||||
true),
|
||||
engineField,
|
||||
new EmbedField(
|
||||
"Local Commit",
|
||||
localCommitPushed && gitHub
|
||||
@@ -323,7 +334,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask<Func<string, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
|
||||
Models.RevisionInformation revisionInformation,
|
||||
Version byondVersion,
|
||||
ByondVersion byondVersion,
|
||||
DateTimeOffset? estimatedCompletionTime,
|
||||
string gitHubOwner,
|
||||
string gitHubRepo,
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Interop;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
@@ -83,7 +84,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// Send the message for a deployment.
|
||||
/// </summary>
|
||||
/// <param name="revisionInformation">The <see cref="RevisionInformation"/> of the deployment.</param>
|
||||
/// <param name="byondVersion">The BYOND <see cref="Version"/> of the deployment.</param>
|
||||
/// <param name="byondVersion">The <see cref="ByondVersion"/> of the deployment.</param>
|
||||
/// <param name="estimatedCompletionTime">The optional <see cref="DateTimeOffset"/> the deployment is expected to be completed at.</param>
|
||||
/// <param name="gitHubOwner">The repository GitHub owner, if any.</param>
|
||||
/// <param name="gitHubRepo">The repository GitHub name, if any.</param>
|
||||
@@ -92,8 +93,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="Func{T1, T2, TResult}"/> to call to update the message at the deployment's conclusion. Parameters: Error message if any, DreamMaker output if any. Returns another callback which should be called to mark the deployment as active.</returns>
|
||||
ValueTask<Func<string, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
|
||||
RevisionInformation revisionInformation,
|
||||
Version byondVersion,
|
||||
Models.RevisionInformation revisionInformation,
|
||||
ByondVersion byondVersion,
|
||||
DateTimeOffset? estimatedCompletionTime,
|
||||
string gitHubOwner,
|
||||
string gitHubRepo,
|
||||
|
||||
@@ -12,6 +12,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Interop;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
@@ -220,7 +221,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// <inheritdoc />
|
||||
public override async ValueTask<Func<string, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
|
||||
Models.RevisionInformation revisionInformation,
|
||||
Version byondVersion,
|
||||
ByondVersion byondVersion,
|
||||
DateTimeOffset? estimatedCompletionTime,
|
||||
string gitHubOwner,
|
||||
string gitHubRepo,
|
||||
@@ -271,9 +272,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
commitInsert,
|
||||
testmergeInsert,
|
||||
remoteCommitInsert,
|
||||
byondVersion.Build > 0
|
||||
? byondVersion.ToString()
|
||||
: $"{byondVersion.Major}.{byondVersion.Minor}",
|
||||
byondVersion.ToString(),
|
||||
estimatedCompletionTime.HasValue
|
||||
? $" ETA: {estimatedCompletionTime - DateTimeOffset.UtcNow}"
|
||||
: String.Empty),
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Api.Rights;
|
||||
using Tgstation.Server.Host.Components.Interop;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
@@ -182,8 +183,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract ValueTask<Func<string, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
|
||||
RevisionInformation revisionInformation,
|
||||
Version byondVersion,
|
||||
Models.RevisionInformation revisionInformation,
|
||||
ByondVersion byondVersion,
|
||||
DateTimeOffset? estimatedCompletionTime,
|
||||
string gitHubOwner,
|
||||
string gitHubRepo,
|
||||
@@ -273,7 +274,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
connectNow = false;
|
||||
if (!Connected)
|
||||
{
|
||||
var job = new Job
|
||||
var job = new Models.Job
|
||||
{
|
||||
Description = $"Reconnect chat bot: {ChatBot.Name}",
|
||||
CancelRight = (ulong)ChatBotRights.WriteEnabled,
|
||||
|
||||
@@ -245,6 +245,12 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
.ThenInclude(x => x.MergedBy)
|
||||
.FirstAsync(cancellationToken)); // can't wait to see that query
|
||||
|
||||
if (!Api.Models.Internal.ByondVersion.TryParse(compileJob.ByondVersion, out var byondVersion))
|
||||
{
|
||||
logger.LogWarning("Error loading compile job, bad BYOND version: {0}", compileJob.ByondVersion);
|
||||
return null; // omae wa mou shinderu
|
||||
}
|
||||
|
||||
if (!compileJob.Job.StoppedAt.HasValue)
|
||||
{
|
||||
// This happens when we're told to load the compile job that is currently finished up
|
||||
@@ -262,7 +268,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
CleanRegisteredCompileJob(compileJob);
|
||||
}
|
||||
|
||||
var newProvider = new DmbProvider(compileJob, ioManager, CleanupAction);
|
||||
var newProvider = new DmbProvider(compileJob, byondVersion, ioManager, CleanupAction);
|
||||
try
|
||||
{
|
||||
const string LegacyADirectoryName = "A";
|
||||
@@ -299,7 +305,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
// rebuild the provider because it's using the legacy style directories
|
||||
// Don't dispose it
|
||||
logger.LogDebug("Creating legacy two folder .dmb provider targeting {aDirName} directory...", LegacyADirectoryName);
|
||||
newProvider = new DmbProvider(compileJob, ioManager, CleanupAction, Path.DirectorySeparatorChar + LegacyADirectoryName);
|
||||
newProvider = new DmbProvider(compileJob, byondVersion, ioManager, CleanupAction, Path.DirectorySeparatorChar + LegacyADirectoryName);
|
||||
}
|
||||
|
||||
lock (jobLockCounts)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
@@ -14,10 +14,11 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <inheritdoc />
|
||||
public string Directory => ioManager.ResolvePath(CompileJob.DirectoryName.ToString() + directoryAppend);
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CompileJob"/> for the <see cref="DmbProvider"/>.
|
||||
/// </summary>
|
||||
public CompileJob CompileJob { get; }
|
||||
/// <inheritdoc />
|
||||
public Models.CompileJob CompileJob { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ByondVersion ByondVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for the <see cref="DmbProvider"/>.
|
||||
@@ -25,7 +26,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
readonly IIOManager ioManager;
|
||||
|
||||
/// <summary>
|
||||
/// Extra path to add to the end of <see cref="Api.Models.Internal.CompileJob.DirectoryName"/>.
|
||||
/// Extra path to add to the end of <see cref="CompileJob.DirectoryName"/>.
|
||||
/// </summary>
|
||||
readonly string directoryAppend;
|
||||
|
||||
@@ -38,12 +39,14 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// Initializes a new instance of the <see cref="DmbProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="compileJob">The value of <see cref="CompileJob"/>.</param>
|
||||
/// <param name="byondVersion">The value of <see cref="ByondVersion"/>.</param>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
|
||||
/// <param name="onDispose">The value of <see cref="onDispose"/>.</param>
|
||||
/// <param name="directoryAppend">The optional value of <see cref="directoryAppend"/>.</param>
|
||||
public DmbProvider(CompileJob compileJob, IIOManager ioManager, Action onDispose, string directoryAppend = null)
|
||||
public DmbProvider(Models.CompileJob compileJob, ByondVersion byondVersion, IIOManager ioManager, Action onDispose, string directoryAppend = null)
|
||||
{
|
||||
CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob));
|
||||
ByondVersion = byondVersion ?? throw new ArgumentNullException(nameof(byondVersion));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
|
||||
this.directoryAppend = directoryAppend ?? String.Empty;
|
||||
|
||||
@@ -553,7 +553,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <param name="job">The <see cref="CompileJob"/> to run and populate.</param>
|
||||
/// <param name="dreamMakerSettings">The <see cref="Api.Models.Internal.DreamMakerSettings"/> to use.</param>
|
||||
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> to use.</param>
|
||||
/// <param name="byondLock">The <see cref="IByondExecutableLock"/> to use.</param>
|
||||
/// <param name="byondLock">The <see cref="IEngineExecutableLock"/> to use.</param>
|
||||
/// <param name="repository">The <see cref="IRepository"/> to use.</param>
|
||||
/// <param name="remoteDeploymentManager">The <see cref="IRemoteDeploymentManager"/> to use.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
@@ -563,7 +563,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
Models.CompileJob job,
|
||||
Api.Models.Internal.DreamMakerSettings dreamMakerSettings,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
IByondExecutableLock byondLock,
|
||||
IEngineExecutableLock byondLock,
|
||||
IRepository repository,
|
||||
IRemoteDeploymentManager remoteDeploymentManager,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -591,7 +591,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
resolvedOutputDirectory,
|
||||
repoOrigin.ToString(),
|
||||
$"{byondLock.Version.Major}.{byondLock.Version.Minor}",
|
||||
byondLock.Version.ToString(),
|
||||
},
|
||||
true,
|
||||
cancellationToken);
|
||||
@@ -630,14 +630,14 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
resolvedOutputDirectory,
|
||||
repoOrigin.ToString(),
|
||||
$"{byondLock.Version.Major}.{byondLock.Version.Minor}",
|
||||
byondLock.Version.ToString(),
|
||||
},
|
||||
true,
|
||||
cancellationToken);
|
||||
|
||||
// run compiler
|
||||
progressReporter.StageName = "Running DreamMaker";
|
||||
var exitCode = await RunDreamMaker(byondLock.DreamMakerPath, job, cancellationToken);
|
||||
var compileSuceeded = await RunDreamMaker(byondLock, job, cancellationToken);
|
||||
|
||||
// Session takes ownership of the lock and Disposes it so save this for later
|
||||
var byondVersion = byondLock.Version;
|
||||
@@ -645,10 +645,10 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
// verify api
|
||||
try
|
||||
{
|
||||
if (exitCode != 0)
|
||||
if (!compileSuceeded)
|
||||
throw new JobException(
|
||||
ErrorCode.DreamMakerExitCode,
|
||||
new JobException($"Exit code: {exitCode}{Environment.NewLine}{Environment.NewLine}{job.Output}"));
|
||||
new JobException($"Compilation failed:{Environment.NewLine}{Environment.NewLine}{job.Output}"));
|
||||
|
||||
progressReporter.StageName = "Validating DMAPI";
|
||||
await VerifyApi(
|
||||
@@ -670,7 +670,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
new List<string>
|
||||
{
|
||||
resolvedOutputDirectory,
|
||||
exitCode == 0 ? "1" : "0",
|
||||
compileSuceeded ? "1" : "0",
|
||||
byondVersion.ToString(),
|
||||
},
|
||||
true,
|
||||
@@ -769,7 +769,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <param name="timeout">The timeout in seconds for validation.</param>
|
||||
/// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level to use to validate the API.</param>
|
||||
/// <param name="job">The <see cref="CompileJob"/> for the operation.</param>
|
||||
/// <param name="byondLock">The current <see cref="IByondExecutableLock"/>.</param>
|
||||
/// <param name="byondLock">The current <see cref="IEngineExecutableLock"/>.</param>
|
||||
/// <param name="portToUse">The port to use for API validation.</param>
|
||||
/// <param name="requireValidate">If the API validation is required to complete the deployment.</param>
|
||||
/// <param name="logOutput">If output should be logged to the DreamDaemon Diagnostics folder.</param>
|
||||
@@ -779,7 +779,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
uint timeout,
|
||||
DreamDaemonSecurity securityLevel,
|
||||
Models.CompileJob job,
|
||||
IByondExecutableLock byondLock,
|
||||
IEngineExecutableLock byondLock,
|
||||
ushort portToUse,
|
||||
bool requireValidate,
|
||||
bool logOutput,
|
||||
@@ -803,7 +803,11 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
job.MinimumSecurityLevel = securityLevel; // needed for the TempDmbProvider
|
||||
|
||||
ApiValidationStatus validationStatus;
|
||||
using (var provider = new TemporaryDmbProvider(ioManager.ResolvePath(job.DirectoryName.ToString()), String.Concat(job.DmeName, DmbExtension), job))
|
||||
using (var provider = new TemporaryDmbProvider(
|
||||
ioManager.ResolvePath(job.DirectoryName.ToString()),
|
||||
String.Concat(job.DmeName, DmbExtension),
|
||||
job,
|
||||
byondLock.Version))
|
||||
await using (var controller = await sessionControllerFactory.LaunchNew(provider, byondLock, launchParameters, true, cancellationToken))
|
||||
{
|
||||
var launchResult = await controller.LaunchResult.WaitAsync(cancellationToken);
|
||||
@@ -853,33 +857,46 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// Compiles a .dme with DreamMaker.
|
||||
/// </summary>
|
||||
/// <param name="dreamMakerPath">The path to the DreamMaker executable.</param>
|
||||
/// <param name="engineLock">The <see cref="IEngineExecutableLock"/> to use.</param>
|
||||
/// <param name="job">The <see cref="CompileJob"/> for the operation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
|
||||
async ValueTask<int> RunDreamMaker(string dreamMakerPath, Models.CompileJob job, CancellationToken cancellationToken)
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in <see langword="true"/> if compilation succeeded, <see langword="false"/> otherwise.</returns>
|
||||
async ValueTask<bool> RunDreamMaker(IEngineExecutableLock engineLock, Models.CompileJob job, CancellationToken cancellationToken)
|
||||
{
|
||||
await using var dm = processExecutor.LaunchProcess(
|
||||
dreamMakerPath,
|
||||
ioManager.ResolvePath(
|
||||
job.DirectoryName.ToString()),
|
||||
$"-clean {job.DmeName}.{DmeExtension}",
|
||||
readStandardHandles: true,
|
||||
noShellExecute: true);
|
||||
var arguments = engineLock.FormatCompilerArguments($"{job.DmeName}.{DmeExtension}");
|
||||
bool result;
|
||||
if (arguments == null)
|
||||
{
|
||||
logger.LogTrace("Engine lock says compilation isn't necessary.");
|
||||
job.Output = $"{engineLock.Version.Engine} does not require compilation.";
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
await using var dm = processExecutor.LaunchProcess(
|
||||
engineLock.CompilerExePath,
|
||||
ioManager.ResolvePath(
|
||||
job.DirectoryName.ToString()),
|
||||
arguments,
|
||||
readStandardHandles: true,
|
||||
noShellExecute: true);
|
||||
|
||||
if (sessionConfiguration.LowPriorityDeploymentProcesses)
|
||||
dm.AdjustPriority(false);
|
||||
if (sessionConfiguration.LowPriorityDeploymentProcesses)
|
||||
dm.AdjustPriority(false);
|
||||
|
||||
int exitCode;
|
||||
using (cancellationToken.Register(() => dm.Terminate()))
|
||||
exitCode = (await dm.Lifetime).Value;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
int exitCode;
|
||||
using (cancellationToken.Register(() => dm.Terminate()))
|
||||
exitCode = (await dm.Lifetime).Value;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
logger.LogDebug("DreamMaker exit code: {exitCode}", exitCode);
|
||||
job.Output = $"{await dm.GetCombinedOutput(cancellationToken)}{Environment.NewLine}{Environment.NewLine}Exit Code: {exitCode}";
|
||||
logger.LogDebug("DreamMaker output: {newLine}{output}", Environment.NewLine, job.Output);
|
||||
result = exitCode == 0;
|
||||
}
|
||||
|
||||
logger.LogDebug("DreamMaker exit code: {exitCode}", exitCode);
|
||||
job.Output = await dm.GetCombinedOutput(cancellationToken);
|
||||
currentDreamMakerOutput = job.Output;
|
||||
logger.LogDebug("DreamMaker output: {newLine}{output}", Environment.NewLine, job.Output);
|
||||
return exitCode;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
@@ -22,7 +22,12 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <summary>
|
||||
/// The <see cref="CompileJob"/> of the .dmb.
|
||||
/// </summary>
|
||||
CompileJob CompileJob { get; }
|
||||
Models.CompileJob CompileJob { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Api.Models.Internal.ByondVersion"/> used to build the .dmb.
|
||||
/// </summary>
|
||||
ByondVersion ByondVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Disposing the <see cref="IDmbProvider"/> won't cause a cleanup of the working directory.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
@@ -24,7 +24,10 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
public string Directory => ioManager.ResolvePath(LiveGameDirectory);
|
||||
|
||||
/// <inheritdoc />
|
||||
public CompileJob CompileJob => baseProvider.CompileJob;
|
||||
public Models.CompileJob CompileJob => baseProvider.CompileJob;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ByondVersion ByondVersion => baseProvider.ByondVersion;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="MakeActive(CancellationToken)"/> has been run.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Deployment
|
||||
{
|
||||
@@ -16,7 +16,10 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
public string Directory { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public CompileJob CompileJob { get; }
|
||||
public Models.CompileJob CompileJob { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ByondVersion ByondVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TemporaryDmbProvider"/> class.
|
||||
@@ -24,11 +27,13 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <param name="directory">The value of <see cref="Directory"/>.</param>
|
||||
/// <param name="dmb">The value of <see cref="DmbName"/>.</param>
|
||||
/// <param name="compileJob">The value of <see cref="CompileJob"/>.</param>
|
||||
public TemporaryDmbProvider(string directory, string dmb, CompileJob compileJob)
|
||||
/// <param name="byondVersion">The value of <see cref="ByondVersion"/>.</param>
|
||||
public TemporaryDmbProvider(string directory, string dmb, Models.CompileJob compileJob, ByondVersion byondVersion)
|
||||
{
|
||||
DmbName = dmb ?? throw new ArgumentNullException(nameof(dmb));
|
||||
Directory = directory ?? throw new ArgumentNullException(nameof(directory));
|
||||
CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob));
|
||||
ByondVersion = byondVersion ?? throw new ArgumentNullException(nameof(byondVersion));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -16,14 +16,14 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
/// Create a <see cref="ISessionController"/> from a freshly launch DreamDaemon instance.
|
||||
/// </summary>
|
||||
/// <param name="dmbProvider">The <see cref="IDmbProvider"/> to use.</param>
|
||||
/// <param name="currentByondLock">The current <see cref="IByondExecutableLock"/> if any.</param>
|
||||
/// <param name="currentByondLock">The current <see cref="IEngineExecutableLock"/> if any.</param>
|
||||
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> to use. <see cref="DreamDaemonLaunchParameters.SecurityLevel"/> will be updated with the minumum required security level for the launch.</param>
|
||||
/// <param name="apiValidate">If the <see cref="ISessionController"/> should only validate the DMAPI then exit.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a new <see cref="ISessionController"/>.</returns>
|
||||
ValueTask<ISessionController> LaunchNew(
|
||||
IDmbProvider dmbProvider,
|
||||
IByondExecutableLock currentByondLock,
|
||||
IEngineExecutableLock currentByondLock,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
bool apiValidate,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
@@ -123,9 +123,9 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
readonly IProcess process;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IByondExecutableLock"/> for the <see cref="SessionController"/>.
|
||||
/// The <see cref="IEngineExecutableLock"/> for the <see cref="SessionController"/>.
|
||||
/// </summary>
|
||||
readonly IByondExecutableLock byondLock;
|
||||
readonly IEngineExecutableLock byondLock;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IChatTrackingContext"/> for the <see cref="SessionController"/>.
|
||||
@@ -244,7 +244,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
ReattachInformation reattachInformation,
|
||||
Api.Models.Instance metadata,
|
||||
IProcess process,
|
||||
IByondExecutableLock byondLock,
|
||||
IEngineExecutableLock byondLock,
|
||||
global::Byond.TopicSender.ITopicClient byondTopicSender,
|
||||
IChatTrackingContext chatTrackingContext,
|
||||
IBridgeRegistrar bridgeRegistrar,
|
||||
|
||||
@@ -6,8 +6,6 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Byond.TopicSender;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -128,38 +126,6 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
/// </summary>
|
||||
readonly Api.Models.Instance instance;
|
||||
|
||||
/// <summary>
|
||||
/// Change a given <paramref name="securityLevel"/> into the appropriate DreamDaemon command line word.
|
||||
/// </summary>
|
||||
/// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level to change.</param>
|
||||
/// <returns>A <see cref="string"/> representation of the command line parameter.</returns>
|
||||
static string SecurityWord(DreamDaemonSecurity securityLevel)
|
||||
{
|
||||
return securityLevel switch
|
||||
{
|
||||
DreamDaemonSecurity.Safe => "safe",
|
||||
DreamDaemonSecurity.Trusted => "trusted",
|
||||
DreamDaemonSecurity.Ultrasafe => "ultrasafe",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon security level: {0}", securityLevel)),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change a given <paramref name="visibility"/> into the appropriate DreamDaemon command line word.
|
||||
/// </summary>
|
||||
/// <param name="visibility">The <see cref="DreamDaemonVisibility"/> level to change.</param>
|
||||
/// <returns>A <see cref="string"/> representation of the command line parameter.</returns>
|
||||
static string VisibilityWord(DreamDaemonVisibility visibility)
|
||||
{
|
||||
return visibility switch
|
||||
{
|
||||
DreamDaemonVisibility.Public => "public",
|
||||
DreamDaemonVisibility.Private => "private",
|
||||
DreamDaemonVisibility.Invisible => "invisible",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(visibility), visibility, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon visibility level: {0}", visibility)),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a given <paramref name="port"/> can be bound to.
|
||||
/// </summary>
|
||||
@@ -242,7 +208,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
#pragma warning disable CA1506 // TODO: Decomplexify
|
||||
public async ValueTask<ISessionController> LaunchNew(
|
||||
IDmbProvider dmbProvider,
|
||||
IByondExecutableLock currentByondLock,
|
||||
IEngineExecutableLock currentByondLock,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
bool apiValidate,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -274,7 +240,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
|
||||
// get the byond lock
|
||||
var byondLock = currentByondLock ?? await byond.UseExecutables(
|
||||
Version.Parse(dmbProvider.CompileJob.ByondVersion),
|
||||
dmbProvider.ByondVersion,
|
||||
gameIOManager.ConcatPath(dmbProvider.Directory, dmbProvider.DmbName),
|
||||
cancellationToken);
|
||||
try
|
||||
@@ -289,7 +255,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
string outputFilePath = null;
|
||||
var preserveLogFile = true;
|
||||
|
||||
var cliSupported = byondLock.SupportsCli;
|
||||
var hasStandardOutput = byondLock.HasStandardOutput;
|
||||
if (launchParameters.LogOutput.Value)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
@@ -302,7 +268,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
|
||||
logger.LogInformation("Logging DreamDaemon output to {path}...", outputFilePath);
|
||||
}
|
||||
else if (!cliSupported)
|
||||
else if (!hasStandardOutput)
|
||||
{
|
||||
outputFilePath = gameIOManager.ConcatPath(dmbProvider.Directory, $"{Guid.NewGuid()}.dd.log");
|
||||
preserveLogFile = false;
|
||||
@@ -310,17 +276,12 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
|
||||
var accessIdentifier = cryptographySuite.GetSecureString();
|
||||
|
||||
var byondTopicSender = topicClientFactory.CreateTopicClient(
|
||||
TimeSpan.FromMilliseconds(
|
||||
launchParameters.TopicRequestTimeout.Value));
|
||||
|
||||
if (!apiValidate && dmbProvider.CompileJob.DMApiVersion == null)
|
||||
logger.LogDebug("Session will have no DMAPI support!");
|
||||
|
||||
// launch dd
|
||||
var process = await CreateDreamDaemonProcess(
|
||||
var process = await CreateGameServerProcess(
|
||||
dmbProvider,
|
||||
byondTopicSender,
|
||||
byondLock,
|
||||
launchParameters,
|
||||
accessIdentifier,
|
||||
@@ -347,6 +308,10 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
accessIdentifier,
|
||||
launchParameters.Port.Value);
|
||||
|
||||
var byondTopicSender = topicClientFactory.CreateTopicClient(
|
||||
TimeSpan.FromMilliseconds(
|
||||
launchParameters.TopicRequestTimeout.Value));
|
||||
|
||||
var sessionController = new SessionController(
|
||||
reattachInformation,
|
||||
instance,
|
||||
@@ -362,7 +327,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
() => LogDDOutput(
|
||||
process,
|
||||
outputFilePath,
|
||||
cliSupported,
|
||||
hasStandardOutput,
|
||||
preserveLogFile,
|
||||
CancellationToken.None), // DCT: None available
|
||||
launchParameters.StartupTimeout,
|
||||
@@ -406,7 +371,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
logger.LogTrace("Begin session reattach...");
|
||||
var byondTopicSender = topicClientFactory.CreateTopicClient(reattachInformation.TopicRequestTimeout);
|
||||
var byondLock = await byond.UseExecutables(
|
||||
Version.Parse(reattachInformation.Dmb.CompileJob.ByondVersion),
|
||||
reattachInformation.Dmb.ByondVersion,
|
||||
null, // Doesn't matter if it's trusted or not on reattach
|
||||
cancellationToken);
|
||||
|
||||
@@ -423,7 +388,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
|
||||
try
|
||||
{
|
||||
if (!byondLock.SupportsCli)
|
||||
if (byondLock.PromptsForNetworkAccess)
|
||||
networkPromptReaper.RegisterProcess(process);
|
||||
|
||||
var chatTrackingContext = chat.CreateTrackingContext();
|
||||
@@ -479,60 +444,45 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the DreamDaemon <see cref="IProcess"/>.
|
||||
/// Creates the game server <see cref="IProcess"/>.
|
||||
/// </summary>
|
||||
/// <param name="dmbProvider">The <see cref="IDmbProvider"/>.</param>
|
||||
/// <param name="byondTopicSender">The <see cref="ITopicClient"/> to use for sanitization.</param>
|
||||
/// <param name="byondLock">The <see cref="IByondExecutableLock"/>.</param>
|
||||
/// <param name="byondLock">The <see cref="IEngineExecutableLock"/>.</param>
|
||||
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/>.</param>
|
||||
/// <param name="accessIdentifier">The secure string to use for the session.</param>
|
||||
/// <param name="logFilePath">The path to log DreamDaemon output to.</param>
|
||||
/// <param name="apiValidate">If we are only validating the DMAPI then exiting.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the DreamDaemon <see cref="IProcess"/>.</returns>
|
||||
async ValueTask<IProcess> CreateDreamDaemonProcess(
|
||||
async ValueTask<IProcess> CreateGameServerProcess(
|
||||
IDmbProvider dmbProvider,
|
||||
ITopicClient byondTopicSender,
|
||||
IByondExecutableLock byondLock,
|
||||
IEngineExecutableLock byondLock,
|
||||
DreamDaemonLaunchParameters launchParameters,
|
||||
string accessIdentifier,
|
||||
string logFilePath,
|
||||
bool apiValidate,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// set command line options
|
||||
// more sanitization here cause it uses the same scheme
|
||||
var parameters = $"{DMApiConstants.ParamApiVersion}={byondTopicSender.SanitizeString(DMApiConstants.InteropVersion.Semver().ToString())}&{byondTopicSender.SanitizeString(DMApiConstants.ParamServerPort)}={serverPortProvider.HttpApiPort}&{byondTopicSender.SanitizeString(DMApiConstants.ParamAccessIdentifier)}={byondTopicSender.SanitizeString(accessIdentifier)}";
|
||||
|
||||
if (!String.IsNullOrEmpty(launchParameters.AdditionalParameters))
|
||||
parameters = $"{parameters}&{launchParameters.AdditionalParameters}";
|
||||
|
||||
// important to run on all ports to allow port changing
|
||||
var arguments = String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0} -port {1} -ports 1-65535 {2}-close -verbose -{3} -{4}{5}{6}{7} -params \"{8}\"",
|
||||
dmbProvider.DmbName,
|
||||
launchParameters.Port.Value,
|
||||
launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty,
|
||||
SecurityWord(launchParameters.SecurityLevel.Value),
|
||||
VisibilityWord(launchParameters.Visibility.Value),
|
||||
!byondLock.SupportsCli
|
||||
? $" -logself -log {logFilePath}"
|
||||
: String.Empty, // DD doesn't output anything if -logself is set???
|
||||
launchParameters.StartProfiler.Value
|
||||
? " -profile"
|
||||
: String.Empty,
|
||||
byondLock.SupportsMapThreads && launchParameters.MapThreads.Value != 0
|
||||
? $" -map-threads {launchParameters.MapThreads.Value}"
|
||||
: String.Empty,
|
||||
parameters);
|
||||
var arguments = byondLock.FormatServerArguments(
|
||||
dmbProvider,
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ DMApiConstants.ParamApiVersion, DMApiConstants.InteropVersion.Semver().ToString() },
|
||||
{ DMApiConstants.ParamServerPort, serverPortProvider.HttpApiPort.ToString(CultureInfo.InvariantCulture) },
|
||||
{ DMApiConstants.ParamAccessIdentifier, accessIdentifier },
|
||||
},
|
||||
launchParameters,
|
||||
!byondLock.HasStandardOutput
|
||||
? logFilePath
|
||||
: null);
|
||||
|
||||
var process = processExecutor.LaunchProcess(
|
||||
byondLock.DreamDaemonPath,
|
||||
byondLock.ServerExePath,
|
||||
dmbProvider.Directory,
|
||||
arguments,
|
||||
logFilePath,
|
||||
byondLock.SupportsCli,
|
||||
byondLock.HasStandardOutput,
|
||||
true);
|
||||
|
||||
try
|
||||
@@ -545,7 +495,7 @@ namespace Tgstation.Server.Host.Components.Session
|
||||
else if (sessionConfiguration.LowPriorityDeploymentProcesses)
|
||||
process.AdjustPriority(false);
|
||||
|
||||
if (!byondLock.SupportsCli)
|
||||
if (!byondLock.HasStandardOutput)
|
||||
networkPromptReaper.RegisterProcess(process);
|
||||
|
||||
// If this isnt a staging DD (From a Deployment), fire off an event
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="Version"/> to normalize.</param>
|
||||
/// <returns>The normalized <see cref="Version"/>. May be a reference to <paramref name="version"/>.</returns>
|
||||
static Version NormalizeVersion(Version version) => version.Build == 0 ? new Version(version.Major, version.Minor) : version;
|
||||
static Version NormalizeByondVersion(Version version) => version.Build == 0 ? new Version(version.Major, version.Minor) : version;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ByondController"/> class.
|
||||
@@ -72,23 +72,26 @@ namespace Tgstation.Server.Host.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active <see cref="ByondResponse.Version"/>.
|
||||
/// Gets the active <see cref="Api.Models.Internal.ByondVersion"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
|
||||
/// <response code="200">Retrieved version information successfully.</response>
|
||||
/// <response code="409">No BYOND versions installed.</response>
|
||||
[HttpGet]
|
||||
[TgsAuthorize(ByondRights.ReadActive)]
|
||||
[ProducesResponseType(typeof(ByondResponse), 200)]
|
||||
[ProducesResponseType(typeof(ErrorMessageResponse), 409)]
|
||||
public ValueTask<IActionResult> Read()
|
||||
=> WithComponentInstance(instance =>
|
||||
ValueTask.FromResult<IActionResult>(
|
||||
Json(new ByondResponse
|
||||
{
|
||||
Version = instance.ByondManager.ActiveVersion,
|
||||
})));
|
||||
instance.ByondManager.ActiveVersion != null
|
||||
? Json(
|
||||
new ByondResponse(
|
||||
instance.ByondManager.ActiveVersion))
|
||||
: Conflict(new ErrorMessageResponse(ErrorCode.ResourceNotPresent))));
|
||||
|
||||
/// <summary>
|
||||
/// Lists installed <see cref="ByondResponse.Version"/>s.
|
||||
/// Lists installed <see cref="Api.Models.Internal.ByondVersion"/>s.
|
||||
/// </summary>
|
||||
/// <param name="page">The current page.</param>
|
||||
/// <param name="pageSize">The page size.</param>
|
||||
@@ -106,10 +109,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
instance
|
||||
.ByondManager
|
||||
.InstalledVersions
|
||||
.Select(x => new ByondResponse
|
||||
{
|
||||
Version = x,
|
||||
})
|
||||
.Select(x => new ByondResponse(x))
|
||||
.AsQueryable()
|
||||
.OrderBy(x => x.Version))),
|
||||
null,
|
||||
@@ -126,27 +126,42 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <response code="200">Switched active version successfully.</response>
|
||||
/// <response code="202">Created <see cref="Job"/> to install and switch active version successfully.</response>
|
||||
[HttpPost]
|
||||
[TgsAuthorize(ByondRights.InstallOfficialOrChangeActiveVersion | ByondRights.InstallCustomVersion)]
|
||||
[TgsAuthorize(
|
||||
ByondRights.InstallOfficialOrChangeActiveByondVersion
|
||||
| ByondRights.InstallCustomByondVersion
|
||||
| ByondRights.InstallOfficialOrChangeActiveOpenDreamVersion
|
||||
| ByondRights.InstallCustomOpenDreamVersion)]
|
||||
[ProducesResponseType(typeof(ByondInstallResponse), 200)]
|
||||
[ProducesResponseType(typeof(ByondInstallResponse), 202)]
|
||||
#pragma warning disable CA1506 // TODO: Decomplexify
|
||||
#pragma warning disable CA1502 // TODO: Decomplexify
|
||||
#pragma warning disable CA1506
|
||||
public async ValueTask<IActionResult> Update([FromBody] ByondVersionRequest model, CancellationToken cancellationToken)
|
||||
#pragma warning restore CA1506
|
||||
#pragma warning restore CA1502
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(model);
|
||||
|
||||
var uploadingZip = model.UploadCustomZip == true;
|
||||
var isByondEngine = model.Engine.Value == EngineType.Byond;
|
||||
|
||||
if (model.Version == null
|
||||
|| model.Version.Revision != -1
|
||||
|| (uploadingZip && model.Version.Build > 0))
|
||||
if ((isByondEngine && (model.Version.Revision != -1 || (uploadingZip && model.Version.Build > 0) || model.SourceCommittish != null || model.SourceRepository != null))
|
||||
|| (!isByondEngine && (model.Version != null || String.IsNullOrWhiteSpace(model.SourceCommittish))))
|
||||
return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
|
||||
|
||||
var version = NormalizeVersion(model.Version);
|
||||
Uri sourceRepo;
|
||||
if (isByondEngine)
|
||||
{
|
||||
model.Version = NormalizeByondVersion(model.Version);
|
||||
sourceRepo = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceRepo = model.SourceRepository ?? new Uri("https://github.com/OpenDreamProject/OpenDream");
|
||||
}
|
||||
|
||||
var userByondRights = AuthenticationContext.InstancePermissionSet.ByondRights.Value;
|
||||
if ((!userByondRights.HasFlag(ByondRights.InstallOfficialOrChangeActiveVersion) && !uploadingZip)
|
||||
|| (!userByondRights.HasFlag(ByondRights.InstallCustomVersion) && uploadingZip))
|
||||
if ((!userByondRights.HasFlag(ByondRights.InstallOfficialOrChangeActiveByondVersion) && !uploadingZip)
|
||||
|| (!userByondRights.HasFlag(ByondRights.InstallCustomByondVersion) && uploadingZip))
|
||||
return Forbid();
|
||||
|
||||
// remove cruff fields
|
||||
@@ -155,44 +170,50 @@ namespace Tgstation.Server.Host.Controllers
|
||||
async instance =>
|
||||
{
|
||||
var byondManager = instance.ByondManager;
|
||||
var versionAlreadyInstalled = !uploadingZip && byondManager.InstalledVersions.Any(x => x == version);
|
||||
var versionAlreadyInstalled = !uploadingZip && byondManager.InstalledVersions.Any(x => x.Equals(model));
|
||||
if (versionAlreadyInstalled)
|
||||
{
|
||||
Logger.LogInformation(
|
||||
"User ID {userId} changing instance ID {instanceId} BYOND version to {newByondVersion}",
|
||||
"User ID {userId} changing instance ID {instanceId} {engineType} version to {newByondVersion}",
|
||||
AuthenticationContext.User.Id,
|
||||
Instance.Id,
|
||||
version);
|
||||
model.Engine,
|
||||
model.Version);
|
||||
|
||||
try
|
||||
{
|
||||
await byondManager.ChangeVersion(null, version, null, false, cancellationToken);
|
||||
await byondManager.ChangeVersion(null, model, null, false, cancellationToken);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
Logger.LogDebug(
|
||||
ex,
|
||||
"Race condition: BYOND version {version} uninstalled before we could switch to it. Creating install job instead...",
|
||||
version);
|
||||
"Race condition: {engineType} version {version} uninstalled before we could switch to it. Creating install job instead...",
|
||||
model.Engine.Value,
|
||||
model.Version);
|
||||
versionAlreadyInstalled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!versionAlreadyInstalled)
|
||||
{
|
||||
if (version.Build > 0)
|
||||
if (model.Version.Build > 0)
|
||||
return BadRequest(new ErrorMessageResponse(ErrorCode.ByondNonExistentCustomVersion));
|
||||
|
||||
Logger.LogInformation(
|
||||
"User ID {userId} installing BYOND version to {newByondVersion} on instance ID {instanceId}",
|
||||
"User ID {userId} installing {engineType} version {newByondVersion}{sourceCommittish} on instance ID {instanceId}",
|
||||
AuthenticationContext.User.Id,
|
||||
version,
|
||||
model.Engine.Value,
|
||||
model.Version,
|
||||
model.SourceCommittish != null
|
||||
? $" ({model.SourceCommittish})"
|
||||
: String.Empty,
|
||||
Instance.Id);
|
||||
|
||||
// run the install through the job manager
|
||||
var job = new Job
|
||||
{
|
||||
Description = $"Install {(!uploadingZip ? String.Empty : "custom ")}BYOND version {version}",
|
||||
Description = $"Install {(!uploadingZip ? String.Empty : "custom ")}{model.Engine.Value} version {model.Version}",
|
||||
StartedBy = AuthenticationContext.User,
|
||||
CancelRightsType = RightsType.Byond,
|
||||
CancelRight = (ulong)ByondRights.CancelInstall,
|
||||
@@ -209,7 +230,13 @@ namespace Tgstation.Server.Host.Controllers
|
||||
job,
|
||||
async (core, databaseContextFactory, paramJob, progressHandler, jobCancellationToken) =>
|
||||
{
|
||||
Stream zipFileStream = null;
|
||||
if (sourceRepo != null)
|
||||
await core.ByondManager.EnsureEngineSource(
|
||||
sourceRepo,
|
||||
model.Engine.Value,
|
||||
jobCancellationToken);
|
||||
|
||||
MemoryStream zipFileStream = null;
|
||||
if (fileUploadTicket != null)
|
||||
await using (fileUploadTicket)
|
||||
{
|
||||
@@ -229,7 +256,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
await using (zipFileStream)
|
||||
await core.ByondManager.ChangeVersion(
|
||||
progressHandler,
|
||||
version,
|
||||
model,
|
||||
zipFileStream,
|
||||
true,
|
||||
jobCancellationToken);
|
||||
@@ -260,7 +287,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation.</returns>
|
||||
/// <response code="202">Created <see cref="Job"/> to delete target version successfully.</response>
|
||||
/// <response code="409">Attempted to delete the active BYOND <see cref="Version"/>.</response>
|
||||
/// <response code="410">The <see cref="ByondVersionDeleteRequest.Version"/> specified was not installed.</response>
|
||||
/// <response code="410">The <see cref="Api.Models.Internal.ByondVersion"/> specified was not installed.</response>
|
||||
[HttpDelete]
|
||||
[TgsAuthorize(ByondRights.DeleteInstall)]
|
||||
[ProducesResponseType(typeof(JobResponse), 202)]
|
||||
@@ -270,22 +297,22 @@ namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(model);
|
||||
|
||||
if (model.Version == null
|
||||
|| model.Version.Revision != -1)
|
||||
if (model.Version.Revision != -1)
|
||||
return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
|
||||
|
||||
var version = NormalizeVersion(model.Version);
|
||||
if (model.Engine == EngineType.Byond)
|
||||
model.Version = NormalizeByondVersion(model.Version);
|
||||
|
||||
var notInstalledResponse = await WithComponentInstance(
|
||||
instance =>
|
||||
{
|
||||
var byondManager = instance.ByondManager;
|
||||
|
||||
if (version == byondManager.ActiveVersion)
|
||||
if (model.Equals(byondManager.ActiveVersion))
|
||||
return ValueTask.FromResult<IActionResult>(
|
||||
Conflict(new ErrorMessageResponse(ErrorCode.ByondCannotDeleteActiveVersion)));
|
||||
|
||||
var versionNotInstalled = !byondManager.InstalledVersions.Any(x => x == version);
|
||||
var versionNotInstalled = !byondManager.InstalledVersions.Any(x => x.Equals(model));
|
||||
|
||||
return ValueTask.FromResult<IActionResult>(
|
||||
versionNotInstalled
|
||||
@@ -296,22 +323,27 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (notInstalledResponse != null)
|
||||
return notInstalledResponse;
|
||||
|
||||
var isCustomVersion = version.Build != -1;
|
||||
var isByondVersion = model.Engine.Value == EngineType.Byond;
|
||||
|
||||
// run the install through the job manager
|
||||
var job = new Job
|
||||
{
|
||||
Description = $"Delete installed BYOND version {version}",
|
||||
Description = $"Delete installed {model.Engine.Value} version {model.Version}",
|
||||
StartedBy = AuthenticationContext.User,
|
||||
CancelRightsType = RightsType.Byond,
|
||||
CancelRight = (ulong)(isCustomVersion ? ByondRights.InstallOfficialOrChangeActiveVersion : ByondRights.InstallCustomVersion),
|
||||
CancelRight = (ulong)(
|
||||
isByondVersion
|
||||
? model.Version.Build != -1
|
||||
? ByondRights.InstallOfficialOrChangeActiveByondVersion
|
||||
: ByondRights.InstallCustomByondVersion
|
||||
: ByondRights.InstallCustomOpenDreamVersion | ByondRights.InstallOfficialOrChangeActiveOpenDreamVersion),
|
||||
Instance = Instance,
|
||||
};
|
||||
|
||||
await jobManager.RegisterOperation(
|
||||
job,
|
||||
(instanceCore, databaseContextFactory, job, progressReporter, jobCancellationToken)
|
||||
=> instanceCore.ByondManager.DeleteVersion(progressReporter, version, jobCancellationToken),
|
||||
=> instanceCore.ByondManager.DeleteVersion(progressReporter, model, jobCancellationToken),
|
||||
cancellationToken);
|
||||
|
||||
var apiResponse = job.ToApi();
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Tgstation.Server.Host.Models
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public CompileJobResponse ToApi() => new CompileJobResponse
|
||||
public CompileJobResponse ToApi() => new ()
|
||||
{
|
||||
DirectoryName = DirectoryName,
|
||||
DmeName = DmeName,
|
||||
@@ -90,7 +90,9 @@ namespace Tgstation.Server.Host.Models
|
||||
Job = Job.ToApi(),
|
||||
Output = Output,
|
||||
RevisionInformation = RevisionInformation.ToApi(),
|
||||
ByondVersion = Version.Parse(ByondVersion),
|
||||
ByondVersion = Api.Models.Internal.ByondVersion.TryParse(ByondVersion, out var version)
|
||||
? version
|
||||
: throw new InvalidOperationException($"Failed to parse BYOND version: {ByondVersion}"),
|
||||
MinimumSecurityLevel = MinimumSecurityLevel,
|
||||
DMApiVersion = DMApiVersion,
|
||||
RepositoryOrigin = RepositoryOrigin != null ? new Uri(RepositoryOrigin) : null,
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Tgstation.Server.Api.Rights.Tests
|
||||
[TestMethod]
|
||||
public void TestAllRightsWorks()
|
||||
{
|
||||
var allByondRights = ByondRights.CancelInstall | ByondRights.InstallOfficialOrChangeActiveVersion | ByondRights.ListInstalled | ByondRights.ReadActive | ByondRights.InstallCustomVersion | ByondRights.DeleteInstall;
|
||||
var allByondRights = ByondRights.CancelInstall | ByondRights.InstallOfficialOrChangeActiveByondVersion | ByondRights.ListInstalled | ByondRights.ReadActive | ByondRights.InstallCustomByondVersion | ByondRights.DeleteInstall;
|
||||
var automaticByondRights = RightsHelper.AllRights<ByondRights>();
|
||||
|
||||
Assert.AreEqual(allByondRights, automaticByondRights);
|
||||
|
||||
@@ -11,6 +11,8 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Api.Models.Response;
|
||||
using Tgstation.Server.Common.Http;
|
||||
|
||||
@@ -22,10 +24,12 @@ namespace Tgstation.Server.Client.Tests
|
||||
[TestMethod]
|
||||
public async Task TestDeserializingByondModelsWork()
|
||||
{
|
||||
var sample = new ByondResponse
|
||||
{
|
||||
Version = new Version(511, 1385, 0)
|
||||
};
|
||||
var sample = new ByondResponse(
|
||||
new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = new Version(511, 1385, 0)
|
||||
});
|
||||
|
||||
var sampleJson = JsonConvert.SerializeObject(sample, new JsonSerializerSettings
|
||||
{
|
||||
@@ -51,10 +55,12 @@ namespace Tgstation.Server.Client.Tests
|
||||
[TestMethod]
|
||||
public async Task TestUnrecognizedResponse()
|
||||
{
|
||||
var sample = new ByondResponse
|
||||
{
|
||||
Version = new Version(511, 1385)
|
||||
};
|
||||
var sample = new ByondResponse(
|
||||
new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = new Version(511, 1385)
|
||||
});
|
||||
|
||||
var fakeJson = "asdfasd <>F#(*)U*#JLI";
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.IO;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Byond.Tests
|
||||
@@ -62,7 +64,11 @@ namespace Tgstation.Server.Host.Components.Byond.Tests
|
||||
new MemoryStream(ourArray)))
|
||||
.Verifiable();
|
||||
|
||||
var result = await installer.DownloadVersion(new Version(511, 1385), default);
|
||||
var result = await installer.DownloadVersion(new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = new Version(123, 252345),
|
||||
}, default);
|
||||
|
||||
Assert.IsTrue(ourArray.SequenceEqual(result.ToArray()));
|
||||
mockIOManager.Verify();
|
||||
@@ -79,9 +85,17 @@ namespace Tgstation.Server.Host.Components.Byond.Tests
|
||||
|
||||
const string FakePath = "fake";
|
||||
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() => installer.InstallByond(null, null, default).AsTask());
|
||||
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() => installer.InstallByond(new Version(123,252345), null, default).AsTask());
|
||||
|
||||
await installer.InstallByond(new Version(511, 1385), FakePath, default);
|
||||
var byondVersion = new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = new Version(123, 252345),
|
||||
};
|
||||
|
||||
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() => installer.InstallByond(byondVersion, null, default).AsTask());
|
||||
|
||||
byondVersion.Version = new Version(511, 1385);
|
||||
await installer.InstallByond(byondVersion, FakePath, default);
|
||||
|
||||
mockPostWriteHandler.Verify(x => x.HandleWrite(It.IsAny<string>()), Times.Exactly(4));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using System;
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Tgstation.Server.Host.Security.Tests
|
||||
var authContext = new AuthenticationContext(null, user, instanceUser);
|
||||
|
||||
user.PermissionSet.AdministrationRights = AdministrationRights.WriteUsers;
|
||||
instanceUser.ByondRights = ByondRights.InstallOfficialOrChangeActiveVersion | ByondRights.ReadActive;
|
||||
instanceUser.ByondRights = ByondRights.InstallOfficialOrChangeActiveByondVersion | ByondRights.ReadActive;
|
||||
Assert.AreEqual((ulong)user.PermissionSet.AdministrationRights, authContext.GetRight(RightsType.Administration));
|
||||
Assert.AreEqual((ulong)instanceUser.ByondRights, authContext.GetRight(RightsType.Byond));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Moq;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Common.Http;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.IO;
|
||||
@@ -42,9 +43,9 @@ namespace Tgstation.Server.Tests
|
||||
var logger = loggerFactory.CreateLogger("CachingFileDownloader");
|
||||
|
||||
var cfd = new CachingFileDownloader(loggerFactory.CreateLogger<CachingFileDownloader>());
|
||||
var edgeVersion = await ByondTest.GetEdgeVersion(cfd, cancellationToken);
|
||||
var edgeVersion = await ByondTest.GetEdgeVersion(Api.Models.EngineType.Byond, cfd, cancellationToken);
|
||||
|
||||
await InitializeByondVersion(logger, edgeVersion, new PlatformIdentifier().IsWindows, cancellationToken);
|
||||
await InitializeByondVersion(logger, edgeVersion.Version, new PlatformIdentifier().IsWindows, cancellationToken);
|
||||
|
||||
// predownload the target github release update asset
|
||||
var gitHubToken = Environment.GetEnvironmentVariable("TGS_TEST_GITHUB_TOKEN");
|
||||
@@ -76,10 +77,16 @@ namespace Tgstation.Server.Tests
|
||||
ServiceCollectionExtensions.UseFileDownloader<CachingFileDownloader>();
|
||||
}
|
||||
|
||||
public static async ValueTask InitializeByondVersion(ILogger logger, Version version, bool windows, CancellationToken cancellationToken)
|
||||
public static async ValueTask InitializeByondVersion(ILogger logger, Version byondVersion, bool windows, CancellationToken cancellationToken)
|
||||
{
|
||||
var version = new ByondVersion
|
||||
{
|
||||
Engine = Api.Models.EngineType.Byond,
|
||||
Version = byondVersion,
|
||||
};
|
||||
|
||||
var url = new Uri(
|
||||
$"https://www.byond.com/download/build/{version.Major}/{version.Major}.{version.Minor}_byond{(!windows ? "_linux" : string.Empty)}.zip");
|
||||
$"https://www.byond.com/download/build/{version.Version.Major}/{version.Version.Major}.{version.Version.Minor}_byond{(!windows ? "_linux" : string.Empty)}.zip");
|
||||
string path = null;
|
||||
if (TestingUtils.RunningInGitHubActions)
|
||||
{
|
||||
@@ -91,7 +98,7 @@ namespace Tgstation.Server.Tests
|
||||
windows ? "windows" : "linux");
|
||||
path = Path.Combine(
|
||||
dir,
|
||||
$"{version.Major}.{version.Minor}.zip");
|
||||
$"{version.Version.Major}.{version.Version.Minor}.zip");
|
||||
}
|
||||
|
||||
await (await CacheFile(logger, url, null, path, cancellationToken)).DisposeAsync();
|
||||
|
||||
@@ -12,6 +12,7 @@ using Moq;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Chat;
|
||||
using Tgstation.Server.Host.Components.Chat.Commands;
|
||||
using Tgstation.Server.Host.Components.Chat.Providers;
|
||||
@@ -100,7 +101,15 @@ namespace Tgstation.Server.Tests.Live
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
|
||||
public override ValueTask<Func<string, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(RevisionInformation revisionInformation, Version byondVersion, DateTimeOffset? estimatedCompletionTime, string gitHubOwner, string gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken)
|
||||
public override ValueTask<Func<string, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
|
||||
Host.Models.RevisionInformation revisionInformation,
|
||||
ByondVersion byondVersion,
|
||||
DateTimeOffset? estimatedCompletionTime,
|
||||
string gitHubOwner,
|
||||
string gitHubRepo,
|
||||
ulong channelId,
|
||||
bool localCommitPushed,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(revisionInformation);
|
||||
ArgumentNullException.ThrowIfNull(byondVersion);
|
||||
|
||||
@@ -6,13 +6,17 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Elasticsearch.Net.Specification.IndexLifecycleManagementApi;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Moq;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Api.Models.Request;
|
||||
using Tgstation.Server.Api.Models.Response;
|
||||
using Tgstation.Server.Client;
|
||||
@@ -32,16 +36,22 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
|
||||
readonly Api.Models.Instance metadata;
|
||||
|
||||
static Version edgeVersion;
|
||||
static Dictionary<EngineType, ByondVersion> edgeVersions = new Dictionary<EngineType, ByondVersion>
|
||||
{
|
||||
{ EngineType.Byond, null },
|
||||
{ EngineType.OpenDream, null }
|
||||
};
|
||||
|
||||
Version testVersion;
|
||||
ByondVersion testVersion;
|
||||
EngineType testEngine;
|
||||
|
||||
public ByondTest(IByondClient byondClient, IJobsClient jobsClient, IFileDownloader fileDownloader, Api.Models.Instance metadata)
|
||||
public ByondTest(IByondClient byondClient, IJobsClient jobsClient, IFileDownloader fileDownloader, Api.Models.Instance metadata, EngineType engineType)
|
||||
: base(jobsClient)
|
||||
{
|
||||
this.byondClient = byondClient ?? throw new ArgumentNullException(nameof(byondClient));
|
||||
this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader));
|
||||
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
|
||||
this.testEngine = engineType;
|
||||
}
|
||||
|
||||
public Task Run(CancellationToken cancellationToken, out Task firstInstall)
|
||||
@@ -50,42 +60,67 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
return RunContinued(firstInstall, cancellationToken);
|
||||
}
|
||||
|
||||
public static async Task<Version> GetEdgeVersion(IFileDownloader fileDownloader, CancellationToken cancellationToken)
|
||||
public static async ValueTask<ByondVersion> GetEdgeVersion(EngineType engineType, IFileDownloader fileDownloader, CancellationToken cancellationToken)
|
||||
{
|
||||
var edgeVersion = edgeVersions[engineType];
|
||||
|
||||
if (edgeVersion != null)
|
||||
return edgeVersion;
|
||||
|
||||
await using var provider = fileDownloader.DownloadFile(new Uri("https://www.byond.com/download/version.txt"), null);
|
||||
var stream = await provider.GetResult(cancellationToken);
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8, false, -1, true);
|
||||
var text = await reader.ReadToEndAsync();
|
||||
var splits = text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
ByondVersion byondVersion;
|
||||
if (engineType == EngineType.Byond)
|
||||
{
|
||||
await using var provider = fileDownloader.DownloadFile(new Uri("https://www.byond.com/download/version.txt"), null);
|
||||
var stream = await provider.GetResult(cancellationToken);
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8, false, -1, true);
|
||||
var text = await reader.ReadToEndAsync(cancellationToken);
|
||||
var splits = text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var targetVersion = splits.Last();
|
||||
var targetVersion = splits.Last();
|
||||
|
||||
var missingVersionMap = new PlatformIdentifier().IsWindows
|
||||
? new Dictionary<string, string>()
|
||||
{
|
||||
}
|
||||
// linux map also needs updating in CI
|
||||
: new Dictionary<string, string>()
|
||||
{
|
||||
var missingVersionMap = new PlatformIdentifier().IsWindows
|
||||
? new Dictionary<string, string>()
|
||||
{
|
||||
}
|
||||
// linux map also needs updating in CI
|
||||
: new Dictionary<string, string>()
|
||||
{
|
||||
{ "515.1612", "515.1611" }
|
||||
};
|
||||
};
|
||||
|
||||
if (missingVersionMap.TryGetValue(targetVersion, out var remappedVersion))
|
||||
targetVersion = remappedVersion;
|
||||
if (missingVersionMap.TryGetValue(targetVersion, out var remappedVersion))
|
||||
targetVersion = remappedVersion;
|
||||
|
||||
return edgeVersion = Version.Parse(targetVersion);
|
||||
Assert.IsTrue(ByondVersion.TryParse(targetVersion, out byondVersion), $"Bad version: {targetVersion}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail($"Edge version retrieval for {engineType} not implemented!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return edgeVersions[engineType] = byondVersion;
|
||||
}
|
||||
|
||||
async Task RunPartOne(CancellationToken cancellationToken)
|
||||
{
|
||||
testVersion = await GetEdgeVersion(fileDownloader, cancellationToken);
|
||||
testVersion = await GetEdgeVersion(EngineType.Byond, fileDownloader, cancellationToken);
|
||||
await TestNoVersion(cancellationToken);
|
||||
await TestInstallNullVersion(cancellationToken);
|
||||
await TestInstallStable(cancellationToken);
|
||||
}
|
||||
|
||||
ValueTask TestInstallNullVersion(CancellationToken cancellationToken)
|
||||
=> ApiAssert.ThrowsException<ApiConflictException, ByondInstallResponse>(
|
||||
() => byondClient.SetActiveVersion(
|
||||
new ByondVersionRequest
|
||||
{
|
||||
Engine = testEngine,
|
||||
},
|
||||
null,
|
||||
cancellationToken),
|
||||
ErrorCode.ModelValidationFailure);
|
||||
|
||||
async Task RunContinued(Task firstInstall, CancellationToken cancellationToken)
|
||||
{
|
||||
await firstInstall;
|
||||
@@ -98,7 +133,8 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
{
|
||||
var deleteThisOneBecauseItWasntPartOfTheOriginalTest = await byondClient.DeleteVersion(new ByondVersionDeleteRequest
|
||||
{
|
||||
Version = new(testVersion.Major, testVersion.Minor, 2)
|
||||
Engine = testEngine,
|
||||
Version = new(testVersion.Version.Major, testVersion.Version.Minor, 2)
|
||||
}, cancellationToken);
|
||||
await WaitForJob(deleteThisOneBecauseItWasntPartOfTheOriginalTest, 30, false, null, cancellationToken);
|
||||
|
||||
@@ -112,14 +148,18 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
var uninstallResponseTask = byondClient.DeleteVersion(
|
||||
new ByondVersionDeleteRequest
|
||||
{
|
||||
Version = testVersion
|
||||
Version = testVersion.Version,
|
||||
Engine = testVersion.Engine,
|
||||
SourceCommittish = testVersion.SourceCommittish,
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
var badBecauseActiveResponseTask = ApiAssert.ThrowsException<ConflictException, JobResponse>(() => byondClient.DeleteVersion(
|
||||
new ByondVersionDeleteRequest
|
||||
{
|
||||
Version = new(testVersion.Major, testVersion.Minor, 1)
|
||||
Version = new(testVersion.Version.Major, testVersion.Version.Minor, 1),
|
||||
Engine = testVersion.Engine,
|
||||
SourceCommittish = testVersion.SourceCommittish,
|
||||
},
|
||||
cancellationToken), ErrorCode.ByondCannotDeleteActiveVersion);
|
||||
|
||||
@@ -140,7 +180,7 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
var newVersions = await byondClient.InstalledVersions(null, cancellationToken);
|
||||
Assert.IsNotNull(newVersions);
|
||||
Assert.AreEqual(1, newVersions.Count);
|
||||
Assert.AreEqual(new Version(testVersion.Major, testVersion.Minor, 1), newVersions[0].Version);
|
||||
Assert.AreEqual(new Version(testVersion.Version.Major, testVersion.Version.Minor, 1), newVersions[0].Version);
|
||||
}
|
||||
|
||||
async Task TestInstallFakeVersion(CancellationToken cancellationToken)
|
||||
@@ -158,7 +198,9 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
{
|
||||
var newModel = new ByondVersionRequest
|
||||
{
|
||||
Version = testVersion
|
||||
Version = testVersion.Version,
|
||||
Engine = testVersion.Engine,
|
||||
SourceCommittish = testVersion.SourceCommittish,
|
||||
};
|
||||
var test = await byondClient.SetActiveVersion(newModel, null, cancellationToken);
|
||||
Assert.IsNotNull(test.InstallJob);
|
||||
@@ -220,7 +262,9 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
var test = await byondClient.SetActiveVersion(
|
||||
new ByondVersionRequest
|
||||
{
|
||||
Version = testVersion,
|
||||
Engine = testVersion.Engine,
|
||||
Version = testVersion.Version,
|
||||
SourceCommittish = testVersion.SourceCommittish,
|
||||
UploadCustomZip = true
|
||||
},
|
||||
stableBytesMs,
|
||||
@@ -234,7 +278,9 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
var test2 = await byondClient.SetActiveVersion(
|
||||
new ByondVersionRequest
|
||||
{
|
||||
Version = testVersion,
|
||||
Version = testVersion.Version,
|
||||
SourceCommittish = testVersion.SourceCommittish,
|
||||
Engine = testVersion.Engine,
|
||||
UploadCustomZip = true
|
||||
},
|
||||
stableBytesMs,
|
||||
@@ -244,22 +290,24 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
await WaitForJob(test2.InstallJob, 30, false, null, cancellationToken);
|
||||
|
||||
var newSettings = await byondClient.ActiveVersion(cancellationToken);
|
||||
Assert.AreEqual(new Version(testVersion.Major, testVersion.Minor, 2), newSettings.Version);
|
||||
Assert.AreEqual(new Version(testVersion.Version.Major, testVersion.Version.Minor, 2), newSettings.Version);
|
||||
|
||||
// test a few switches
|
||||
var installResponse = await byondClient.SetActiveVersion(new ByondVersionRequest
|
||||
{
|
||||
Version = testVersion
|
||||
Version = testVersion.Version,
|
||||
SourceCommittish = testVersion.SourceCommittish,
|
||||
Engine = testVersion.Engine,
|
||||
}, null, cancellationToken);
|
||||
Assert.IsNull(installResponse.InstallJob);
|
||||
await ApiAssert.ThrowsException<ApiConflictException, ByondInstallResponse>(() => byondClient.SetActiveVersion(new ByondVersionRequest
|
||||
{
|
||||
Version = new Version(testVersion.Major, testVersion.Minor, 3)
|
||||
Version = new Version(testVersion.Version.Major, testVersion.Version.Minor, 3)
|
||||
}, null, cancellationToken), ErrorCode.ByondNonExistentCustomVersion);
|
||||
|
||||
installResponse = await byondClient.SetActiveVersion(new ByondVersionRequest
|
||||
{
|
||||
Version = new Version(testVersion.Major, testVersion.Minor, 1)
|
||||
Version = new Version(testVersion.Version.Major, testVersion.Version.Minor, 1)
|
||||
}, null, cancellationToken);
|
||||
Assert.IsNull(installResponse.InstallJob);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Api.Models.Request;
|
||||
using Tgstation.Server.Api.Models.Response;
|
||||
using Tgstation.Server.Client;
|
||||
@@ -43,9 +44,10 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
ushort ddPort,
|
||||
bool highPrioDD,
|
||||
bool lowPrioDeployment,
|
||||
EngineType engineType,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs, fileDownloader, instanceClient.Metadata);
|
||||
var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs, fileDownloader, instanceClient.Metadata, engineType);
|
||||
var chatTest = new ChatTest(instanceClient.ChatBots, instanceManagerClient, instanceClient.Jobs, instanceClient.Metadata);
|
||||
var configTest = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata);
|
||||
var repoTest = new RepositoryTest(instanceClient.Repository, instanceClient.Jobs);
|
||||
@@ -66,17 +68,23 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
await byondTask;
|
||||
|
||||
await new WatchdogTest(
|
||||
await ByondTest.GetEdgeVersion(fileDownloader, cancellationToken), instanceClient, instanceManager, serverPort, highPrioDD, ddPort).Run(cancellationToken);
|
||||
await ByondTest.GetEdgeVersion(engineType, fileDownloader, cancellationToken), instanceClient, instanceManager, serverPort, highPrioDD, ddPort).Run(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task RunCompatTests(
|
||||
Version compatVersion,
|
||||
Version compatByondVersion,
|
||||
IInstanceClient instanceClient,
|
||||
ushort dmPort,
|
||||
ushort ddPort,
|
||||
bool highPrioDD,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var compatVersion = new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = compatByondVersion,
|
||||
};
|
||||
|
||||
System.Console.WriteLine($"COMPAT TEST START: {compatVersion}");
|
||||
const string Origin = "https://github.com/Cyberboss/common_core";
|
||||
var cloneRequest = instanceClient.Repository.Clone(new RepositoryCreateRequest
|
||||
@@ -155,7 +163,9 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
installJob2 = await instanceClient.Byond.SetActiveVersion(new ByondVersionRequest
|
||||
{
|
||||
UploadCustomZip = true,
|
||||
Version = compatVersion,
|
||||
Version = compatVersion.Version,
|
||||
Engine = compatVersion.Engine,
|
||||
SourceCommittish = compatVersion.SourceCommittish
|
||||
}, stableBytesMs, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Api.Models.Request;
|
||||
using Tgstation.Server.Api.Models.Response;
|
||||
using Tgstation.Server.Client;
|
||||
@@ -57,11 +58,11 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
readonly ushort ddPort;
|
||||
readonly bool highPrioDD;
|
||||
readonly TopicClient topicClient;
|
||||
readonly Version testVersion;
|
||||
readonly ByondVersion testVersion;
|
||||
|
||||
bool ranTimeoutTest = false;
|
||||
|
||||
public WatchdogTest(Version testVersion, IInstanceClient instanceClient, InstanceManager instanceManager, ushort serverPort, bool highPrioDD, ushort ddPort)
|
||||
public WatchdogTest(ByondVersion testVersion, IInstanceClient instanceClient, InstanceManager instanceManager, ushort serverPort, bool highPrioDD, ushort ddPort)
|
||||
: base(instanceClient.Jobs)
|
||||
{
|
||||
this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient));
|
||||
@@ -94,8 +95,9 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
var byondVersion = list[0];
|
||||
|
||||
Assert.AreEqual(1, byondVersion.Version.Build);
|
||||
Assert.AreEqual(testVersion.Major, byondVersion.Version.Major);
|
||||
Assert.AreEqual(testVersion.Minor, byondVersion.Version.Minor);
|
||||
Assert.AreEqual(testVersion.Version.Major, byondVersion.Version.Major);
|
||||
Assert.AreEqual(testVersion.Version.Minor, byondVersion.Version.Minor);
|
||||
Assert.AreEqual(testVersion.Engine, byondVersion.Engine);
|
||||
}
|
||||
|
||||
await Task.WhenAll(
|
||||
@@ -230,10 +232,10 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
|
||||
async Task<JobResponse> TestDeleteByondInstallErrorCasesAndQueing(CancellationToken cancellationToken)
|
||||
{
|
||||
var testCustomVersion = new Version(testVersion.Major, testVersion.Minor, 1);
|
||||
var testCustomVersion = new Version(testVersion.Version.Major, testVersion.Version.Minor, 1);
|
||||
var currentByond = await instanceClient.Byond.ActiveVersion(cancellationToken);
|
||||
Assert.IsNotNull(currentByond);
|
||||
Assert.AreEqual(testVersion.Semver(), currentByond.Version);
|
||||
Assert.AreEqual(testVersion.Version.Semver(), currentByond.Version);
|
||||
|
||||
// Change the active version and check we get delayed while deleting the old one because the watchdog is using it
|
||||
var setActiveResponse = await instanceClient.Byond.SetActiveVersion(
|
||||
@@ -250,7 +252,8 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
var deleteJob = await instanceClient.Byond.DeleteVersion(
|
||||
new ByondVersionDeleteRequest
|
||||
{
|
||||
Version = testVersion,
|
||||
Version = testVersion.Version,
|
||||
SourceCommittish = testVersion.SourceCommittish,
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
@@ -265,7 +268,9 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
setActiveResponse = await instanceClient.Byond.SetActiveVersion(
|
||||
new ByondVersionRequest
|
||||
{
|
||||
Version = testVersion,
|
||||
Version = testVersion.Version,
|
||||
Engine = testVersion.Engine,
|
||||
SourceCommittish = testVersion.SourceCommittish
|
||||
},
|
||||
null,
|
||||
cancellationToken);
|
||||
@@ -291,7 +296,9 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
deleteJob = await instanceClient.Byond.DeleteVersion(
|
||||
new ByondVersionDeleteRequest
|
||||
{
|
||||
Version = testVersion,
|
||||
Version = testVersion.Version,
|
||||
Engine = testVersion.Engine,
|
||||
SourceCommittish = testVersion.SourceCommittish,
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
@@ -954,9 +961,8 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
System.Console.WriteLine("TEST: WATCHDOG BYOND VERSION UPDATE TEST");
|
||||
var versionToInstall = testVersion;
|
||||
|
||||
versionToInstall = versionToInstall.Semver();
|
||||
var currentByondVersion = await instanceClient.Byond.ActiveVersion(cancellationToken);
|
||||
Assert.AreNotEqual(versionToInstall, currentByondVersion.Version);
|
||||
Assert.AreNotEqual(versionToInstall, currentByondVersion);
|
||||
|
||||
var initialStatus = await instanceClient.DreamDaemon.Read(cancellationToken);
|
||||
|
||||
@@ -969,7 +975,9 @@ namespace Tgstation.Server.Tests.Live.Instance
|
||||
var byondInstallJobTask = instanceClient.Byond.SetActiveVersion(
|
||||
new ByondVersionRequest
|
||||
{
|
||||
Version = versionToInstall
|
||||
Version = versionToInstall.Version,
|
||||
Engine = versionToInstall.Engine,
|
||||
SourceCommittish = versionToInstall.SourceCommittish,
|
||||
},
|
||||
null,
|
||||
cancellationToken);
|
||||
|
||||
@@ -28,6 +28,7 @@ using Npgsql;
|
||||
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Api.Models.Request;
|
||||
using Tgstation.Server.Api.Models.Response;
|
||||
using Tgstation.Server.Api.Rights;
|
||||
@@ -955,7 +956,12 @@ namespace Tgstation.Server.Tests.Live
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestStandardTgsOperation()
|
||||
public Task TestStandardTgsOperation() => TestStandardTgsOperation(EngineType.Byond);
|
||||
|
||||
[TestMethod]
|
||||
public Task TestOpenDreamTgsOperation() => TestStandardTgsOperation(EngineType.OpenDream);
|
||||
|
||||
async Task TestStandardTgsOperation(EngineType engineType)
|
||||
{
|
||||
using(var currentProcess = System.Diagnostics.Process.GetCurrentProcess())
|
||||
{
|
||||
@@ -980,7 +986,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
ServiceCollectionExtensions.UseAdditionalLoggerProvider<HardFailLoggerProvider>();
|
||||
|
||||
var failureTask = HardFailLoggerProvider.FailureSource;
|
||||
var internalTask = TestTgsInternal(hardCancellationToken);
|
||||
var internalTask = TestTgsInternal(engineType, hardCancellationToken);
|
||||
await Task.WhenAny(
|
||||
internalTask,
|
||||
failureTask);
|
||||
@@ -1012,7 +1018,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
await internalTask;
|
||||
}
|
||||
|
||||
async Task TestTgsInternal(CancellationToken hardCancellationToken)
|
||||
async Task TestTgsInternal(EngineType engineType, CancellationToken hardCancellationToken)
|
||||
{
|
||||
var discordConnectionString = Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_TOKEN");
|
||||
var ircConnectionString = Environment.GetEnvironmentVariable("TGS_TEST_IRC_CONNECTION_STRING");
|
||||
@@ -1118,7 +1124,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
async Task RunInstanceTests()
|
||||
{
|
||||
// Some earlier linux BYOND versions have a critical bug where replacing the directory in non-basic watchdogs causes the DreamDaemon cwd to change
|
||||
var canRunCompatTests = new PlatformIdentifier().IsWindows;
|
||||
var canRunCompatTests = engineType == EngineType.Byond && new PlatformIdentifier().IsWindows;
|
||||
var compatTests = canRunCompatTests
|
||||
? FailFast(
|
||||
instanceTest
|
||||
@@ -1142,6 +1148,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
mainDDPort,
|
||||
server.HighPriorityDreamDaemon,
|
||||
server.LowPriorityDeployments,
|
||||
engineType,
|
||||
cancellationToken));
|
||||
|
||||
await compatTests;
|
||||
@@ -1308,7 +1315,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
preStartupTime = DateTimeOffset.UtcNow;
|
||||
serverTask = server.Run(cancellationToken).AsTask();
|
||||
long expectedCompileJobId, expectedStaged;
|
||||
var edgeByond = await ByondTest.GetEdgeVersion(fileDownloader, cancellationToken);
|
||||
var edgeVersion = await ByondTest.GetEdgeVersion(engineType, fileDownloader, cancellationToken);
|
||||
using (var adminClient = await CreateAdminClient(server.Url, cancellationToken))
|
||||
{
|
||||
var instanceClient = adminClient.Instances.CreateClient(instance);
|
||||
@@ -1319,7 +1326,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
Assert.AreEqual(WatchdogStatus.Online, dd.Status.Value);
|
||||
|
||||
var compileJob = await instanceClient.DreamMaker.Compile(cancellationToken);
|
||||
var wdt = new WatchdogTest(edgeByond, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort);
|
||||
var wdt = new WatchdogTest(edgeVersion, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort);
|
||||
await wdt.WaitForJob(compileJob, 30, false, null, cancellationToken);
|
||||
|
||||
dd = await instanceClient.DreamDaemon.Read(cancellationToken);
|
||||
@@ -1366,7 +1373,7 @@ namespace Tgstation.Server.Tests.Live
|
||||
Assert.AreEqual(WatchdogStatus.Online, currentDD.Status);
|
||||
Assert.AreEqual(expectedStaged, currentDD.StagedCompileJob.Job.Id.Value);
|
||||
|
||||
var wdt = new WatchdogTest(edgeByond, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort);
|
||||
var wdt = new WatchdogTest(edgeVersion, instanceClient, GetInstanceManager(), (ushort)server.Url.Port, server.HighPriorityDreamDaemon, mainDDPort);
|
||||
currentDD = await wdt.TellWorldToReboot(cancellationToken);
|
||||
Assert.AreEqual(expectedStaged, currentDD.ActiveCompileJob.Job.Id.Value);
|
||||
Assert.IsNull(currentDD.StagedCompileJob);
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace Tgstation.Server.Tests
|
||||
{
|
||||
new Host.Models.InstancePermissionSet
|
||||
{
|
||||
ByondRights = ByondRights.InstallCustomVersion,
|
||||
ByondRights = ByondRights.InstallCustomByondVersion,
|
||||
ChatBotRights = ChatBotRights.None,
|
||||
ConfigurationRights = ConfigurationRights.Read,
|
||||
DreamDaemonRights = DreamDaemonRights.ReadRevision,
|
||||
|
||||
@@ -28,6 +28,8 @@ using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.System;
|
||||
using System.Net;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Tests
|
||||
{
|
||||
@@ -125,12 +127,18 @@ namespace Tgstation.Server.Tests
|
||||
|
||||
const string ArchiveEntryPath = "byond/bin/dd.exe";
|
||||
var hasEntry = ArchiveHasFileEntry(
|
||||
await byondInstaller.DownloadVersion(WindowsByondInstaller.DDExeVersion, default),
|
||||
await byondInstaller.DownloadVersion(
|
||||
new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = WindowsByondInstaller.DDExeVersion
|
||||
},
|
||||
default),
|
||||
ArchiveEntryPath);
|
||||
|
||||
Assert.IsTrue(hasEntry);
|
||||
|
||||
var (byondBytes, version) = await GetByondVersionPriorTo(byondInstaller, WindowsByondInstaller.DDExeVersion);
|
||||
var (byondBytes, _) = await GetByondVersionPriorTo(byondInstaller, WindowsByondInstaller.DDExeVersion);
|
||||
hasEntry = ArchiveHasFileEntry(
|
||||
byondBytes,
|
||||
ArchiveEntryPath);
|
||||
@@ -203,8 +211,18 @@ namespace Tgstation.Server.Tests
|
||||
try
|
||||
{
|
||||
await TestMapThreadsVersion(
|
||||
ByondInstallerBase.MapThreadsVersion,
|
||||
await byondInstaller.DownloadVersion(ByondInstallerBase.MapThreadsVersion, default),
|
||||
new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = ByondInstallerBase.MapThreadsVersion,
|
||||
},
|
||||
await byondInstaller.DownloadVersion(
|
||||
new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = ByondInstallerBase.MapThreadsVersion
|
||||
},
|
||||
default),
|
||||
byondInstaller,
|
||||
ioManager,
|
||||
processExecutor,
|
||||
@@ -380,22 +398,32 @@ namespace Tgstation.Server.Tests
|
||||
Assert.AreEqual(latestMigrationSL, DatabaseContext.SLLatestMigration);
|
||||
}
|
||||
|
||||
static async Task<Tuple<MemoryStream, Version>> GetByondVersionPriorTo(IByondInstaller byondInstaller, Version version)
|
||||
static async Task<Tuple<MemoryStream, ByondVersion>> GetByondVersionPriorTo(IByondInstaller byondInstaller, Version version)
|
||||
{
|
||||
var minusOneMinor = new Version(version.Major, version.Minor - 1);
|
||||
var byondVersion = new ByondVersion
|
||||
{
|
||||
Engine = EngineType.Byond,
|
||||
Version = minusOneMinor
|
||||
};
|
||||
try
|
||||
{
|
||||
return Tuple.Create(await byondInstaller.DownloadVersion(minusOneMinor, default), minusOneMinor);
|
||||
return Tuple.Create(await byondInstaller.DownloadVersion(
|
||||
byondVersion,
|
||||
CancellationToken.None), byondVersion);
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
var minusOneMajor = new Version(minusOneMinor.Major - 1, minusOneMinor.Minor);
|
||||
return Tuple.Create(await byondInstaller.DownloadVersion(minusOneMajor, default), minusOneMajor);
|
||||
byondVersion.Version = minusOneMajor;
|
||||
return Tuple.Create(await byondInstaller.DownloadVersion(
|
||||
byondVersion,
|
||||
CancellationToken.None), byondVersion);
|
||||
}
|
||||
}
|
||||
|
||||
static async Task TestMapThreadsVersion(
|
||||
Version byondVersion,
|
||||
ByondVersion byondVersion,
|
||||
Stream byondBytes,
|
||||
IByondInstaller byondInstaller,
|
||||
IIOManager ioManager,
|
||||
|
||||
Reference in New Issue
Block a user