diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index 41a91b5a25..4d02e969b6 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -91,11 +91,6 @@ namespace Tgstation.Server.Host.Components /// readonly IJobManager jobManager; - /// - /// The for the - /// - readonly ICredentialsProvider credentialsProvider; - /// /// The for the /// @@ -111,6 +106,11 @@ namespace Tgstation.Server.Host.Components /// readonly IPlatformIdentifier platformIdentifier; + /// + /// The for the . + /// + readonly IRepositoryFactory repositoryFactory; + /// /// Construct an /// @@ -128,10 +128,10 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of /// The value of - /// The value of /// The value of /// The value of /// The value of + /// The value of . public InstanceFactory( IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, @@ -147,10 +147,10 @@ namespace Tgstation.Server.Host.Components IPostWriteHandler postWriteHandler, IWatchdogFactory watchdogFactory, IJobManager jobManager, - ICredentialsProvider credentialsProvider, INetworkPromptReaper networkPromptReaper, IGitHubClientFactory gitHubClientFactory, - IPlatformIdentifier platformIdentifier) + IPlatformIdentifier platformIdentifier, + IRepositoryFactory repositoryFactory) { this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); @@ -166,10 +166,10 @@ namespace Tgstation.Server.Host.Components this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler)); this.watchdogFactory = watchdogFactory ?? throw new ArgumentNullException(nameof(watchdogFactory)); this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); - this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider)); this.networkPromptReaper = networkPromptReaper ?? throw new ArgumentNullException(nameof(networkPromptReaper)); this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory)); this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier)); + this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory)); } /// @@ -187,7 +187,13 @@ namespace Tgstation.Server.Host.Components var configuration = new StaticFiles.Configuration(configurationIoManager, synchronousIOManager, symlinkFactory, processExecutor, postWriteHandler, platformIdentifier, loggerFactory.CreateLogger()); var eventConsumer = new EventConsumer(configuration); - var repoManager = new RepositoryManager(metadata.RepositorySettings, repoIoManager, eventConsumer, credentialsProvider, loggerFactory.CreateLogger(), loggerFactory.CreateLogger()); + var repoManager = new RepositoryManager( + repositoryFactory, + repoIoManager, + eventConsumer, + loggerFactory.CreateLogger(), + loggerFactory.CreateLogger(), + metadata.RepositorySettings); try { var byond = new ByondManager(byondIOManager, byondInstaller, eventConsumer, loggerFactory.CreateLogger()); diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index 28fbc31558..d9c45691d1 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -10,6 +10,7 @@ using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Jobs; +using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.Components { @@ -46,6 +47,11 @@ namespace Tgstation.Server.Host.Components /// readonly IServerControl serverControl; + /// + /// The for the + /// + readonly IPlatformIdentifier platformIdentifier; + /// /// The for the /// @@ -75,8 +81,17 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of /// The value of + /// The value of . /// The value of - public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger logger) + public InstanceManager( + IInstanceFactory instanceFactory, + IIOManager ioManager, + IDatabaseContextFactory databaseContextFactory, + IApplication application, + IJobManager jobManager, + IServerControl serverControl, + IPlatformIdentifier platformIdentifier, + ILogger logger) { this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory)); this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); @@ -84,6 +99,7 @@ namespace Tgstation.Server.Host.Components this.application = application ?? throw new ArgumentNullException(nameof(application)); this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl)); + this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); serverControl.RegisterForRestart(this); @@ -212,6 +228,7 @@ namespace Tgstation.Server.Host.Components { try { + platformIdentifier.CheckCompatibility(); var factoryStartup = instanceFactory.StartAsync(cancellationToken); await databaseContext.Initialize(cancellationToken).ConfigureAwait(false); await jobManager.StartAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Tgstation.Server.Host/Components/Repository/CredentialsProvider.cs b/src/Tgstation.Server.Host/Components/Repository/CredentialsProvider.cs deleted file mode 100644 index 80112f3e47..0000000000 --- a/src/Tgstation.Server.Host/Components/Repository/CredentialsProvider.cs +++ /dev/null @@ -1,53 +0,0 @@ -using LibGit2Sharp; -using LibGit2Sharp.Handlers; -using Microsoft.Extensions.Logging; -using System; -using Tgstation.Server.Host.Jobs; - -namespace Tgstation.Server.Host.Components.Repository -{ - /// - sealed class CredentialsProvider : ICredentialsProvider - { - /// - /// The for the - /// - readonly ILogger logger; - - /// - /// Construct a - /// - /// The value of - public CredentialsProvider(ILogger logger) - { - this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - /// - public CredentialsHandler GenerateHandler(string username, string password) => (a, b, supportedCredentialTypes) => - { - var hasCreds = username != null; - var supportsUserPass = supportedCredentialTypes.HasFlag(SupportedCredentialTypes.UsernamePassword); - var supportsAnonymous = supportedCredentialTypes.HasFlag(SupportedCredentialTypes.Default); - - logger.LogTrace("Credentials requested. Present: {0}. Supports anonymous: {1}. Supports user/pass: {2}", hasCreds, supportsAnonymous, supportsUserPass); - if (supportsUserPass) - { - if (hasCreds) - return new UsernamePasswordCredentials - { - Username = username, - Password = password - }; - } - - if (supportsAnonymous) - return new DefaultCredentials(); - - if (hasCreds) - throw new JobException("Remote does not support anonymous authentication!"); - - throw new JobException("Server does not support anonymous or username/password authentication!"); - }; - } -} diff --git a/src/Tgstation.Server.Host/Components/Repository/ICredentialsProvider.cs b/src/Tgstation.Server.Host/Components/Repository/ICredentialsProvider.cs index c130b02a78..496a015187 100644 --- a/src/Tgstation.Server.Host/Components/Repository/ICredentialsProvider.cs +++ b/src/Tgstation.Server.Host/Components/Repository/ICredentialsProvider.cs @@ -13,6 +13,6 @@ namespace Tgstation.Server.Host.Components.Repository /// The optional username to use in the /// The optional password to use in the /// A new - CredentialsHandler GenerateHandler(string username, string password); + CredentialsHandler GenerateCredentialsHandler(string username, string password); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Repository/IRepositoryFactory.cs b/src/Tgstation.Server.Host/Components/Repository/IRepositoryFactory.cs new file mode 100644 index 0000000000..a4e7d2b4c3 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Repository/IRepositoryFactory.cs @@ -0,0 +1,36 @@ +using LibGit2Sharp; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Repository +{ + /// + /// Factory for creating s. + /// + interface IRepositoryFactory : ICredentialsProvider + { + /// + /// Create an in-memeory . + /// + /// A new in-memory . + LibGit2Sharp.IRepository CreateInMemory(); + + /// + /// Load a from a given . + /// + /// The full path to the . + /// The loaded . + LibGit2Sharp.IRepository CreateFromPath(string path); + + /// + /// Clone a remote . + /// + /// The of the remote. + /// The . + /// The full path to the cloned . + /// The for the operation. + /// A representing the running operation. + Task Clone(Uri url, CloneOptions cloneOptions, string path, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryFactory.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryFactory.cs new file mode 100644 index 0000000000..f687a75c33 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryFactory.cs @@ -0,0 +1,84 @@ +using LibGit2Sharp; +using LibGit2Sharp.Handlers; +using Microsoft.Extensions.Logging; +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Jobs; + +namespace Tgstation.Server.Host.Components.Repository +{ + /// + sealed class RepositoryFactory : IRepositoryFactory + { + /// + /// The for the . + /// + readonly ILogger logger; + + /// + /// Initializes a new instance of the . + /// + /// The value of . + public RepositoryFactory(ILogger logger) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public LibGit2Sharp.IRepository CreateInMemory() + { + logger.LogTrace("Creating in-memory LibGit2Sharp..."); + return new LibGit2Sharp.Repository(); + } + + /// + public LibGit2Sharp.IRepository CreateFromPath(string path) + { + if (path == null) + throw new ArgumentNullException(nameof(path)); + logger.LogTrace("Creating LibGit2Sharp Repostory at {0}...", path); + return new LibGit2Sharp.Repository(path); + } + + /// + public Task Clone(Uri url, CloneOptions cloneOptions, string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + try + { + LibGit2Sharp.Repository.Clone(url.ToString(), path, cloneOptions); + } + catch (UserCancelledException) + { + cancellationToken.ThrowIfCancellationRequested(); + } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public CredentialsHandler GenerateCredentialsHandler(string username, string password) => (a, b, supportedCredentialTypes) => + { + var hasCreds = username != null; + var supportsUserPass = supportedCredentialTypes.HasFlag(SupportedCredentialTypes.UsernamePassword); + var supportsAnonymous = supportedCredentialTypes.HasFlag(SupportedCredentialTypes.Default); + + logger.LogTrace("Credentials requested. Present: {0}. Supports anonymous: {1}. Supports user/pass: {2}", hasCreds, supportsAnonymous, supportsUserPass); + if (supportsUserPass) + { + if (hasCreds) + return new UsernamePasswordCredentials + { + Username = username, + Password = password + }; + } + + if (supportsAnonymous) + return new DefaultCredentials(); + + if (hasCreds) + throw new JobException("Remote does not support anonymous authentication!"); + + throw new JobException("Server does not support anonymous or username/password authentication!"); + }; + } +} diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs index 4feeb46897..f198ad0b4b 100644 --- a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs @@ -18,6 +18,11 @@ namespace Tgstation.Server.Host.Components.Repository /// public bool CloneInProgress { get; private set; } + /// + /// The for the + /// + readonly IRepositoryFactory repositoryFactory; + /// /// The for the /// @@ -28,11 +33,6 @@ namespace Tgstation.Server.Host.Components.Repository /// readonly IEventConsumer eventConsumer; - /// - /// The for the - /// - readonly ICredentialsProvider credentialsProvider; - /// /// The created s /// @@ -56,18 +56,23 @@ namespace Tgstation.Server.Host.Components.Repository /// /// Construct a /// - /// The value of + /// The value of . /// The value of /// The value of - /// The value of /// The value of /// The value of - public RepositoryManager(RepositorySettings repositorySettings, IIOManager ioManager, IEventConsumer eventConsumer, ICredentialsProvider credentialsProvider, ILogger repositoryLogger, ILogger logger) + /// The value of + public RepositoryManager( + IRepositoryFactory repositoryFactory, + IIOManager ioManager, + IEventConsumer eventConsumer, + ILogger repositoryLogger, + ILogger logger, + RepositorySettings repositorySettings) { this.repositorySettings = repositorySettings ?? throw new ArgumentNullException(nameof(repositorySettings)); this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); - this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider)); this.repositoryLogger = repositoryLogger ?? throw new ArgumentNullException(nameof(repositoryLogger)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); semaphore = new SemaphoreSlim(1); @@ -104,30 +109,28 @@ namespace Tgstation.Server.Host.Components.Repository if (!await ioManager.DirectoryExists(".", cancellationToken).ConfigureAwait(false)) try { - await Task.Factory.StartNew(() => + var cloneOptions = new CloneOptions { - string path = null; - try + OnProgress = (a) => !cancellationToken.IsCancellationRequested, + OnTransferProgress = (a) => { - path = LibGit2Sharp.Repository.Clone(url.ToString(), ioManager.ResolvePath("."), new CloneOptions - { - OnProgress = (a) => !cancellationToken.IsCancellationRequested, - OnTransferProgress = (a) => - { - var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2)); - progressReporter((int)percentage); - return !cancellationToken.IsCancellationRequested; - }, - RecurseSubmodules = true, - OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested, - RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested, - BranchName = initialBranch, - CredentialsProvider = credentialsProvider.GenerateHandler(username, password) - }); - } - catch (UserCancelledException) { } - cancellationToken.ThrowIfCancellationRequested(); - }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2)); + progressReporter((int)percentage); + return !cancellationToken.IsCancellationRequested; + }, + RecurseSubmodules = true, + OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested, + RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested, + BranchName = initialBranch, + CredentialsProvider = repositoryFactory.GenerateCredentialsHandler(username, password) + }; + + await repositoryFactory.Clone( + url, + cloneOptions, + ioManager.ResolvePath("."), + cancellationToken) + .ConfigureAwait(false); } catch { @@ -168,13 +171,13 @@ namespace Tgstation.Server.Host.Components.Repository if (CloneInProgress) throw new InvalidOperationException("The repository is being cloned!"); await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - LibGit2Sharp.Repository repo = null; + LibGit2Sharp.IRepository repo = null; await Task.Factory.StartNew(() => { try { logger.LogTrace("Creating LibGit2Sharp.Repository..."); - repo = new LibGit2Sharp.Repository(ioManager.ResolvePath(".")); + repo = repositoryFactory.CreateFromPath(ioManager.ResolvePath(".")); } catch (RepositoryNotFoundException e) { @@ -193,7 +196,7 @@ namespace Tgstation.Server.Host.Components.Repository return null; } - return new Repository(repo, ioManager, eventConsumer, credentialsProvider, repositoryLogger, () => + return new Repository(repo, ioManager, eventConsumer, repositoryFactory, repositoryLogger, () => { logger.LogTrace("Releasing semaphore due to Repository disposal..."); semaphore.Release(); diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index bb372eae36..55021ccf9f 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -347,7 +347,7 @@ namespace Tgstation.Server.Host.Core }); // configure component services - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Tgstation.Server.Host/System/PlatformIdentifier.cs b/src/Tgstation.Server.Host/System/PlatformIdentifier.cs index e0fe08363d..04c5ee01cb 100644 --- a/src/Tgstation.Server.Host/System/PlatformIdentifier.cs +++ b/src/Tgstation.Server.Host/System/PlatformIdentifier.cs @@ -1,7 +1,7 @@ -using LibGit2Sharp; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using System; using System.Runtime.InteropServices; +using Tgstation.Server.Host.Components.Repository; using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.System @@ -20,6 +20,11 @@ namespace Tgstation.Server.Host.System /// readonly ISystemIdentityFactory systemIdentityFactory; + /// + /// The for the . + /// + readonly IRepositoryFactory repositoryFactory; + /// /// The for the . /// @@ -28,11 +33,16 @@ namespace Tgstation.Server.Host.System /// /// Construct a /// - /// The value of . + /// The value of . + /// The value of . /// The value of . - public PlatformIdentifier(ISystemIdentityFactory systemIdentityFactory, ILogger logger) + public PlatformIdentifier( + ISystemIdentityFactory systemIdentityFactory, + IRepositoryFactory repositoryFactory, + ILogger logger) { this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); + this.repositoryFactory = repositoryFactory ?? throw new ArgumentNullException(nameof(repositoryFactory)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); @@ -44,7 +54,7 @@ namespace Tgstation.Server.Host.System { try { - new Repository().Dispose(); + repositoryFactory.CreateInMemory().Dispose(); } catch {