BYOND believable

This commit is contained in:
Cyberboss
2018-07-23 15:41:53 -04:00
parent 7c47cbe565
commit 7f77d7f9ab
11 changed files with 158 additions and 123 deletions
-6
View File
@@ -9,12 +9,6 @@ namespace Tgstation.Server.Api.Models
[Model(RightsType.Byond, RequiresInstance = true)]
public sealed class Byond
{
/// <summary>
/// The <see cref="ByondStatus"/> for the <see cref="Byond"/> installation
/// </summary>
[Permissions(DenyWrite = true, ReadRight = ByondRights.ReadStatus)]
public ByondStatus ByondStatus { get; set; }
/// <summary>
/// The <see cref="System.Version"/> of the <see cref="Byond"/> installation used for new compiles. Will be <see langword="null"/> if the user does not have permission to view it or there is no BYOND version installed. Only considers the <see cref="Version.Major"/> and <see cref="Version.Minor"/> numbers
/// </summary>
@@ -1,39 +0,0 @@
namespace Tgstation.Server.Api.Models
{
/// <summary>
/// The status of a <see cref="Byond"/> update job
/// </summary>
#pragma warning disable CA1717 // Only FlagsAttribute enums should have plural names
public enum ByondStatus
#pragma warning restore CA1717 // Only FlagsAttribute enums should have plural names
{
/// <summary>
/// No update in progress
/// </summary>
Idle,
/// <summary>
/// Preparing to update
/// </summary>
Starting,
/// <summary>
/// Revision is downloading
/// </summary>
Downloading,
/// <summary>
/// Revision is deflating
/// </summary>
Staging,
/// <summary>
/// Revision is ready and waiting for DreamDaemon reboot
/// </summary>
Staged,
/// <summary>
/// Revision is being applied
/// </summary>
Updating,
/// <summary>
/// User does not have permission to view the <see cref="ByondStatus"/>
/// </summary>
Hidden,
}
}
@@ -1,63 +0,0 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components
{
/// <inheritdoc />
sealed class Byond : IByond
{
/// <summary>
/// The <see cref="IIOManager"/> for <see cref="Byond"/>
/// </summary>
readonly IIOManager ioManager;
/// <summary>
/// The <see cref="ILogger"/> for <see cref="Byond"/>
/// </summary>
readonly ILogger<Byond> logger;
/// <summary>
/// List of installed BYOND <see cref="Version"/>s
/// </summary>
readonly List<Version> installedVersions;
/// <summary>
/// Construct <see cref="Byond"/>
/// </summary>
/// <param name="ioManager">The value of <see cref="ioManager"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
public Byond(IIOManager ioManager, ILogger<Byond> logger)
{
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <inheritdoc />
public Task ChangeVersion(Version version, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task ClearCache(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task<Version> GetVersion(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public IByondExecutableLock UseExecutables(Version requiredVersion)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,113 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.IO;
namespace Tgstation.Server.Host.Components
{
/// <inheritdoc />
sealed class ByondManager : IByondManager
{
const string VersionFileName = "Version.txt";
/// <summary>
/// The <see cref="IIOManager"/> for the <see cref="ByondManager"/>
/// </summary>
readonly IIOManager ioManager;
/// <summary>
/// The <see cref="IByondInstaller"/> for the <see cref="ByondManager"/>
/// </summary>
readonly IByondInstaller byondInstaller;
/// <summary>
/// The <see cref="ILogger"/> for the <see cref="ByondManager"/>
/// </summary>
readonly ILogger<ByondManager> logger;
/// <summary>
/// Map of byond <see cref="Version"/>s to <see cref="Task"/>s that complete when they are installed
/// </summary>
readonly Dictionary<string, Task> installedVersions;
/// <summary>
/// Construct a <see cref="ByondManager"/>
/// </summary>
/// <param name="ioManager">The value of <see cref="ioManager"/></param>
/// <param name="byondInstaller">The value of <see cref="byondInstaller"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
public ByondManager(IIOManager ioManager, IByondInstaller byondInstaller, ILogger<ByondManager> logger)
{
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
static string VersionKey(Version version) => new Version(version.Major, version.Minor).ToString();
async Task InstallVersion(Version version, CancellationToken cancellationToken)
{
var ourTcs = new TaskCompletionSource<object>();
Task inProgressTask;
var versionKey = VersionKey(version);
bool installed;
lock (installedVersions)
{
installed = installedVersions.TryGetValue(versionKey, out inProgressTask);
if (!installed)
installedVersions.Add(versionKey, ourTcs.Task);
}
if(installed)
using (cancellationToken.Register(() => ourTcs.SetCanceled()))
{
await Task.WhenAny(ourTcs.Task, inProgressTask).ConfigureAwait(false);
return;
}
var downloadTask = byondInstaller.DownloadVersion(version, cancellationToken);
//okay up to us to install it then
await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);
await ioManager.CreateDirectory(versionKey, cancellationToken).ConfigureAwait(false);
var resolvedPath = ioManager.ResolvePath(versionKey);
using (var zipBytes = await downloadTask.ConfigureAwait(false))
using (var archive = new ZipArchive(zipBytes))
await Task.Factory.StartNew(() => archive.ExtractToDirectory(resolvedPath), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
await byondInstaller.InstallByond(resolvedPath, cancellationToken).ConfigureAwait(false);
//make sure to do this last because this is what tells us we have a valid version
await ioManager.WriteAllBytes(ioManager.ConcatPath(versionKey, VersionFileName), Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
public Task ChangeVersion(Version version, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task ClearCache(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task<Version> GetVersion(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public IByondExecutableLock UseExecutables(Version requiredVersion)
{
throw new NotImplementedException();
}
}
}
@@ -39,9 +39,9 @@ namespace Tgstation.Server.Host.Components
public CompilerStatus Status { get; private set; }
/// <summary>
/// The <see cref="IByond"/> for <see cref="DreamMaker"/>
/// The <see cref="IByondManager"/> for <see cref="DreamMaker"/>
/// </summary>
readonly IByond byond;
readonly IByondManager byond;
/// <summary>
/// The <see cref="IIOManager"/> for <see cref="DreamMaker"/>
/// </summary>
@@ -82,7 +82,7 @@ namespace Tgstation.Server.Host.Components
/// <param name="application">The value of <see cref="application"/></param>
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
public DreamMaker(IByond byond, IIOManager ioManager, IConfiguration configuration, ISessionControllerFactory sessionControllerFactory, ICompileJobConsumer compileJobConsumer, IApplication application, IEventConsumer eventConsumer, ILogger<DreamMaker> logger)
public DreamMaker(IByondManager byond, IIOManager ioManager, IConfiguration configuration, ISessionControllerFactory sessionControllerFactory, ICompileJobConsumer compileJobConsumer, IApplication application, IEventConsumer eventConsumer, ILogger<DreamMaker> logger)
{
this.byond = byond;
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
@@ -0,0 +1,30 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Stream = System.IO.Stream;
namespace Tgstation.Server.Host.Components
{
/// <summary>
/// For downloading and installing BYOND extractions
/// </summary>
interface IByondInstaller
{
/// <summary>
/// Download a given BYOND <paramref name="version"/>
/// </summary>
/// <param name="version">The <see cref="Version"/> of BYOND to download</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="Stream"/> of the zipfile</returns>
Task<Stream> DownloadVersion(Version version, CancellationToken cancellationToken);
/// <summary>
/// Does actions necessary to get an extracted BYOND installation working
/// </summary>
/// <param name="path">The path to the BYOND installation</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns></returns>
Task InstallByond(string path, CancellationToken cancellationToken);
}
}
@@ -7,17 +7,17 @@ namespace Tgstation.Server.Host.Components
/// <summary>
/// For managing the BYOND installation
/// </summary>
public interface IByond
public interface IByondManager
{
/// <summary>
/// Change the current BYOND version
/// Change the active BYOND version
/// </summary>
/// <param name="version">The new <see cref="Version"/></param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
Task ChangeVersion(Version version, CancellationToken cancellationToken);
/// <summary>
/// Get the currently installed BYOND version
/// Get the currently active BYOND version
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>The current BYOND version</returns>
@@ -16,9 +16,9 @@ namespace Tgstation.Server.Host.Components
IRepositoryManager RepositoryManager { get; }
/// <summary>
/// The <see cref="IByond"/> for the <see cref="IInstance"/>
/// The <see cref="IByondManager"/> for the <see cref="IInstance"/>
/// </summary>
IByond Byond { get; }
IByondManager ByondManager { get; }
/// <summary>
/// The <see cref="IDreamMaker"/> for the <see cref="IInstance"/>
@@ -16,7 +16,7 @@ namespace Tgstation.Server.Host.Components
public IRepositoryManager RepositoryManager { get; }
/// <inheritdoc />
public IByond Byond { get; }
public IByondManager ByondManager { get; }
/// <inheritdoc />
public IDreamMaker DreamMaker { get; }
@@ -59,11 +59,11 @@ namespace Tgstation.Server.Host.Components
/// </summary>
CancellationTokenSource timerCts;
public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IWatchdog watchdog, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory)
public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByondManager byondManager, IDreamMaker dreamMaker, IWatchdog watchdog, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory)
{
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
Byond = byond ?? throw new ArgumentNullException(nameof(byond));
ByondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager));
DreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker));
watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
Chat = chat ?? throw new ArgumentNullException(nameof(chat));
@@ -115,7 +115,7 @@ namespace Tgstation.Server.Host.Components
var repoManager = new RepositoryManager(metadata.RepositorySettings, repoIoManager);
IByond byond = null;
IByondManager byond = null;
var configuration = new Configuration(configurationIoManager, synchronousIOManager, symlinkFactory, loggerFactory.CreateLogger<Configuration>());
var chat = chatFactory.CreateChat();
@@ -24,9 +24,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
readonly IExecutor executor;
/// <summary>
/// The <see cref="IByond"/> for the <see cref="SessionControllerFactory"/>
/// The <see cref="IByondManager"/> for the <see cref="SessionControllerFactory"/>
/// </summary>
readonly IByond byond;
readonly IByondManager byond;
/// <summary>
/// The <see cref="IByondTopicSender"/> for the <see cref="SessionControllerFactory"/>
@@ -81,7 +81,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <param name="ioManager">The value of <see cref="ioManager"/></param>
/// <param name="chat">The value of <see cref="chat"/></param>
/// <param name="loggerFactory">The value of <see cref="loggerFactory"/></param>
public SessionControllerFactory(IExecutor executor, IByond byond, IByondTopicSender byondTopicSender, IInteropRegistrar interopRegistrar, ICryptographySuite cryptographySuite, IApplication application, IIOManager ioManager, IChat chat, ILoggerFactory loggerFactory, Models.Instance instance)
public SessionControllerFactory(IExecutor executor, IByondManager byond, IByondTopicSender byondTopicSender, IInteropRegistrar interopRegistrar, ICryptographySuite cryptographySuite, IApplication application, IIOManager ioManager, IChat chat, ILoggerFactory loggerFactory, Models.Instance instance)
{
this.executor = executor ?? throw new ArgumentNullException(nameof(executor));
this.byond = byond ?? throw new ArgumentNullException(nameof(byond));