Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OD with Nix #2032

Merged
merged 11 commits into from
Nov 30, 2024
8 changes: 5 additions & 3 deletions build/package/nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,13 @@ stdenv.mkDerivation {
};

buildInputs = with pkgs; [
dotnetCorePackages.aspnetcore_8_0
dotnetCorePackages.sdk_8_0
gdb
systemd
zlib
gcc_multi
glibc
bash
];
nativeBuildInputs = with pkgs; [
makeWrapper
Expand All @@ -98,12 +99,13 @@ stdenv.mkDerivation {
mkdir -p $out/bin
unzip "${fixedOutput}/ServerConsole.zip" -d $out/bin
rm -rf $out/bin/lib
makeWrapper ${pkgs.dotnetCorePackages.aspnetcore_8_0}/dotnet $out/bin/tgstation-server --suffix PATH : ${
makeWrapper ${pkgs.dotnetCorePackages.sdk_8_0}/dotnet $out/bin/tgstation-server --suffix PATH : ${
lib.makeBinPath (
with pkgs;
[
dotnetCorePackages.aspnetcore_8_0
dotnetCorePackages.sdk_8_0
gdb
bash
]
)
} --suffix LD_LIBRARY_PATH : ${
Expand Down
11 changes: 11 additions & 0 deletions build/package/nix/tgstation-server.nix
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ let
];

byond-patcher = pkgs-i686.writeShellScriptBin "EngineInstallComplete-050-TgsPatchELFByond.sh" ''
# If the file doesn't exist, assume OD
if [[ ! -f ../../Byond/$1/byond/bin/DreamDaemon ]] ; then
echo "DreamDaemon doesn't appear to exist. Assuming OD install"
exit
fi

BYOND_PATH=$(realpath ../../Byond/$1/byond/bin/)

${pkgs.patchelf}/bin/patchelf --set-interpreter "$(cat ${stdenv.cc}/nix-support/dynamic-linker)" \
Expand Down Expand Up @@ -109,6 +115,11 @@ in
group = cfg.groupname;
mode = "0640";
};
"tgstation-server.d/EventScripts/README.txt" = {
text = "TGS event scripts placed here will be executed by all online instances";
group = cfg.groupname;
mode = "0640";
};
};

systemd.services.tgstation-server = {
Expand Down
35 changes: 18 additions & 17 deletions src/Tgstation.Server.Host/Components/Engine/ByondInstallerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,29 +74,30 @@ protected ByondInstallerBase(IIOManager ioManager, ILogger<ByondInstallerBase> l
}

/// <inheritdoc />
public override IEngineInstallation CreateInstallation(EngineVersion version, string path, Task installationTask)
public override ValueTask<IEngineInstallation> CreateInstallation(EngineVersion version, string path, Task installationTask, CancellationToken cancellationToken)
{
CheckVersionValidity(version);

var installationIOManager = new ResolvingIOManager(IOManager, path);
var supportsMapThreads = version.Version >= MapThreadsVersion;

return new ByondInstallation(
installationIOManager,
installationTask,
version,
installationIOManager.ResolvePath(
installationIOManager.ConcatPath(
ByondBinPath,
GetDreamDaemonName(
version.Version!,
out var supportsCli))),
installationIOManager.ResolvePath(
installationIOManager.ConcatPath(
ByondBinPath,
DreamMakerName)),
supportsCli,
supportsMapThreads);
return ValueTask.FromResult<IEngineInstallation>(
new ByondInstallation(
installationIOManager,
installationTask,
version,
installationIOManager.ResolvePath(
installationIOManager.ConcatPath(
ByondBinPath,
GetDreamDaemonName(
version.Version!,
out var supportsCli))),
installationIOManager.ResolvePath(
installationIOManager.ConcatPath(
ByondBinPath,
DreamMakerName)),
supportsCli,
supportsMapThreads));
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public Task CleanCache(CancellationToken cancellationToken)
=> Task.WhenAll(delegatedInstallers.Values.Select(installer => installer.CleanCache(cancellationToken)));

/// <inheritdoc />
public IEngineInstallation CreateInstallation(EngineVersion version, string path, Task installationTask)
=> DelegateCall(version, installer => installer.CreateInstallation(version, path, installationTask));
public ValueTask<IEngineInstallation> CreateInstallation(EngineVersion version, string path, Task installationTask, CancellationToken cancellationToken)
=> DelegateCall(version, installer => installer.CreateInstallation(version, path, installationTask, cancellationToken));

/// <inheritdoc />
public ValueTask<IEngineInstallationData> DownloadVersion(EngineVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected EngineInstallerBase(IIOManager ioManager, ILogger<EngineInstallerBase>
}

/// <inheritdoc />
public abstract IEngineInstallation CreateInstallation(EngineVersion version, string path, Task installationTask);
public abstract ValueTask<IEngineInstallation> CreateInstallation(EngineVersion version, string path, Task installationTask, CancellationToken cancellationToken);

/// <inheritdoc />
public abstract Task CleanCache(CancellationToken cancellationToken);
Expand Down
175 changes: 93 additions & 82 deletions src/Tgstation.Server.Host/Components/Engine/EngineManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ async ValueTask ReadVersion(string path)

try
{
AddInstallationContainer(version, path, Task.CompletedTask);
var installation = await engineInstaller.CreateInstallation(version, path, Task.CompletedTask, cancellationToken);
AddInstallationContainer(installation);
logger.LogDebug("Added detected BYOND version {versionKey}...", version);
}
catch (Exception ex)
Expand Down Expand Up @@ -410,107 +411,121 @@ async ValueTask<EngineExecutableLock> AssertAndLockVersion(
IEngineInstallation installation;
EngineExecutableLock installLock;
bool installedOrInstalling;
lock (installedVersions)

// loop is because of the race condition with potentialInstallation, installedVersions, and CustomIteration selection
while (true)
{
if (customVersionStream != null)
lock (installedVersions)
{
var customInstallationNumber = 1;
do
if (customVersionStream != null)
{
version.CustomIteration = customInstallationNumber++;
var customInstallationNumber = 1;
do
{
version.CustomIteration = customInstallationNumber++;
}
while (installedVersions.ContainsKey(version));
}
while (installedVersions.ContainsKey(version));
}

installedOrInstalling = installedVersions.TryGetValue(version, out var installationContainerNullable);
ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock> installationContainer;
if (!installedOrInstalling)
{
if (!allowInstallation)
throw new InvalidOperationException($"Engine version {version} not installed!");

installationContainer = AddInstallationContainer(
version,
ioManager.ResolvePath(version.ToString()),
ourTcs.Task);
}
else
installationContainer = installationContainerNullable!;

installation = installationContainer.Instance;
installLock = installationContainer.AddReference();
}
var potentialInstallation = await engineInstaller.CreateInstallation(
version,
ioManager.ResolvePath(version.ToString()),
ourTcs.Task,
cancellationToken);

var deploymentPipelineProcesses = !neededForLock;
try
{
if (installedOrInstalling)
lock (installedVersions)
{
progressReporter.StageName = "Waiting for existing installation job...";
if (customVersionStream != null && installedVersions.ContainsKey(version))
continue;

if (neededForLock && !installation.InstallationTask.IsCompleted)
logger.LogWarning("The required engine version ({version}) is not readily available! We will have to wait for it to install.", version);
installedOrInstalling = installedVersions.TryGetValue(version, out var installationContainerNullable);
ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock> installationContainer;
if (!installedOrInstalling)
{
if (!allowInstallation)
throw new InvalidOperationException($"Engine version {version} not installed!");

await installation.InstallationTask.WaitAsync(cancellationToken);
return installLock;
installationContainer = AddInstallationContainer(potentialInstallation);
}
else
installationContainer = installationContainerNullable!;

installation = installationContainer.Instance;
installLock = installationContainer.AddReference();
}

// okay up to us to install it then
string? installPath = null;
var deploymentPipelineProcesses = !neededForLock;
try
{
if (customVersionStream != null)
logger.LogInformation("Installing custom engine version as {version}...", version);
else if (neededForLock)
if (installedOrInstalling)
{
if (version.CustomIteration.HasValue)
throw new JobException(ErrorCode.EngineNonExistentCustomVersion);
progressReporter.StageName = "Waiting for existing installation job...";

if (neededForLock && !installation.InstallationTask.IsCompleted)
logger.LogWarning("The required engine version ({version}) is not readily available! We will have to wait for it to install.", version);

logger.LogWarning("The required engine version ({version}) is not readily available! We will have to install it.", version);
await installation.InstallationTask.WaitAsync(cancellationToken);
return installLock;
}
else
logger.LogInformation("Requested engine version {version} not currently installed. Doing so now...", version);

progressReporter.StageName = "Running event";
// okay up to us to install it then
string? installPath = null;
try
{
if (customVersionStream != null)
logger.LogInformation("Installing custom engine version as {version}...", version);
else if (neededForLock)
{
if (version.CustomIteration.HasValue)
throw new JobException(ErrorCode.EngineNonExistentCustomVersion);

logger.LogWarning("The required engine version ({version}) is not readily available! We will have to install it.", version);
}
else
logger.LogInformation("Requested engine version {version} not currently installed. Doing so now...", version);

var versionString = version.ToString();
await eventConsumer.HandleEvent(EventType.EngineInstallStart, new List<string> { versionString }, deploymentPipelineProcesses, cancellationToken);
progressReporter.StageName = "Running event";

installPath = await InstallVersionFiles(progressReporter, version, customVersionStream, deploymentPipelineProcesses, cancellationToken);
await eventConsumer.HandleEvent(EventType.EngineInstallComplete, new List<string> { versionString }, deploymentPipelineProcesses, cancellationToken);
var versionString = version.ToString();
await eventConsumer.HandleEvent(EventType.EngineInstallStart, new List<string> { versionString }, deploymentPipelineProcesses, cancellationToken);

ourTcs.SetResult();
}
catch (Exception ex)
{
if (installPath != null)
installPath = await InstallVersionFiles(progressReporter, version, customVersionStream, deploymentPipelineProcesses, cancellationToken);
await eventConsumer.HandleEvent(EventType.EngineInstallComplete, new List<string> { versionString }, deploymentPipelineProcesses, cancellationToken);

ourTcs.SetResult();
}
catch (Exception ex)
{
try
if (installPath != null)
{
logger.LogDebug("Cleaning up failed installation at {path}...", installPath);
await ioManager.DeleteDirectory(installPath, cancellationToken);
}
catch (Exception ex2)
{
logger.LogError(ex2, "Error cleaning up failed installation!");
try
{
logger.LogDebug("Cleaning up failed installation at {path}...", installPath);
await ioManager.DeleteDirectory(installPath, cancellationToken);
}
catch (Exception ex2)
{
logger.LogError(ex2, "Error cleaning up failed installation!");
}
}
}
else if (ex is not OperationCanceledException)
await eventConsumer.HandleEvent(EventType.EngineInstallFail, new List<string> { ex.Message }, deploymentPipelineProcesses, cancellationToken);
else if (ex is not OperationCanceledException)
await eventConsumer.HandleEvent(EventType.EngineInstallFail, new List<string> { ex.Message }, deploymentPipelineProcesses, cancellationToken);

lock (installedVersions)
installedVersions.Remove(version);
lock (installedVersions)
installedVersions.Remove(version);

ourTcs.SetException(ex);
ourTcs.SetException(ex);
throw;
}

return installLock;
}
catch
{
installLock.Dispose();
throw;
}

return installLock;
}
catch
{
installLock.Dispose();
throw;
}
}

Expand Down Expand Up @@ -616,18 +631,14 @@ await ioManager.WriteAllBytes(
/// <summary>
/// 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="installPath">The path to the installation.</param>
/// <param name="installationTask">The <see cref="ValueTask"/> representing the installation process.</param>
/// <returns>The new <see cref="IEngineInstallation"/>.</returns>
ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock> AddInstallationContainer(EngineVersion version, string installPath, Task installationTask)
/// <param name="installation">The <see cref="IEngineInstallation"/> being added.</param>
/// <returns>A new <see cref="ReferenceCountingContainer{TWrapped, TReference}"/> for the <see cref="IEngineInstallation"/>/<see cref="EngineExecutableLock"/>.</returns>
ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock> AddInstallationContainer(IEngineInstallation installation)
{
var installation = engineInstaller.CreateInstallation(version, installPath, installationTask);

var installationContainer = new ReferenceCountingContainer<IEngineInstallation, EngineExecutableLock>(installation);

lock (installedVersions)
installedVersions.Add(version, installationContainer);
installedVersions.Add(installation.Version, installationContainer);

return installationContainer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ interface IEngineInstaller
/// <param name="version">The <see cref="EngineVersion"/> of the installation.</param>
/// <param name="path">The path to the installation.</param>
/// <param name="installationTask">The <see cref="Task"/> representing the installation process for the installation.</param>
/// <returns>The <see cref="IEngineInstallation"/>.</returns>
IEngineInstallation CreateInstallation(EngineVersion version, string path, Task installationTask);
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <see cref="IEngineInstallation"/>.</returns>
ValueTask<IEngineInstallation> CreateInstallation(EngineVersion version, string path, Task installationTask, CancellationToken cancellationToken);

/// <summary>
/// Download a given engine <paramref name="version"/>.
Expand Down
Loading
Loading