diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs b/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs new file mode 100644 index 0000000000..7d7c98bf9c --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Byond +{ + /// + abstract class ByondInstallerBase : IByondInstaller + { + /// + public abstract string DreamDaemonName { get; } + + /// + public abstract string DreamMakerName { get; } + + /// + /// Gets the URL formatter string for downloading a byond version of {0:Major} {1:Minor}. + /// + protected abstract string ByondRevisionsURLTemplate { get; } + + /// + /// Gets the for the . + /// + protected IIOManager IOManager { get; } + + /// + /// Gets the for the . + /// + protected ILogger Logger { get; } + + /// + /// Initializes a new instance of the . + /// + /// The value of . + /// The value of . + protected ByondInstallerBase(IIOManager ioManager, ILogger logger) + { + IOManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public abstract Task CleanCache(CancellationToken cancellationToken); + + /// + public abstract Task InstallByond(string path, Version version, CancellationToken cancellationToken); + + /// + public Task DownloadVersion(Version version, CancellationToken cancellationToken) + { + if (version == null) + throw new ArgumentNullException(nameof(version)); + + var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURLTemplate, version.Major, version.Minor); + + Logger.LogTrace("Downloading from: {0}", url); + + return IOManager.DownloadFile(new Uri(url), cancellationToken); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs index 6d741bb952..8b42a813ee 100644 --- a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs @@ -11,13 +11,8 @@ namespace Tgstation.Server.Host.Components.Byond /// /// for Posix systems /// - sealed class PosixByondInstaller : IByondInstaller + sealed class PosixByondInstaller : ByondInstallerBase { - /// - /// The URL format string for getting BYOND linux version {0}.{1} zipfile - /// - const string ByondRevisionsURLTemplate = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond_linux.zip"; - /// /// Path to the BYOND cache /// @@ -28,45 +23,37 @@ namespace Tgstation.Server.Host.Components.Byond const string ShellScriptExtension = ".sh"; /// - public string DreamDaemonName => DreamDaemonExecutableName + ShellScriptExtension; + public override string DreamDaemonName => DreamDaemonExecutableName + ShellScriptExtension; /// - public string DreamMakerName => DreamMakerExecutableName + ShellScriptExtension; + public override string DreamMakerName => DreamMakerExecutableName + ShellScriptExtension; - /// - /// The for the - /// - readonly IIOManager ioManager; + /// + protected override string ByondRevisionsURLTemplate => "https://secure.byond.com/download/build/{0}/{0}.{1}_byond_linux.zip"; /// /// The for the /// readonly IPostWriteHandler postWriteHandler; - /// - /// The for the - /// - readonly ILogger logger; - /// /// Construct a /// - /// The value of /// The value of - /// The value of - public PosixByondInstaller(IIOManager ioManager, IPostWriteHandler postWriteHandler, ILogger logger) + /// The for the . + /// The for the . + public PosixByondInstaller(IPostWriteHandler postWriteHandler, IIOManager ioManager, ILogger logger) + : base(ioManager, logger) { - this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler)); - this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// - public async Task CleanCache(CancellationToken cancellationToken) + public override async Task CleanCache(CancellationToken cancellationToken) { try { - await ioManager.DeleteDirectory(ByondCachePath, cancellationToken).ConfigureAwait(false); + await IOManager.DeleteDirectory(ByondCachePath, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -74,23 +61,12 @@ namespace Tgstation.Server.Host.Components.Byond } catch (Exception e) { - logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e); + Logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e); } } /// - public async Task DownloadVersion(Version version, CancellationToken cancellationToken) - { - if (version == null) - throw new ArgumentNullException(nameof(version)); - - var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURLTemplate, version.Major, version.Minor); - - return await ioManager.DownloadFile(new Uri(url), cancellationToken).ConfigureAwait(false); - } - - /// - public Task InstallByond(string path, Version version, CancellationToken cancellationToken) + public override Task InstallByond(string path, Version version, CancellationToken cancellationToken) { if (path == null) throw new ArgumentNullException(nameof(path)); @@ -106,16 +82,16 @@ namespace Tgstation.Server.Host.Components.Byond async Task WriteAndMakeExecutable(string fullPath, string script) { - await ioManager.WriteAllBytes(fullPath, Encoding.ASCII.GetBytes(script), cancellationToken).ConfigureAwait(false); + await IOManager.WriteAllBytes(fullPath, Encoding.ASCII.GetBytes(script), cancellationToken).ConfigureAwait(false); postWriteHandler.HandleWrite(fullPath); } - var basePath = ioManager.ConcatPath(path, ByondManager.BinPath); + var basePath = IOManager.ConcatPath(path, ByondManager.BinPath); - var task = Task.WhenAll(WriteAndMakeExecutable(ioManager.ConcatPath(basePath, DreamDaemonName), dreamDaemonScript), WriteAndMakeExecutable(ioManager.ConcatPath(basePath, DreamMakerName), dreamMakerScript)); + var task = Task.WhenAll(WriteAndMakeExecutable(IOManager.ConcatPath(basePath, DreamDaemonName), dreamDaemonScript), WriteAndMakeExecutable(IOManager.ConcatPath(basePath, DreamMakerName), dreamMakerScript)); - postWriteHandler.HandleWrite(ioManager.ConcatPath(basePath, DreamDaemonExecutableName)); - postWriteHandler.HandleWrite(ioManager.ConcatPath(basePath, DreamMakerExecutableName)); + postWriteHandler.HandleWrite(IOManager.ConcatPath(basePath, DreamDaemonExecutableName)); + postWriteHandler.HandleWrite(IOManager.ConcatPath(basePath, DreamMakerExecutableName)); return task; } diff --git a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs index 801b0468fc..4b48d6f0bf 100644 --- a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs +++ b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs @@ -12,13 +12,8 @@ namespace Tgstation.Server.Host.Components.Byond /// /// for windows systems /// - sealed class WindowsByondInstaller : IByondInstaller, IDisposable + sealed class WindowsByondInstaller : ByondInstallerBase, IDisposable { - /// - /// The URL format string for getting BYOND windows version {0}.{1} zipfile - /// - const string ByondRevisionsURLTemplate = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond.zip"; - /// /// Directory to byond installation configuration /// @@ -40,26 +35,19 @@ namespace Tgstation.Server.Host.Components.Byond const string ByondDXDir = "byond/directx"; /// - public string DreamDaemonName => "dreamdaemon.exe"; + public override string DreamDaemonName => "dreamdaemon.exe"; /// - public string DreamMakerName => "dm.exe"; + public override string DreamMakerName => "dm.exe"; - /// - /// The for the - /// - readonly IIOManager ioManager; + /// + protected override string ByondRevisionsURLTemplate => "https://secure.byond.com/download/build/{0}/{0}.{1}_byond.zip"; /// /// The for the /// readonly IProcessExecutor processExecutor; - /// - /// The for the - /// - readonly ILogger logger; - /// /// The for the /// @@ -73,14 +61,13 @@ namespace Tgstation.Server.Host.Components.Byond /// /// Construct a /// - /// The value of /// The value of - /// The value of - public WindowsByondInstaller(IIOManager ioManager, IProcessExecutor processExecutor, ILogger logger) + /// The for the . + /// The for the . + public WindowsByondInstaller(IProcessExecutor processExecutor, IIOManager ioManager, ILogger logger) + : base(ioManager, logger) { - this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor)); - this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); semaphore = new SemaphoreSlim(1); installedDirectX = false; @@ -90,11 +77,11 @@ namespace Tgstation.Server.Host.Components.Byond public void Dispose() => semaphore.Dispose(); /// - public async Task CleanCache(CancellationToken cancellationToken) + public override async Task CleanCache(CancellationToken cancellationToken) { try { - await ioManager.DeleteDirectory(ioManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "byond/cache"), cancellationToken).ConfigureAwait(false); + await IOManager.DeleteDirectory(IOManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "byond/cache"), cancellationToken).ConfigureAwait(false); } catch(OperationCanceledException) { @@ -102,26 +89,18 @@ namespace Tgstation.Server.Host.Components.Byond } catch (Exception e) { - logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e); + Logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e); } } /// - public Task DownloadVersion(Version version, CancellationToken cancellationToken) - { - var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURLTemplate, version.Major, version.Minor); - - return ioManager.DownloadFile(new Uri(url), cancellationToken); - } - - /// - public async Task InstallByond(string path, Version version, CancellationToken cancellationToken) + public override async Task InstallByond(string path, Version version, CancellationToken cancellationToken) { async Task SetNoPromptTrusted() { - var configPath = ioManager.ConcatPath(path, ByondConfigDir); - await ioManager.CreateDirectory(configPath, cancellationToken).ConfigureAwait(false); - await ioManager.WriteAllBytes(ioManager.ConcatPath(configPath, ByondDDConfig), Encoding.UTF8.GetBytes(ByondNoPromptTrustedMode), cancellationToken).ConfigureAwait(false); + var configPath = IOManager.ConcatPath(path, ByondConfigDir); + await IOManager.CreateDirectory(configPath, cancellationToken).ConfigureAwait(false); + await IOManager.WriteAllBytes(IOManager.ConcatPath(configPath, ByondDDConfig), Encoding.UTF8.GetBytes(ByondNoPromptTrustedMode), cancellationToken).ConfigureAwait(false); } var setNoPromptTrustedModeTask = SetNoPromptTrusted(); @@ -135,13 +114,13 @@ namespace Tgstation.Server.Host.Components.Byond { // ^check again because race conditions // always install it, it's pretty fast and will do better redundancy checking than us - var rbdx = ioManager.ConcatPath(path, ByondDXDir); + var rbdx = IOManager.ConcatPath(path, ByondDXDir); // noShellExecute because we aren't doing runas shennanigans IProcess directXInstaller; try { - directXInstaller = processExecutor.LaunchProcess(ioManager.ConcatPath(rbdx, "DXSETUP.exe"), rbdx, "/silent", noShellExecute: true); + directXInstaller = processExecutor.LaunchProcess(IOManager.ConcatPath(rbdx, "DXSETUP.exe"), rbdx, "/silent", noShellExecute: true); } catch (Exception e) { diff --git a/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs b/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs index e2fb2cff0f..b6337b1ebd 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/Byond/TestPosixByondInstaller.cs @@ -14,22 +14,22 @@ namespace Tgstation.Server.Host.Components.Byond.Tests public void TestConstruction() { Assert.ThrowsException(() => new PosixByondInstaller(null, null, null)); - var mockIOManager = new Mock(); - Assert.ThrowsException(() => new PosixByondInstaller(mockIOManager.Object, null, null)); var mockPostWriteHandler = new Mock(); - Assert.ThrowsException(() => new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, null)); + Assert.ThrowsException(() => new PosixByondInstaller(mockPostWriteHandler.Object, null, null)); + var mockIOManager = new Mock(); + Assert.ThrowsException(() => new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, null)); var mockLogger = new Mock>(); - new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, mockLogger.Object); + new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockLogger.Object); } [TestMethod] public async Task TestCacheClean() { - var mockIOManager = new Mock(); var mockPostWriteHandler = new Mock(); + var mockIOManager = new Mock(); var mockLogger = new Mock>(); - var installer = new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, mockLogger.Object); + var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockLogger.Object); const string ByondCachePath = "~/.byond/cache"; @@ -37,7 +37,7 @@ namespace Tgstation.Server.Host.Components.Byond.Tests await installer.CleanCache(default); - mockIOManager.Verify(); + mockPostWriteHandler.Verify(); mockIOManager.Setup(x => x.DeleteDirectory(ByondCachePath, default)).Throws(new OperationCanceledException()).Verifiable(); @@ -60,7 +60,7 @@ namespace Tgstation.Server.Host.Components.Byond.Tests var mockIOManager = new Mock(); var mockPostWriteHandler = new Mock(); var mockLogger = new Mock>(); - var installer = new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, mockLogger.Object); + var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockLogger.Object); await Assert.ThrowsExceptionAsync(() => installer.DownloadVersion(null, default)).ConfigureAwait(false); @@ -79,7 +79,7 @@ namespace Tgstation.Server.Host.Components.Byond.Tests var mockIOManager = new Mock(); var mockPostWriteHandler = new Mock(); var mockLogger = new Mock>(); - var installer = new PosixByondInstaller(mockIOManager.Object, mockPostWriteHandler.Object, mockLogger.Object); + var installer = new PosixByondInstaller(mockPostWriteHandler.Object, mockIOManager.Object, mockLogger.Object); const string FakePath = "fake"; await Assert.ThrowsExceptionAsync(() => installer.InstallByond(null, null, default)).ConfigureAwait(false);