2using System.Collections.Generic;
6using System.Threading.Tasks;
8using Microsoft.EntityFrameworkCore;
9using Microsoft.Extensions.Logging;
111 ILogger<DmbFactory>
logger,
118 this.logger =
logger ??
throw new ArgumentNullException(nameof(
logger));
119 this.metadata =
metadata ??
throw new ArgumentNullException(nameof(
metadata));
133 ArgumentNullException.ThrowIfNull(job);
136 if (newProvider ==
null)
146 newProvider.CompileJob,
166 throw new InvalidOperationException(
"No .dmb available!");
168 throw new ArgumentOutOfRangeException(nameof(lockCount), lockCount,
"lockCount must be greater than or equal to 0!");
173 logger.LogTrace(
"Compile job {0} lock count now: {1}", jobId, incremented);
179 public async Task
StartAsync(CancellationToken cancellationToken)
187 .Where(x => x.Job.Instance.Id ==
metadata.Id)
188 .OrderByDescending(x => x.Job.StoppedAt)
189 .FirstOrDefaultAsync(cancellationToken);
201 public async Task
StopAsync(CancellationToken cancellationToken)
205 using (cancellationToken.Register(() =>
cleanupCts.Cancel()))
215#pragma warning disable CA1506
218 ArgumentNullException.ThrowIfNull(compileJob);
221 logger.LogTrace(
"Loading compile job {0}...", compileJob.
Id);
223 async db => compileJob = await db
226 .Where(x => x.Id == compileJob.
Id)
228 .ThenInclude(x => x.StartedBy)
229 .Include(x => x.RevisionInformation)
230 .ThenInclude(x => x.PrimaryTestMerge)
231 .ThenInclude(x => x.MergedBy)
232 .Include(x => x.RevisionInformation)
233 .ThenInclude(x => x.ActiveTestMerges)
234 .ThenInclude(x => x.TestMerge)
235 .ThenInclude(x => x.MergedBy)
236 .FirstAsync(cancellationToken));
243 logger.LogTrace(
"Setting missing StoppedAt for CompileJob.Job #{0}...", compileJob.
Job.
Id);
244 compileJob.Job.StoppedAt = DateTimeOffset.UtcNow;
247 var providerSubmitted =
false;
251 if (providerSubmitted)
258 const string LegacyADirectoryName =
"A";
259 const string LegacyBDirectoryName =
"B";
263 newProvider.Directory,
264 newProvider.DmbName),
267 if (!dmbExistsAtRoot)
269 logger.LogTrace(
"Didn't find .dmb at game directory root, checking A/B dirs...");
272 newProvider.Directory,
273 LegacyADirectoryName,
274 newProvider.DmbName),
278 newProvider.Directory,
279 LegacyBDirectoryName,
280 newProvider.DmbName),
283 if (!(await primaryCheckTask && await secondaryCheckTask))
285 logger.LogWarning(
"Error loading compile job, .dmb missing!");
291 logger.LogDebug(
"Creating legacy two folder .dmb provider targeting {0} directory...", LegacyADirectoryName);
292 newProvider =
new DmbProvider(compileJob,
ioManager, CleanupAction, Path.DirectorySeparatorChar + LegacyADirectoryName);
305 providerSubmitted =
true;
307 logger.LogTrace(
"Compile job {0} lock count now: {1}", compileJob.
Id, value);
313 if (!providerSubmitted)
314 newProvider.Dispose();
317#pragma warning restore CA1506
320#pragma warning disable CA1506
323 List<long> jobIdsToSkip;
329 List<string> jobUidsToNotErase =
null;
332 if (jobIdsToSkip.Any())
336 jobUidsToNotErase = (await db
340 x => x.Job.Instance.Id ==
metadata.Id
341 && jobIdsToSkip.Contains(x.Id.Value))
342 .Select(x => x.DirectoryName.Value)
343 .ToListAsync(cancellationToken))
344 .Select(x => x.ToString())
349 jobUidsToNotErase =
new List<string>();
353 logger.LogTrace(
"We will not clean the following directories: {0}", String.Join(
", ", jobUidsToNotErase));
360 var tasks = directories.Select(async x =>
363 if (jobUidsToNotErase.Contains(nameOnly))
365 logger.LogDebug(
"Cleaning unused game folder: {0}...", nameOnly);
369 await DeleteCompileJobContent(x, cancellationToken);
371 catch (OperationCanceledException)
377 logger.LogWarning(e,
"Error deleting directory {0}!", x);
381 await Task.WhenAll(tasks);
383#pragma warning restore CA1506
399 async Task HandleCleanup()
405 var deploymentJob = remoteDeploymentManager.
MarkInactive(job, CancellationToken.None);
410 async Task WrapThrowableTasks()
414 await Task.WhenAll(deleteTask, deploymentJob);
422 await Task.WhenAll(otherTask, WrapThrowableTasks());
436 logger.LogTrace(
"Compile job {0} lock count now: {1}", job.
Id, decremented);
439 logger.LogError(
"Extra Dispose of DmbProvider for CompileJob {compileJobId}!", job.
Id);
virtual ? long Id
The ID of the entity.
Metadata about a server instance.
Guid? DirectoryName
The Game folder the results were compiled into.
DateTimeOffset? StoppedAt
When the Job stopped.
readonly IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory
The IRemoteDeploymentManagerFactory for the DmbFactory.
TaskCompletionSource newerDmbTcs
TaskCompletionSource resulting in the latest DmbProvider yet to exist.
readonly IDictionary< long, int > jobLockCounts
Map of CompileJob.JobIds to locks on them.
readonly IIOManager ioManager
The IIOManager for the DmbFactory.
readonly IEventConsumer eventConsumer
The IEventConsumer for DmbFactory.
async Task CleanUnusedCompileJobs(CancellationToken cancellationToken)
Deletes all compile jobs that are inactive in the Game folder. A Task representing the running operat...
DmbFactory(IDatabaseContextFactory databaseContextFactory, IIOManager ioManager, IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, IEventConsumer eventConsumer, ILogger< DmbFactory > logger, Api.Models.Instance metadata)
Initializes a new instance of the DmbFactory class.
bool DmbAvailable
If LockNextDmb will succeed.
CompileJob LatestCompileJob()
Gets the latest CompileJob. The latest CompileJob.
readonly ILogger< DmbFactory > logger
The ILogger for the DmbFactory.
void CleanRegisteredCompileJob(CompileJob job)
Delete the Api.Models.Internal.CompileJob.DirectoryName of job .
async Task StopAsync(CancellationToken cancellationToken)
readonly Api.Models.Instance metadata
The Api.Models.Instance for the DmbFactory.
async Task LoadCompileJob(CompileJob job, CancellationToken cancellationToken)
Load a new job into the ICompileJobSink. A Task representing the running operation.
IDmbProvider LockNextDmb(int lockCount)
Gets the next IDmbProvider. A new IDmbProvider.
Task OnNewerDmb
Get a Task that completes when the result of a call to LockNextDmb will be different than the previou...
async Task DeleteCompileJobContent(string directory, CancellationToken cancellationToken)
Handles cleaning the resources of a CompileJob.
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the DmbFactory.
readonly CancellationTokenSource cleanupCts
The CancellationTokenSource for cleanupTask.
bool started
If the DmbFactory is "started" via IComponentService.
async Task StartAsync(CancellationToken cancellationToken)
Task cleanupTask
Task representing calls to CleanRegisteredCompileJob(CompileJob).
async Task< IDmbProvider > FromCompileJob(CompileJob compileJob, CancellationToken cancellationToken)
Gets a IDmbProvider for a given CompileJob. A Task<TResult> resulting in a new IDmbProvider represent...
IDmbProvider nextDmbProvider
The latest DmbProvider.
A IDmbProvider that uses symlinks.
const string LiveGameDirectory
The directory where the baseProvider is symlinked to.
Job Job
See CompileJobResponse.Job.
Factory for IDmbProviders.
Provides absolute paths to the latest compiled .dmbs.
CompileJob CompileJob
The CompileJob of the .dmb.
Factory for creating IRemoteDeploymentManagers.
IRemoteDeploymentManager CreateRemoteDeploymentManager(Api.Models.Instance metadata, RemoteGitProvider remoteGitProvider)
Creates a IRemoteDeploymentManager for a given remoteGitProvider .
Task StageDeployment(CompileJob compileJob, CancellationToken cancellationToken)
Stage a given compileJob 's deployment.
Task MarkInactive(CompileJob compileJob, CancellationToken cancellationToken)
Mark the deplotment for a given compileJob as inactive.
Consumes EventTypes and takes the appropriate actions.
Task HandleEvent(EventType eventType, IEnumerable< string > parameters, bool deploymentPipeline, CancellationToken cancellationToken)
Handle a given eventType .
Factory for scoping usage of IDatabaseContexts. Meant for use by Components.
Task UseContext(Func< IDatabaseContext, Task > operation)
Run an operation in the scope of an IDatabaseContext.
Interface for using filesystems.
string GetFileName(string path)
Gets the file name portion of a path .
string ResolvePath()
Retrieve the full path of the current working directory.
string ConcatPath(params string[] paths)
Combines an array of strings into a path.
Task< IReadOnlyList< string > > GetDirectories(string path, CancellationToken cancellationToken)
Returns directory names in a given path .
Task CreateDirectory(string path, CancellationToken cancellationToken)
Create a directory at path .
Task DeleteDirectory(string path, CancellationToken cancellationToken)
Recursively delete a directory, removes and does not enter any symlinks encounterd.
Task< bool > FileExists(string path, CancellationToken cancellationToken)
Check that the file at path exists.
EventType
Types of events. Mirror in tgs.dm.