mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-27 07:04:57 +01:00
Remove timer shennanigans from repository. Fix null project name handling. Add IDatabaseContextFactory
This commit is contained in:
@@ -41,6 +41,12 @@ namespace Tgstation.Server.Api.Models
|
||||
[Permissions(WriteRight = InstanceManagerRights.SetConfiguration)]
|
||||
public bool ConfigurationAllowed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time interval in minutes the repository is automatically pulled and compiles
|
||||
/// </summary>
|
||||
[Permissions(WriteRight = InstanceManagerRights.SetAutoUpdate)]
|
||||
public int? AutoUpdateInterval { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Instance CloneMetadata() => new Instance
|
||||
{
|
||||
|
||||
@@ -8,12 +8,6 @@ namespace Tgstation.Server.Api.Models.Internal
|
||||
[Model(RightsType.DreamMaker, ReadRight = DreamMakerRights.Read, CanCrud = true, RequiresInstance = true)]
|
||||
public class DreamMakerSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// How often the <see cref="DreamMakerSettings"/> automatically compiles in minutes
|
||||
/// </summary>
|
||||
[Permissions(WriteRight = DreamMakerRights.SetAutoCompile)]
|
||||
public int? AutoCompileInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The .dme file <see cref="DreamMakerSettings"/> tries to compile with without the extension
|
||||
/// </summary>
|
||||
|
||||
@@ -46,11 +46,5 @@ namespace Tgstation.Server.Api.Models.Internal
|
||||
/// </summary>
|
||||
[Permissions(WriteRight = RepositoryRights.ChangeTestMergeCommits)]
|
||||
public bool ShowTestMergeCommitters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How often the <see cref="Repository"/> automatically updates in minutes
|
||||
/// </summary>
|
||||
[Permissions(WriteRight = RepositoryRights.ChangeAutoUpdate)]
|
||||
public int? AutoUpdateInterval { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,8 @@ namespace Tgstation.Server.Api.Rights
|
||||
/// </summary>
|
||||
CancelCompile = 4,
|
||||
/// <summary>
|
||||
/// User may modify <see cref="Models.Internal.DreamMakerSettings.AutoCompileInterval"/>
|
||||
/// </summary>
|
||||
SetAutoCompile = 8,
|
||||
/// <summary>
|
||||
/// User may modify <see cref="Models.Internal.DreamMakerSettings.ProjectName"/>
|
||||
/// </summary>
|
||||
SetDme = 16
|
||||
SetDme = 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ namespace Tgstation.Server.Api.Rights
|
||||
/// <summary>
|
||||
/// User can change <see cref="Models.Instance.ConfigurationAllowed"/>
|
||||
/// </summary>
|
||||
SetConfiguration = 128
|
||||
SetConfiguration = 128,
|
||||
/// <summary>
|
||||
/// User can change <see cref="Models.Instance.AutoUpdateInterval"/>
|
||||
/// </summary>
|
||||
SetAutoUpdate = 256
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,20 +37,16 @@ namespace Tgstation.Server.Api.Rights
|
||||
/// </summary>
|
||||
ChangeTestMergeCommits = 64,
|
||||
/// <summary>
|
||||
/// User may change <see cref="Models.Internal.RepositorySettings.AutoUpdateInterval"/>
|
||||
/// </summary>
|
||||
ChangeAutoUpdate = 128,
|
||||
/// <summary>
|
||||
/// User may read and change <see cref="Models.Internal.RepositorySettings.AccessUser"/> and <see cref="Models.Internal.RepositorySettings.AccessToken"/>
|
||||
/// </summary>
|
||||
ChangeCredentials = 256,
|
||||
ChangeCredentials = 128,
|
||||
/// <summary>
|
||||
/// User may set <see cref="Models.Repository.Reference"/> to another git reference (not a SHA)
|
||||
/// </summary>
|
||||
SetReference = 512,
|
||||
SetReference = 256,
|
||||
/// <summary>
|
||||
/// User may read all fields in the <see cref="Models.Repository"/> with the exception of <see cref="Models.Internal.RepositorySettings.AccessToken"/>
|
||||
/// </summary>
|
||||
Read = 1024,
|
||||
Read = 512,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace Tgstation.Server.Host.Components
|
||||
sealed class DmbFactory : IDmbFactory, ICompileJobConsumer
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContext"/> for the <see cref="DmbFactory"/>
|
||||
/// The <see cref="IDatabaseContextFactory"/> for the <see cref="DmbFactory"/>
|
||||
/// </summary>
|
||||
readonly IDatabaseContext databaseContext;
|
||||
readonly IDatabaseContextFactory databaseContextFactory;
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for the <see cref="DmbFactory"/>
|
||||
/// </summary>
|
||||
@@ -42,11 +42,11 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <summary>
|
||||
/// Construct a <see cref="DmbFactory"/>
|
||||
/// </summary>
|
||||
/// <param name="databaseContext">The value of <see cref="databaseContext"/></param>
|
||||
/// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/></param>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/></param>
|
||||
public DmbFactory(IDatabaseContext databaseContext, IIOManager ioManager)
|
||||
public DmbFactory(IDatabaseContextFactory databaseContextFactory, IIOManager ioManager)
|
||||
{
|
||||
this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
|
||||
cleanupCts = new CancellationTokenSource();
|
||||
@@ -117,16 +117,16 @@ namespace Tgstation.Server.Host.Components
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) =>
|
||||
{
|
||||
var cj = await databaseContext.CompileJobs.OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
|
||||
var cj = await db.CompileJobs.OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (cj == default(CompileJob))
|
||||
return;
|
||||
LoadCompileJob(cj);
|
||||
//delete all other compile jobs
|
||||
var directories = await ioManager.GetDirectories(".", cancellationToken).ConfigureAwait(false);
|
||||
await Task.WhenAll(directories.Where(x => x != cj.Job.ToString()).Select(x => ioManager.DeleteDirectory(x, cancellationToken))).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -23,8 +24,13 @@ namespace Tgstation.Server.Host.Components
|
||||
/// Name of the secondary directory used for compilation
|
||||
/// </summary>
|
||||
public const string BDirectoryName = "B";
|
||||
/// <summary>
|
||||
/// Extension for .dmbs
|
||||
/// </summary>
|
||||
public const string DmbExtension = ".dmb";
|
||||
|
||||
/// <summary>
|
||||
/// Extension for .dmes
|
||||
/// </summary>
|
||||
const string DmeExtension = ".dme";
|
||||
|
||||
/// <summary>
|
||||
@@ -61,6 +67,7 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <param name="byond">The value of <see cref="byond"/></param>
|
||||
/// <param name="interop">The value of <see cref="interop"/></param>
|
||||
/// <param name="compileJobConsumer">The value of <see cref="compileJobConsumer"/></param>
|
||||
///
|
||||
public DreamMaker(IIOManager ioManager, IConfiguration configuration, IDreamDaemonExecutor dreamDaemonExecutor, IByond byond, IInterop interop, ICompileJobConsumer compileJobConsumer)
|
||||
{
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
@@ -202,12 +209,12 @@ namespace Tgstation.Server.Host.Components
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Host.Models.CompileJob> Compile(string dmeName, IRepository repository, CancellationToken cancellationToken)
|
||||
public async Task<Host.Models.CompileJob> Compile(string projectName, IRepository repository, CancellationToken cancellationToken)
|
||||
{
|
||||
var job = new Host.Models.CompileJob
|
||||
{
|
||||
DirectoryName = Guid.NewGuid(),
|
||||
DmeName = dmeName
|
||||
DmeName = projectName
|
||||
};
|
||||
await ioManager.CreateDirectory(job.DirectoryName.ToString(), cancellationToken).ConfigureAwait(false);
|
||||
var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
|
||||
@@ -229,6 +236,16 @@ namespace Tgstation.Server.Host.Components
|
||||
using (repository)
|
||||
await repository.CopyTo(fullDirA, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (job.DmeName == null)
|
||||
{
|
||||
job.DmeName = (await ioManager.GetFilesWithExtension(dirA, DmeExtension, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
|
||||
if (job.DmeName == default)
|
||||
{
|
||||
job.Output = "Unable to find any .dme!";
|
||||
return job;
|
||||
}
|
||||
}
|
||||
|
||||
await ModifyDme(job, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
//run compiler, verify api
|
||||
@@ -263,18 +280,5 @@ namespace Tgstation.Server.Host.Components
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
//THIS IS FOR AUTO COMPILE INTERVAL STUFF, STOP TRYING TO REMOVE IT
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <summary>
|
||||
/// For managing the compiler
|
||||
/// </summary>
|
||||
interface IDreamMaker : IHostedService
|
||||
interface IDreamMaker
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts a compile
|
||||
/// </summary>
|
||||
/// <param name="dmeName">The .dme file to use without the extension</param>
|
||||
/// <param name="projectName">The name of the .dme to compile without the extension</param>
|
||||
/// <param name="repository">The <see cref="IRepository"/> to copy from</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the partially populated <see cref="CompileJob"/> for the operation. In particular, note the <see cref="CompileJob.RevisionInformation"/> field will only have it's <see cref="Api.Models.Internal.RevisionInformation.Commit"/> field populated</returns>
|
||||
Task<CompileJob> Compile(string dmeName, IRepository repository, CancellationToken cancellationToken);
|
||||
Task<CompileJob> Compile(string projectName, IRepository repository, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
{
|
||||
@@ -19,5 +20,7 @@ namespace Tgstation.Server.Host.Components
|
||||
Api.Models.Instance GetMetadata();
|
||||
|
||||
void Rename(string newName);
|
||||
|
||||
Task SetAutoUpdateInterval(int? newInterval);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.Core;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
{
|
||||
@@ -11,8 +11,8 @@ namespace Tgstation.Server.Host.Components
|
||||
/// Create an <see cref="IInstance"/>
|
||||
/// </summary>
|
||||
/// <param name="metadata">The <see cref="Host.Models.Instance"/></param>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the operation</param>
|
||||
/// <param name="databaseContextFactory">The <see cref="IDatabaseContextFactory"/> for the operation</param>
|
||||
/// <returns>A new <see cref="IInstance"/></returns>
|
||||
IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext);
|
||||
IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContextFactory databaseContextFactory);
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <summary>
|
||||
/// Factory for creating and loading <see cref="IRepository"/>s
|
||||
/// </summary>
|
||||
interface IRepositoryManager : IHostedService
|
||||
{
|
||||
interface IRepositoryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempt to load the <see cref="IRepository"/> from the default location
|
||||
/// </summary>
|
||||
@@ -24,12 +24,5 @@ namespace Tgstation.Server.Host.Components
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>The newly cloned <see cref="IRepository"/></returns>
|
||||
Task<IRepository> CloneRepository(string url, string accessString, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Change the interval in minutes at which the repository auto updates
|
||||
/// </summary>
|
||||
/// <param name="newInterval">The new interval in minutes or <see langword="null"/> to disable the auto update</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
Task SetAutoUpdateInterval(int? newInterval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,58 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Core;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
{
|
||||
sealed class Instance : IInstance
|
||||
/// <inheritdoc />
|
||||
sealed class Instance : IInstance, IDisposable
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IRepositoryManager RepositoryManager { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IByond Byond { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDreamMaker DreamMaker { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDreamDaemon DreamDaemon { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IChat Chat { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ICompileJobConsumer"/> for the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
readonly ICompileJobConsumer compileJobConsumer;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContextFactory"/> for the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
readonly IDatabaseContextFactory databaseContextFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Api.Models.Instance"/> for the <see cref="Instance"/>
|
||||
/// </summary>
|
||||
readonly Api.Models.Instance metadata;
|
||||
|
||||
public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IDreamDaemon dreamDaemon, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer)
|
||||
/// <summary>
|
||||
/// The auto update <see cref="Task"/>
|
||||
/// </summary>
|
||||
Task timerTask;
|
||||
/// <summary>
|
||||
/// <see cref="CancellationTokenSource"/> for <see cref="timerTask"/>
|
||||
/// </summary>
|
||||
CancellationTokenSource timerCts;
|
||||
|
||||
public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IDreamDaemon dreamDaemon, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory)
|
||||
{
|
||||
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
|
||||
RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
|
||||
@@ -32,10 +62,50 @@ namespace Tgstation.Server.Host.Components
|
||||
Chat = chat ?? throw new ArgumentNullException(nameof(chat));
|
||||
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer));
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => timerCts?.Dispose();
|
||||
|
||||
/// <summary>
|
||||
/// Pull the repository and compile for every set of given <paramref name="minutes"/>
|
||||
/// </summary>
|
||||
/// <param name="minutes">How many minutes the operation should repeat. Does not include running time</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
async Task TimerLoop(int minutes, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(new TimeSpan(0, minutes, 0), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
string accessToken = null, projectName = null;
|
||||
var dbTask = databaseContextFactory.UseContext(async (db) =>
|
||||
{
|
||||
var instanceQuery = db.Instances.Where(x => x.Id == metadata.Id);
|
||||
var projectNameTask = instanceQuery.Select(x => x.DreamMakerSettings.ProjectName).FirstOrDefaultAsync(cancellationToken);
|
||||
accessToken = await instanceQuery.Select(x => x.RepositorySettings.AccessToken).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
|
||||
projectName = await projectNameTask.ConfigureAwait(false);
|
||||
});
|
||||
using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
await dbTask.ConfigureAwait(false);
|
||||
await repo.FetchOrigin(accessToken, cancellationToken).ConfigureAwait(false);
|
||||
await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false);
|
||||
await DreamMaker.Compile(projectName, repo, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Api.Models.Instance GetMetadata() => metadata.CloneMetadata();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Rename(string newName)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(newName))
|
||||
@@ -43,8 +113,38 @@ namespace Tgstation.Server.Host.Components
|
||||
metadata.Name = newName;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(RepositoryManager.StartAsync(cancellationToken), DreamDaemon.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken), DreamMaker.StartAsync(cancellationToken));
|
||||
/// <inheritdoc />
|
||||
public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval), DreamDaemon.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken));
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(RepositoryManager.StopAsync(cancellationToken), DreamDaemon.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken), DreamMaker.StopAsync(cancellationToken));
|
||||
/// <inheritdoc />
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(null), DreamDaemon.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken));
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task SetAutoUpdateInterval(int? newInterval)
|
||||
{
|
||||
Task toWait;
|
||||
lock (this)
|
||||
{
|
||||
if (timerTask != null)
|
||||
{
|
||||
timerCts.Cancel();
|
||||
toWait = timerTask;
|
||||
}
|
||||
else
|
||||
toWait = Task.CompletedTask;
|
||||
}
|
||||
await toWait.ConfigureAwait(false);
|
||||
if (!newInterval.HasValue)
|
||||
return;
|
||||
lock (this)
|
||||
{
|
||||
//race condition, just quit
|
||||
if (timerTask != null)
|
||||
return;
|
||||
timerCts?.Dispose();
|
||||
timerCts = new CancellationTokenSource();
|
||||
timerTask = TimerLoop(newInterval.Value, timerCts.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
{
|
||||
@@ -19,7 +18,7 @@ namespace Tgstation.Server.Host.Components
|
||||
public InstanceFactory(IIOManager ioManager) => this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
|
||||
/// <inheritdoc />
|
||||
public IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext)
|
||||
public IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContextFactory databaseContextFactory)
|
||||
{
|
||||
//Create the ioManager for the instance
|
||||
|
||||
@@ -32,7 +31,7 @@ namespace Tgstation.Server.Host.Components
|
||||
var configurationIoManager = new ResolvingIOManager(instanceIoManager, "Configuration");
|
||||
var codeModificationsIoMananger = new ResolvingIOManager(instanceIoManager, "CodeModifications");
|
||||
|
||||
var dmbFactory = new DmbFactory(databaseContext, gameIoManager);
|
||||
var dmbFactory = new DmbFactory(databaseContextFactory, gameIoManager);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,16 +25,6 @@ namespace Tgstation.Server.Host.Components
|
||||
/// </summary>
|
||||
readonly SemaphoreSlim semaphore;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="CancellationTokenSource"/> for <see cref="currentTimerTask"/>
|
||||
/// </summary>
|
||||
CancellationTokenSource timerCancellationTokenSource;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the running update timer if any
|
||||
/// </summary>
|
||||
Task currentTimerTask;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a <see cref="RepositoryManager"/>
|
||||
/// </summary>
|
||||
@@ -48,49 +38,7 @@ namespace Tgstation.Server.Host.Components
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
timerCancellationTokenSource?.Dispose();
|
||||
semaphore.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops <see cref="currentTimerTask"/> and joins it
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
async Task StopTimer()
|
||||
{
|
||||
if (currentTimerTask == null)
|
||||
return;
|
||||
timerCancellationTokenSource.Cancel();
|
||||
await currentTimerTask.ConfigureAwait(false);
|
||||
currentTimerTask = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asyncronously fetch and reset the current branch for each given amount of <paramref name="minutes"/>
|
||||
/// </summary>
|
||||
/// <param name="minutes">The delay of the timer</param>
|
||||
/// <param name="accessString">The accessString to use for fetch operations</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation</returns>
|
||||
async Task TimerLoop(int minutes, string accessString, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(minutes), cancellationToken).ConfigureAwait(false);
|
||||
using (var repo = await LoadRepository(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
//TODO: Find the unauthorized exception, catch it, and log it
|
||||
await repo.FetchOrigin(accessString, cancellationToken).ConfigureAwait(false);
|
||||
await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
public void Dispose() => semaphore.Dispose();
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IRepository> CloneRepository(string url, string accessString, CancellationToken cancellationToken)
|
||||
@@ -134,33 +82,5 @@ namespace Tgstation.Server.Host.Components
|
||||
localSemaphore = null;
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task SetAutoUpdateInterval(int? newInterval)
|
||||
{
|
||||
await StopTimer().ConfigureAwait(false);
|
||||
if (!newInterval.HasValue)
|
||||
return;
|
||||
|
||||
string accessString = null;
|
||||
|
||||
if (timerCancellationTokenSource != null)
|
||||
timerCancellationTokenSource.Dispose();
|
||||
timerCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
currentTimerTask = TimerLoop(repositorySettings.AutoUpdateInterval.Value, accessString, timerCancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task StartAsync(CancellationToken cancellationToken) => SetAutoUpdateInterval(repositorySettings.AutoUpdateInterval);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var timerStopTask = StopTimer();
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
using (cancellationToken.Register(() => tcs.SetCanceled()))
|
||||
await Task.WhenAny(timerStopTask, tcs.Task).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory for scoping usage of <see cref="IDatabaseContext"/>s. Meant for use by <see cref="Components"/>
|
||||
/// </summary>
|
||||
interface IDatabaseContextFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Run an <paramref name="operation"/> in the scope of an <see cref="IDatabaseContext"/>
|
||||
/// </summary>
|
||||
/// <param name="operation">The operation to run</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running <paramref name="operation"/></returns>
|
||||
Task UseContext(Func<IDatabaseContext, Task> operation);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user