From 2869eaf8983d5fc3478c209e8245c1a6ec296b0e Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Wed, 27 Nov 2024 20:23:00 -0500 Subject: [PATCH] Fix server/compiler paths being incorrect for custom OD installations --- .../Components/Engine/EngineManager.cs | 162 ++++++++++-------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Engine/EngineManager.cs b/src/Tgstation.Server.Host/Components/Engine/EngineManager.cs index 76885286cd..eeadebc71f 100644 --- a/src/Tgstation.Server.Host/Components/Engine/EngineManager.cs +++ b/src/Tgstation.Server.Host/Components/Engine/EngineManager.cs @@ -412,110 +412,120 @@ async ValueTask AssertAndLockVersion( EngineExecutableLock installLock; bool installedOrInstalling; - var potentialInstallation = await engineInstaller.CreateInstallation( - version, - ioManager.ResolvePath(version.ToString()), - ourTcs.Task, - cancellationToken); - - 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 installationContainer; - if (!installedOrInstalling) - { - if (!allowInstallation) - throw new InvalidOperationException($"Engine version {version} not installed!"); - - installationContainer = AddInstallationContainer(potentialInstallation); - } - 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 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); - var versionString = version.ToString(); - await eventConsumer.HandleEvent(EventType.EngineInstallStart, new List { versionString }, deploymentPipelineProcesses, cancellationToken); + 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); - installPath = await InstallVersionFiles(progressReporter, version, customVersionStream, deploymentPipelineProcesses, cancellationToken); - await eventConsumer.HandleEvent(EventType.EngineInstallComplete, new List { versionString }, deploymentPipelineProcesses, cancellationToken); + progressReporter.StageName = "Running event"; - ourTcs.SetResult(); - } - catch (Exception ex) - { - if (installPath != null) + var versionString = version.ToString(); + await eventConsumer.HandleEvent(EventType.EngineInstallStart, new List { versionString }, deploymentPipelineProcesses, cancellationToken); + + installPath = await InstallVersionFiles(progressReporter, version, customVersionStream, deploymentPipelineProcesses, cancellationToken); + await eventConsumer.HandleEvent(EventType.EngineInstallComplete, new List { 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 { ex.Message }, deploymentPipelineProcesses, cancellationToken); + else if (ex is not OperationCanceledException) + await eventConsumer.HandleEvent(EventType.EngineInstallFail, new List { ex.Message }, deploymentPipelineProcesses, cancellationToken); - lock (installedVersions) - installedVersions.Remove(version); + lock (installedVersions) + installedVersions.Remove(version); + + ourTcs.SetException(ex); + throw; + } - ourTcs.SetException(ex); + return installLock; + } + catch + { + installLock.Dispose(); throw; } - - return installLock; - } - catch - { - installLock.Dispose(); - throw; } }