Merge DreamMakerController and Instance auto update compile functions.

Instance auto update now properly specifies it as a job
This commit is contained in:
Jordan Brown
2018-09-15 14:03:44 -04:00
parent a374651e38
commit cad7ddc10f
3 changed files with 99 additions and 110 deletions
@@ -1,5 +1,6 @@
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Host.Components.Byond;
using Tgstation.Server.Host.Components.Chat;
@@ -75,5 +76,15 @@ namespace Tgstation.Server.Host.Components
/// <param name="newInterval">The new auto update inteval</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task SetAutoUpdateInterval(uint newInterval);
/// <summary>
/// Run the compile job and insert it into the database. Meant to be called by a <see cref="Core.IJobManager"/>
/// </summary>
/// <param name="job">The running <see cref="Job"/></param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> for the operation</param>
/// <param name="progressReporter">The <see cref="Action{T1}"/> to report compilation progress</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task CompileProcess(Job job, IServiceProvider serviceProvider, Action<int> progressReporter, CancellationToken cancellationToken);
}
}
@@ -1,9 +1,11 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components.Byond;
using Tgstation.Server.Host.Components.Chat;
using Tgstation.Server.Host.Components.Compiler;
@@ -114,6 +116,59 @@ namespace Tgstation.Server.Host.Components
RepositoryManager.Dispose();
}
/// <inheritdoc />
public async Task CompileProcess(Job job, IServiceProvider serviceProvider, Action<int> progressReporter, CancellationToken cancellationToken)
{
var databaseContext = serviceProvider.GetRequiredService<IDatabaseContext>();
var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == metadata.Id).Select(x => new DreamDaemonSettings
{
StartupTimeout = x.StartupTimeout,
SecurityLevel = x.SecurityLevel
}).FirstOrDefaultAsync(cancellationToken);
var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken).ConfigureAwait(false);
if (dreamMakerSettings == default)
throw new JobException("Missing DreamMakerSettings in DB!");
var ddSettings = await ddSettingsTask.ConfigureAwait(false);
if (ddSettings == default)
throw new JobException("Missing DreamDaemonSettings in DB!");
CompileJob compileJob;
RevisionInformation revInfo;
using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
{
if (repo == null)
throw new JobException("Missing Repository!");
var repoSha = repo.Head;
revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).FirstOrDefaultAsync().ConfigureAwait(false);
if (revInfo == default)
{
revInfo = new RevisionInformation
{
CommitSha = repoSha,
OriginCommitSha = repoSha,
Instance = new Models.Instance
{
Id = metadata.Id
}
};
logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, repoSha);
databaseContext.Instances.Attach(revInfo.Instance);
}
compileJob = await DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.SecurityLevel.Value, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
}
compileJob.Job = job;
databaseContext.CompileJobs.Add(compileJob); //will be saved by job context
job.PostComplete = ct => CompileJobConsumer.LoadCompileJob(compileJob, ct);
}
/// <summary>
/// Pull the repository and compile for every set of given <paramref name="minutes"/>
/// </summary>
@@ -129,33 +184,32 @@ namespace Tgstation.Server.Host.Components
try
{
CompileJob job = null;
Job job = null;
Models.User user = null;
//need this the whole time
var noRepo = false;
await databaseContextFactory.UseContext(async (db) =>
{
//start up queries we'll need in the future
var instanceQuery = db.Instances.Where(x => x.Id == metadata.Id);
var ddSettingsTask = instanceQuery.Select(x => x.DreamDaemonSettings).Select(x => new DreamDaemonSettings
{
StartupTimeout = x.StartupTimeout,
SecurityLevel = x.SecurityLevel
}).FirstAsync(cancellationToken);
var dmSettingsTask = instanceQuery.Select(x => x.DreamMakerSettings).FirstAsync(cancellationToken);
var repositorySettingsTask = instanceQuery.Select(x => x.RepositorySettings).FirstAsync(cancellationToken);
var userTask = db.Users.FirstAsync(cancellationToken);
var repositorySettingsTask = db.RepositorySettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken);
using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
{
if (repo == null)
{
//no repo, no auto updates
noRepo = true;
return;
}
//start the rev info query
var startSha = repo.Head;
var revInfoTask = instanceQuery.SelectMany(x => x.RevisionInformations).Where(x => x.CommitSha == startSha).FirstOrDefaultAsync(cancellationToken);
//need repo setting to fetch
var repositorySettings = await repositorySettingsTask.ConfigureAwait(false);
//the main point of auto update is to pull the remote
await repo.FetchOrigin(repositorySettings.AccessUser, repositorySettings.AccessToken, null, cancellationToken).ConfigureAwait(false);
var startSha = repo.Head;
//take appropriate auto update actions
bool shouldSyncTracked;
if (repositorySettings.AutoUpdatesKeepTestMerges.Value)
@@ -174,38 +228,28 @@ namespace Tgstation.Server.Host.Components
//synch if necessary
if (repositorySettings.AutoUpdatesSynchronize.Value && startSha != repo.Head)
await repo.Sychronize(repositorySettings.AccessUser, repositorySettings.AccessToken, shouldSyncTracked, cancellationToken).ConfigureAwait(false);
//finish other queries
var dmSettings = await dmSettingsTask.ConfigureAwait(false);
var ddSettings = await ddSettingsTask.ConfigureAwait(false);
var revInfo = await revInfoTask.ConfigureAwait(false);
//null rev info handling
if (revInfo == default)
{
var currentSha = repo.Head;
revInfo = new RevisionInformation
{
CommitSha = currentSha,
OriginCommitSha = currentSha,
Instance = new Models.Instance
{
Id = metadata.Id
}
};
logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, currentSha);
db.Instances.Attach(revInfo.Instance);
}
//finally start compile
job = await DreamMaker.Compile(revInfo, dmSettings, ddSettings.SecurityLevel.Value, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
}
db.CompileJobs.Add(job);
await db.Save(cancellationToken).ConfigureAwait(false);
user = await userTask.ConfigureAwait(false);
//finally set up the job
job = new Job
{
StartedBy = user,
Instance = new Models.Instance
{
Id = metadata.Id
},
Description = "Scheduled code deployment",
CancelRightsType = RightsType.DreamMaker,
CancelRight = (ulong)DreamMakerRights.CancelCompile
};
await jobManager.RegisterOperation(job, CompileProcess, cancellationToken).ConfigureAwait(false);
}).ConfigureAwait(false);
await CompileJobConsumer.LoadCompileJob(job, cancellationToken).ConfigureAwait(false);
if(!noRepo)
await jobManager.WaitForJobCompletion(job, user, cancellationToken, default).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
@@ -10,7 +9,6 @@ using System.Threading.Tasks;
using Tgstation.Server.Api;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Components;
using Tgstation.Server.Host.Components.Repository;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
@@ -97,7 +95,7 @@ namespace Tgstation.Server.Host.Controllers
CancelRight = (ulong)DreamMakerRights.CancelCompile,
Instance = Instance
};
await jobManager.RegisterOperation(job, (paramJob, serviceProvider, progressReporter, ct) => RunCompile(paramJob, serviceProvider, Instance, ct), cancellationToken).ConfigureAwait(false);
await jobManager.RegisterOperation(job, instanceManager.GetInstance(Instance).CompileProcess, cancellationToken).ConfigureAwait(false);
return Accepted(job.ToApi());
}
@@ -129,69 +127,5 @@ namespace Tgstation.Server.Host.Controllers
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
return await Read(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Run the compile job and insert it into the database
/// </summary>
/// <param name="job">The running <see cref="Job"/></param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> for the operation</param>
/// <param name="instanceModel">The <see cref="Models.Instance"/> for the operation</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
async Task RunCompile(Job job, IServiceProvider serviceProvider, Models.Instance instanceModel, CancellationToken cancellationToken)
{
var instanceManager = serviceProvider.GetRequiredService<IInstanceManager>();
var databaseContext = serviceProvider.GetRequiredService<IDatabaseContext>();
var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == instanceModel.Id).Select(x => new DreamDaemonSettings
{
StartupTimeout = x.StartupTimeout,
SecurityLevel = x.SecurityLevel
}).FirstOrDefaultAsync(cancellationToken);
var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == instanceModel.Id).FirstAsync(cancellationToken).ConfigureAwait(false);
if (dreamMakerSettings == default)
throw new JobException("Missing DreamMakerSettings in DB!");
var ddSettings = await ddSettingsTask.ConfigureAwait(false);
if (ddSettings == default)
throw new JobException("Missing DreamDaemonSettings in DB!");
var instance = instanceManager.GetInstance(instanceModel);
CompileJob compileJob;
RevisionInformation revInfo;
using (var repo = await instance.RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
{
if (repo == null)
throw new JobException("Missing Repository!");
var repoSha = repo.Head;
revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).FirstOrDefaultAsync().ConfigureAwait(false);
if (revInfo == default)
{
revInfo = new RevisionInformation
{
CommitSha = repoSha,
OriginCommitSha = repoSha,
Instance = new Models.Instance
{
Id = Instance.Id
}
};
Logger.LogWarning(Repository.OriginTrackingErrorTemplate, repoSha);
databaseContext.Instances.Attach(revInfo.Instance);
}
compileJob = await instance.DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.SecurityLevel.Value, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
}
compileJob.Job = job;
databaseContext.CompileJobs.Add(compileJob); //will be saved by job context
job.PostComplete = ct => instance.CompileJobConsumer.LoadCompileJob(compileJob, ct);
}
}
}