More OpenDream engine implementation

This commit is contained in:
Jordan Dominion
2023-10-11 23:35:52 -04:00
parent 98e8404618
commit 90d516cfb9
16 changed files with 354 additions and 107 deletions
@@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.Utils;
namespace Tgstation.Server.Host.Components.Engine
@@ -66,15 +67,21 @@ namespace Tgstation.Server.Host.Components.Engine
/// </summary>
protected abstract string ByondRevisionsUrlTemplate { get; }
/// <summary>
/// The <see cref="IFileDownloader"/> for the <see cref="ByondInstallerBase"/>.
/// </summary>
readonly IFileDownloader fileDownloader;
/// <summary>
/// Initializes a new instance of the <see cref="ByondInstallerBase"/> class.
/// </summary>
/// <param name="ioManager">The <see cref="IIOManager"/> for the <see cref="EngineInstallerBase"/>.</param>
/// <param name="fileDownloader">The <see cref="IFileDownloader"/> for the <see cref="EngineInstallerBase"/>.</param>
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="EngineInstallerBase"/>.</param>
protected ByondInstallerBase(IIOManager ioManager, IFileDownloader fileDownloader, ILogger<ByondInstallerBase> logger)
: base(ioManager, fileDownloader, logger)
/// <param name="fileDownloader">The value of <see cref="fileDownloader"/>.</param>
protected ByondInstallerBase(IIOManager ioManager, ILogger<ByondInstallerBase> logger, IFileDownloader fileDownloader)
: base(ioManager, logger)
{
this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader));
}
/// <inheritdoc />
@@ -184,11 +191,29 @@ namespace Tgstation.Server.Host.Components.Engine
}
/// <inheritdoc />
protected override ValueTask<Uri> GetDownloadZipUrl(ByondVersion version, CancellationToken cancellationToken)
public override async ValueTask<IEngineInstallationData> DownloadVersion(ByondVersion version, JobProgressReporter progressReporter, CancellationToken cancellationToken)
{
CheckVersionValidity(version);
var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsUrlTemplate, version.Version.Major, version.Version.Minor);
return ValueTask.FromResult(new Uri(url));
var url = await GetDownloadZipUrl(version, cancellationToken);
Logger.LogTrace("Downloading {engineType} version {version} from {url}...", TargetEngineType, version, url);
await using var download = fileDownloader.DownloadFile(url, null);
await using var buffer = new BufferedFileStreamProvider(
await download.GetResult(cancellationToken));
var stream = await buffer.GetOwnedResult(cancellationToken);
try
{
return new ZipStreamEngineInstallationData(
IOManager,
stream);
}
catch
{
await stream.DisposeAsync();
throw;
}
}
/// <summary>
@@ -198,5 +223,18 @@ namespace Tgstation.Server.Host.Components.Engine
/// <param name="supportsCli">Whether or not the returned path supports being run as a command-line application.</param>
/// <returns>The file name of the DreamDaemon executable.</returns>
protected abstract string GetDreamDaemonName(Version byondVersion, out bool supportsCli);
/// <summary>
/// Create a <see cref="Uri"/> pointing to the location of the download for a given <paramref name="version"/>.
/// </summary>
/// <param name="version">The <see cref="ByondVersion"/> to create a <see cref="Uri"/> for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a new <see cref="Uri"/> pointing to the version download location.</returns>
ValueTask<Uri> GetDownloadZipUrl(ByondVersion version, CancellationToken cancellationToken)
{
CheckVersionValidity(version);
var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsUrlTemplate, version.Version.Major, version.Version.Minor);
return ValueTask.FromResult(new Uri(url));
}
}
}
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -8,6 +7,7 @@ using Microsoft.Extensions.Logging;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
namespace Tgstation.Server.Host.Components.Engine
{
@@ -29,21 +29,14 @@ namespace Tgstation.Server.Host.Components.Engine
/// </summary>
protected ILogger<EngineInstallerBase> Logger { get; }
/// <summary>
/// The <see cref="IFileDownloader"/> for the <see cref="EngineInstallerBase"/>.
/// </summary>
readonly IFileDownloader fileDownloader;
/// <summary>
/// Initializes a new instance of the <see cref="EngineInstallerBase"/> class.
/// </summary>
/// <param name="ioManager">The value of <see cref="IOManager"/>.</param>
/// <param name="fileDownloader">The value of <see cref="fileDownloader"/>.</param>
/// <param name="logger">The value of <see cref="Logger"/>.</param>
protected EngineInstallerBase(IIOManager ioManager, IFileDownloader fileDownloader, ILogger<EngineInstallerBase> logger)
protected EngineInstallerBase(IIOManager ioManager, ILogger<EngineInstallerBase> logger)
{
IOManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.fileDownloader = fileDownloader ?? throw new ArgumentNullException(nameof(fileDownloader));
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@@ -60,30 +53,11 @@ namespace Tgstation.Server.Host.Components.Engine
public abstract ValueTask UpgradeInstallation(ByondVersion version, string path, CancellationToken cancellationToken);
/// <inheritdoc />
public async ValueTask<MemoryStream> DownloadVersion(ByondVersion version, CancellationToken cancellationToken)
{
CheckVersionValidity(version);
var url = await GetDownloadZipUrl(version, cancellationToken);
Logger.LogTrace("Downloading {engineType} version {version} from {url}...", TargetEngineType, version, url);
await using var download = fileDownloader.DownloadFile(url, null);
await using var buffer = new BufferedFileStreamProvider(
await download.GetResult(cancellationToken));
return await buffer.GetOwnedResult(cancellationToken);
}
public abstract ValueTask<IEngineInstallationData> DownloadVersion(ByondVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken);
/// <inheritdoc />
public abstract ValueTask TrustDmbPath(string fullDmbPath, CancellationToken cancellationToken);
/// <summary>
/// Create a <see cref="Uri"/> pointing to the location of the download for a given <paramref name="version"/>.
/// </summary>
/// <param name="version">The <see cref="ByondVersion"/> to create a <see cref="Uri"/> for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a new <see cref="Uri"/> pointing to the version download location.</returns>
protected abstract ValueTask<Uri> GetDownloadZipUrl(ByondVersion version, CancellationToken cancellationToken);
/// <summary>
/// Check that a given <paramref name="version"/> is of type <see cref="EngineType.Byond"/>.
/// </summary>
@@ -497,18 +497,24 @@ namespace Tgstation.Server.Host.Components.Engine
var directoryCleanupTask = DirectoryCleanup();
try
{
Stream versionZipStream;
IEngineInstallationData engineInstallationData;
if (customVersionStream == null)
{
if (progressReporter != null)
progressReporter.StageName = "Downloading version";
versionZipStream = await engineInstaller.DownloadVersion(version, cancellationToken);
engineInstallationData = await engineInstaller.DownloadVersion(version, progressReporter, cancellationToken);
progressReporter.ReportProgress(null);
}
else
versionZipStream = customVersionStream;
#pragma warning disable CA2000 // Dispose objects before losing scope, false positive
engineInstallationData = new ZipStreamEngineInstallationData(
ioManager,
customVersionStream);
#pragma warning restore CA2000 // Dispose objects before losing scope
await using (versionZipStream)
await using (engineInstallationData)
{
if (progressReporter != null)
progressReporter.StageName = "Cleaning target directory";
@@ -516,10 +522,10 @@ namespace Tgstation.Server.Host.Components.Engine
await directoryCleanupTask;
if (progressReporter != null)
progressReporter.StageName = "Extracting zip";
progressReporter.StageName = "Extracting data";
logger.LogTrace("Extracting downloaded BYOND zip to {extractPath}...", installFullPath);
await ioManager.ZipToDirectory(installFullPath, versionZipStream, cancellationToken);
logger.LogTrace("Extracting engine to {extractPath}...", installFullPath);
await engineInstallationData.ExtractToPath(installFullPath, cancellationToken);
}
if (progressReporter != null)
@@ -0,0 +1,20 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Tgstation.Server.Host.Components.Engine
{
/// <summary>
/// Wraps data containing an engine installation.
/// </summary>
interface IEngineInstallationData : IAsyncDisposable
{
/// <summary>
/// Extracts the installation to a given path.
/// </summary>
/// <param name="path">The full path to extract to.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
Task ExtractToPath(string path, CancellationToken cancellationToken);
}
}
@@ -1,8 +1,8 @@
using System.IO;
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Jobs;
namespace Tgstation.Server.Host.Components.Engine
{
@@ -22,10 +22,11 @@ namespace Tgstation.Server.Host.Components.Engine
/// <summary>
/// Download a given engine <paramref name="version"/>.
/// </summary>
/// <param name="version">The <see cref="ByondVersion"/> of BYOND to download.</param>
/// <param name="version">The <see cref="ByondVersion"/> of the engine to download.</param>
/// <param name="jobProgressReporter">The optional <see cref="JobProgressReporter"/> for the operation.</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(ByondVersion version, CancellationToken cancellationToken);
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the <see cref="IEngineInstallationData"/> for the download.</returns>
ValueTask<IEngineInstallationData> DownloadVersion(ByondVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken);
/// <summary>
/// Does actions necessary to get an extracted installation working.
@@ -4,14 +4,16 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Common;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.IO;
using Tgstation.Server.Host.Jobs;
using Tgstation.Server.Host.System;
using Tgstation.Server.Host.Utils.GitHub;
namespace Tgstation.Server.Host.Components.Engine
{
@@ -20,6 +22,11 @@ namespace Tgstation.Server.Host.Components.Engine
/// </summary>
sealed class OpenDreamInstaller : EngineInstallerBase
{
/// <summary>
/// The name of the subdirectory used for the <see cref="RepositoryEngineInstallationData"/>'s copy.
/// </summary>
private const string InstallationRepositorySubDirectory = "SourceRepo";
/// <inheritdoc />
protected override EngineType TargetEngineType => EngineType.OpenDream;
@@ -28,37 +35,43 @@ namespace Tgstation.Server.Host.Components.Engine
/// </summary>
readonly IPlatformIdentifier platformIdentifier;
/// <summary>
/// The <see cref="IGitHubService"/> for the <see cref="OpenDreamInstaller"/>.
/// </summary>
readonly IGitHubService gitHubService;
/// <summary>
/// The <see cref="IProcessExecutor"/> for the <see cref="OpenDreamInstaller"/>.
/// </summary>
readonly IProcessExecutor processExecutor;
/// <summary>
/// The <see cref="IRepositoryManager"/> for the OpenDream repository.
/// </summary>
readonly IRepositoryManager repositoryManager;
/// <summary>
/// The <see cref="GeneralConfiguration"/> for the <see cref="OpenDreamInstaller"/>.
/// </summary>
readonly GeneralConfiguration generalConfiguration;
/// <summary>
/// Initializes a new instance of the <see cref="OpenDreamInstaller"/> class.
/// </summary>
/// <param name="ioManager">The <see cref="IIOManager"/> for the <see cref="EngineInstallerBase"/>.</param>
/// <param name="fileDownloader">The <see cref="IFileDownloader"/> for the <see cref="EngineInstallerBase"/>.</param>
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="EngineInstallerBase"/>.</param>
/// <param name="platformIdentifier">The value of <see cref="platformIdentifier"/>.</param>
/// <param name="gitHubService">The value of <see cref="gitHubService"/>.</param>
/// <param name="processExecutor">The value of <see cref="processExecutor"/>.</param>
/// <param name="repositoryManager">The value of <see cref="repositoryManager"/>.</param>
/// <param name="generalConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing value of <see cref="generalConfiguration"/>.</param>
public OpenDreamInstaller(
IIOManager ioManager,
IFileDownloader fileDownloader,
ILogger<OpenDreamInstaller> logger,
IPlatformIdentifier platformIdentifier,
IGitHubService gitHubService,
IProcessExecutor processExecutor)
: base(ioManager, fileDownloader, logger)
IProcessExecutor processExecutor,
IRepositoryManager repositoryManager,
IOptions<GeneralConfiguration> generalConfigurationOptions)
: base(ioManager, logger)
{
this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
this.gitHubService = gitHubService ?? throw new ArgumentNullException(nameof(gitHubService));
this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
}
/// <inheritdoc />
@@ -72,10 +85,63 @@ namespace Tgstation.Server.Host.Components.Engine
}
/// <inheritdoc />
public override async ValueTask Install(ByondVersion version, string path, CancellationToken cancellationToken)
public override async ValueTask<IEngineInstallationData> DownloadVersion(ByondVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken)
{
CheckVersionValidity(version);
ArgumentNullException.ThrowIfNull(path);
// get a lock on a system wide OD repo
Logger.LogTrace("Cloning OD repo...");
var progressSection1 = jobProgressReporter.CreateSection("Updating OpenDream git repository", 0.5f);
var repo = await repositoryManager.CloneRepository(
generalConfiguration.OpenDreamGitUrl,
null,
null,
null,
progressSection1,
true,
cancellationToken);
try
{
if (repo == null)
{
Logger.LogTrace("OD repo seems to already exist, attempting load and fetch...");
repo = await repositoryManager.LoadRepository(cancellationToken);
await repo.FetchOrigin(
progressSection1,
null,
null,
false,
cancellationToken);
}
var progressSection2 = jobProgressReporter.CreateSection("Checking out OpenDream version", 0.5f);
await repo.CheckoutObject(
version.SourceCommittish,
null,
null,
true,
progressSection2,
cancellationToken);
return new RepositoryEngineInstallationData(IOManager, repo, InstallationRepositorySubDirectory);
}
catch
{
repo?.Dispose();
throw;
}
}
/// <inheritdoc />
public override async ValueTask Install(ByondVersion version, string installPath, CancellationToken cancellationToken)
{
CheckVersionValidity(version);
ArgumentNullException.ThrowIfNull(installPath);
var dotnetPaths = DotnetHelper.GetPotentialDotnetPaths(platformIdentifier.IsWindows)
.ToList();
@@ -92,8 +158,33 @@ namespace Tgstation.Server.Host.Components.Engine
var dotnetPath = dotnetPaths[selectedPathIndex];
await Task.Yield();
throw new NotImplementedException();
var repositoryPath = IOManager.ConcatPath(installPath, InstallationRepositorySubDirectory);
await using (var buildProcess = processExecutor.LaunchProcess(
dotnetPath,
repositoryPath,
"build -c Release",
null,
true,
true))
{
var buildExitCode = await buildProcess.Lifetime;
if (buildExitCode != 0)
throw new JobException("OpenDream build failed!");
}
const string BinDirectory = "bin";
await IOManager.MoveDirectory(
IOManager.ConcatPath(
repositoryPath,
BinDirectory,
"Content.Server"),
IOManager.ConcatPath(
installPath,
BinDirectory),
cancellationToken);
await IOManager.DeleteDirectory(repositoryPath, cancellationToken);
}
/// <inheritdoc />
@@ -110,21 +201,5 @@ namespace Tgstation.Server.Host.Components.Engine
ArgumentNullException.ThrowIfNull(fullDmbPath);
return ValueTask.CompletedTask;
}
/// <inheritdoc />
protected override async ValueTask<Uri> GetDownloadZipUrl(ByondVersion version, CancellationToken cancellationToken)
{
throw new NotImplementedException("This won't work because of the goddamn fucking robust toolbox submodule");
var fullCommit = await gitHubService.GetCommit("OpenDreamProject", "OpenDream", version.SourceCommittish, cancellationToken);
if (fullCommit.Sha != version.SourceCommittish)
{
Logger.LogInformation("Replacing committish {committish} with full SHA {sha}...", version.SourceCommittish, fullCommit.Sha);
version.SourceCommittish = fullCommit.Sha;
}
var gitHubDownloadUrlString = $"https://codeload.github.com/OpenDreamProject/OpenDream/zip/{version.SourceCommittish}";
return new Uri(gitHubDownloadUrlString);
}
}
}
@@ -58,7 +58,7 @@ namespace Tgstation.Server.Host.Components.Engine
IIOManager ioManager,
IFileDownloader fileDownloader,
ILogger<PosixByondInstaller> logger)
: base(ioManager, fileDownloader, logger)
: base(ioManager, logger, fileDownloader)
{
this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
@@ -0,0 +1,59 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components.Engine
{
/// <summary>
/// Implementation of <see cref="IEngineInstallationData"/> using a <see cref="IRepository"/>.
/// </summary>
sealed class RepositoryEngineInstallationData : IEngineInstallationData
{
/// <summary>
/// The <see cref="IIOManager"/> for the <see cref="RepositoryEngineInstallationData"/>.
/// </summary>
readonly IIOManager ioManager;
/// <summary>
/// The backing <see cref="IRepository"/>.
/// </summary>
readonly IRepository repository;
/// <summary>
/// The name of the subdirectory the <see cref="repository"/> is copied to.
/// </summary>
readonly string targetSubDirectory;
/// <summary>
/// Initializes a new instance of the <see cref="RepositoryEngineInstallationData"/> class.
/// </summary>
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
/// <param name="repository">The value of <see cref="repository"/>.</param>
/// <param name="targetSubDirectory">The value of <see cref="targetSubDirectory"/>.</param>
public RepositoryEngineInstallationData(IIOManager ioManager, IRepository repository, string targetSubDirectory)
{
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
this.targetSubDirectory = targetSubDirectory ?? throw new ArgumentNullException(nameof(targetSubDirectory));
}
/// <inheritdoc />
public ValueTask DisposeAsync()
{
repository.Dispose();
return ValueTask.CompletedTask;
}
/// <inheritdoc />
public Task ExtractToPath(string path, CancellationToken cancellationToken)
=> repository.CopyTo(
ioManager.ConcatPath(
path,
targetSubDirectory),
cancellationToken)
.AsTask();
}
}
@@ -96,7 +96,7 @@ namespace Tgstation.Server.Host.Components.Engine
IFileDownloader fileDownloader,
IOptions<GeneralConfiguration> generalConfigurationOptions,
ILogger<WindowsByondInstaller> logger)
: base(ioManager, fileDownloader, logger)
: base(ioManager, logger, fileDownloader)
{
this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
@@ -0,0 +1,43 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components.Engine
{
/// <summary>
/// Implementation of <see cref="IEngineInstallationData"/> for a zip file in a <see cref="Stream"/>.
/// </summary>
sealed class ZipStreamEngineInstallationData : IEngineInstallationData
{
/// <summary>
/// The <see cref="IIOManager"/> for the <see cref="ZipStreamEngineInstallationData"/>.
/// </summary>
readonly IIOManager ioManager;
/// <summary>
/// The <see cref="MemoryStream"/> containing the zip data of the engine.
/// </summary>
readonly Stream zipStream;
/// <summary>
/// Initializes a new instance of the <see cref="ZipStreamEngineInstallationData"/> class.
/// </summary>
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
/// <param name="zipStream">The value of <see cref="zipStream"/>.</param>
public ZipStreamEngineInstallationData(IIOManager ioManager, Stream zipStream)
{
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.zipStream = zipStream ?? throw new ArgumentNullException(nameof(zipStream));
}
/// <inheritdoc />
public ValueTask DisposeAsync() => zipStream.DisposeAsync();
/// <inheritdoc />
public Task ExtractToPath(string path, CancellationToken cancellationToken)
=> ioManager.ZipToDirectory(path, zipStream, cancellationToken);
}
}
@@ -61,6 +61,11 @@ namespace Tgstation.Server.Host.Configuration
/// </summary>
const uint DefaultShutdownTimeoutMinutes = 300;
/// <summary>
/// The default value for <see cref="OpenDreamGitUrl"/>.
/// </summary>
const string DefaultOpenDreamGitUrl = "https://github.com/OpenDreamProject/OpenDream";
/// <summary>
/// The current <see cref="ConfigVersion"/>.
/// </summary>
@@ -122,6 +127,11 @@ namespace Tgstation.Server.Host.Configuration
/// </summary>
public uint? DeploymentDirectoryCopyTasksPerCore { get; set; }
/// <summary>
/// Location of a publically accessible OpenDream repository.
/// </summary>
public Uri OpenDreamGitUrl { get; set; } = new Uri(DefaultOpenDreamGitUrl);
/// <summary>
/// Initializes a new instance of the <see cref="GeneralConfiguration"/> class.
/// </summary>
@@ -4,6 +4,7 @@ using Moq;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
@@ -51,7 +52,7 @@ namespace Tgstation.Server.Host.Components.Engine.Tests
var mockFileDownloader = new Mock<IFileDownloader>();
var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockFileDownloader.Object, mockLogger.Object);
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() => installer.DownloadVersion(null, default).AsTask());
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() => installer.DownloadVersion(null, null, default).AsTask());
var ourArray = Array.Empty<byte>();
mockFileDownloader
@@ -64,15 +65,20 @@ namespace Tgstation.Server.Host.Components.Engine.Tests
new MemoryStream(ourArray)))
.Verifiable();
var result = await installer.DownloadVersion(new ByondVersion
var result = ExtractMemoryStreamFromInstallationData(await installer.DownloadVersion(new ByondVersion
{
Engine = EngineType.Byond,
Version = new Version(123, 252345),
}, default);
}, null, default));
Assert.IsTrue(ourArray.SequenceEqual(result.ToArray()));
mockIOManager.Verify();
}
static MemoryStream ExtractMemoryStreamFromInstallationData(IEngineInstallationData engineInstallationData)
{
var zipStreamData = (ZipStreamEngineInstallationData)engineInstallationData;
return (MemoryStream)zipStreamData.GetType().GetField("stream", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(zipStreamData);
}
[TestMethod]
public async Task TestInstallByond()
@@ -254,7 +254,7 @@ namespace Tgstation.Server.Tests.Live.Instance
using var windowsByondInstaller = byondInstaller as WindowsByondInstaller;
// get the bytes for stable
using var stableBytesMs = await byondInstaller.DownloadVersion(testVersion, cancellationToken);
using var stableBytesMs = TestingUtils.ExtractMemoryStreamFromInstallationData(await byondInstaller.DownloadVersion(testVersion, null, cancellationToken));
var test = await byondClient.SetActiveVersion(
new ByondVersionRequest
@@ -158,7 +158,7 @@ namespace Tgstation.Server.Tests.Live.Instance
// get the bytes for stable
ByondInstallResponse installJob2;
using (var stableBytesMs = await byondInstaller.DownloadVersion(compatVersion, cancellationToken))
using (var stableBytesMs = TestingUtils.ExtractMemoryStreamFromInstallationData(await byondInstaller.DownloadVersion(compatVersion, null, cancellationToken)))
{
installJob2 = await instanceClient.Byond.SetActiveVersion(new ByondVersionRequest
{
+25 -19
View File
@@ -127,13 +127,15 @@ namespace Tgstation.Server.Tests
const string ArchiveEntryPath = "byond/bin/dd.exe";
var hasEntry = ArchiveHasFileEntry(
await byondInstaller.DownloadVersion(
new ByondVersion
{
Engine = EngineType.Byond,
Version = WindowsByondInstaller.DDExeVersion
},
default),
TestingUtils.ExtractMemoryStreamFromInstallationData(
await byondInstaller.DownloadVersion(
new ByondVersion
{
Engine = EngineType.Byond,
Version = WindowsByondInstaller.DDExeVersion
},
null,
default)),
ArchiveEntryPath);
Assert.IsTrue(hasEntry);
@@ -218,13 +220,15 @@ namespace Tgstation.Server.Tests
Engine = EngineType.Byond,
Version = MapThreadsVersion(),
},
await byondInstaller.DownloadVersion(
new ByondVersion
{
Engine = EngineType.Byond,
Version = MapThreadsVersion()
},
default),
TestingUtils.ExtractMemoryStreamFromInstallationData(
await byondInstaller.DownloadVersion(
new ByondVersion
{
Engine = EngineType.Byond,
Version = MapThreadsVersion()
},
null,
default)),
byondInstaller,
ioManager,
processExecutor,
@@ -400,7 +404,7 @@ namespace Tgstation.Server.Tests
Assert.AreEqual(latestMigrationSL, DatabaseContext.SLLatestMigration);
}
static async Task<Tuple<MemoryStream, ByondVersion>> GetByondVersionPriorTo(IEngineInstaller byondInstaller, Version version)
static async Task<Tuple<MemoryStream, ByondVersion>> GetByondVersionPriorTo(ByondInstallerBase byondInstaller, Version version)
{
var minusOneMinor = new Version(version.Major, version.Minor - 1);
var byondVersion = new ByondVersion
@@ -410,17 +414,19 @@ namespace Tgstation.Server.Tests
};
try
{
return Tuple.Create(await byondInstaller.DownloadVersion(
return Tuple.Create(TestingUtils.ExtractMemoryStreamFromInstallationData(await byondInstaller.DownloadVersion(
byondVersion,
CancellationToken.None), byondVersion);
null,
CancellationToken.None)), byondVersion);
}
catch (HttpRequestException)
{
var minusOneMajor = new Version(minusOneMinor.Major - 1, minusOneMinor.Minor);
byondVersion.Version = minusOneMajor;
return Tuple.Create(await byondInstaller.DownloadVersion(
return Tuple.Create(TestingUtils.ExtractMemoryStreamFromInstallationData(await byondInstaller.DownloadVersion(
byondVersion,
CancellationToken.None), byondVersion);
null,
CancellationToken.None)), byondVersion);
}
}
+10 -1
View File
@@ -1,10 +1,13 @@
using System;
using System.IO;
using System.Reflection;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Tgstation.Server.Host.Components.Engine;
namespace Tgstation.Server.Tests
{
static class TestingUtils
@@ -25,5 +28,11 @@ namespace Tgstation.Server.Tests
.Verifiable();
return mockLoggerFactory.Object;
}
public static MemoryStream ExtractMemoryStreamFromInstallationData(IEngineInstallationData engineInstallationData)
{
var zipStreamData = (ZipStreamEngineInstallationData)engineInstallationData;
return (MemoryStream)zipStreamData.GetType().GetField("stream", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(zipStreamData);
}
}
}