tgstation-server 6.12.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
EngineController.cs
Go to the documentation of this file.
1using System;
2using System.IO;
3using System.Linq;
6
9
25
27{
33 {
38
43
49 static Version NormalizeByondVersion(Version version) => version.Build == 0 ? new Version(version.Major, version.Minor) : version;
50
62 IDatabaseContext databaseContext,
63 IAuthenticationContext authenticationContext,
68 IApiHeadersProvider apiHeadersProvider)
69 : base(
70 databaseContext,
71 authenticationContext,
72 logger,
74 apiHeadersProvider)
75 {
78 }
79
86 [HttpGet]
87 [TgsAuthorize(EngineRights.ReadActive)]
91 => WithComponentInstance(instance =>
92 ValueTask.FromResult<IActionResult>(
93 Json(
95 {
96 EngineVersion = instance.EngineManager.ActiveVersion,
97 })));
98
108 [TgsAuthorize(EngineRights.ListInstalled)]
112 instance => Paginated(
113 () => ValueTask.FromResult(
115 instance
116 .EngineManager
117 .InstalledVersions
118 .Select(x => new EngineResponse
119 {
120 EngineVersion = x,
121 })
122 .AsQueryable()
123 .OrderBy(x => x.EngineVersion!.ToString()))),
124 null,
125 page,
126 pageSize,
128
137 [HttpPost]
139 EngineRights.InstallOfficialOrChangeActiveByondVersion
140 | EngineRights.InstallCustomByondVersion
141 | EngineRights.InstallOfficialOrChangeActiveOpenDreamVersion
142 | EngineRights.InstallCustomOpenDreamVersion)]
145#pragma warning disable CA1502 // TODO: Decomplexify
146#pragma warning disable CA1506
148#pragma warning restore CA1506
149#pragma warning restore CA1502
150 {
151 ArgumentNullException.ThrowIfNull(model);
152 var earlyOut = ValidateEngineVersion(model.EngineVersion);
153 if (earlyOut != null)
154 return earlyOut;
155
157
159 var isByondEngine = model.EngineVersion!.Engine!.Value == EngineType.Byond;
162 : EngineRights.InstallOfficialOrChangeActiveOpenDreamVersion;
165 : EngineRights.InstallCustomOpenDreamVersion;
166 if ((!engineRights.HasFlag(officialPerm) && !uploadingZip)
167 || (!engineRights.HasFlag(customPerm) && uploadingZip))
168 return Forbid();
169
170 // remove cruff fields
173 async instance =>
174 {
175 var byondManager = instance.EngineManager;
176 var versionAlreadyInstalled = !uploadingZip && byondManager.InstalledVersions.Any(x => x.Equals(model.EngineVersion));
178 {
179 Logger.LogInformation(
180 "User ID {userId} changing instance ID {instanceId} engine to {newByondVersion}",
182 Instance.Id,
183 model);
184
185 try
186 {
188 await byondManager.ChangeVersion(progressReporter, model.EngineVersion, null, false, cancellationToken);
189 }
191 {
192 Logger.LogDebug(
193 ex,
194 "Race condition: Engine {version} uninstalled before we could switch to it. Creating install job instead...",
195 model);
197 }
198 }
199
201 {
202 if (model.EngineVersion.CustomIteration.HasValue)
203 return BadRequest(new ErrorMessageResponse(ErrorCode.EngineNonExistentCustomVersion));
204
205 Logger.LogInformation(
206 "User ID {userId} installing engine version {newByondVersion} on instance ID {instanceId}",
208 model,
209 Instance.Id);
210
211 // run the install through the job manager
212 var job = Models.Job.Create(
214 ? JobCode.EngineCustomInstall
215 : JobCode.EngineOfficialInstall,
217 Instance,
218 EngineRights.CancelInstall);
219 job.Description += $" {model.EngineVersion}";
220
222 if (uploadingZip)
224
225 try
226 {
228 job,
229 async (core, databaseContextFactory, paramJob, progressHandler, jobCancellationToken) =>
230 {
232 if (fileUploadTicket != null)
233 await using (fileUploadTicket)
234 {
235 var uploadStream = await fileUploadTicket.GetResult(jobCancellationToken) ?? throw new JobException(ErrorCode.FileUploadExpired);
237 try
238 {
240 }
241 catch
242 {
243 await zipFileStream.DisposeAsync();
244 throw;
245 }
246 }
247
248 await using (zipFileStream)
249 await core!.EngineManager.ChangeVersion(
251 model.EngineVersion,
253 true,
255 },
257
258 result.InstallJob = job.ToApi();
259 result.FileTicket = fileUploadTicket?.Ticket.FileTicket;
260 }
261 catch
262 {
263 if (fileUploadTicket != null)
264 await fileUploadTicket.DisposeAsync();
265
266 throw;
267 }
268 }
269
270 return result.InstallJob != null ? Accepted(result) : Json(result);
271 });
272 }
273
283 [HttpDelete]
284 [TgsAuthorize(EngineRights.DeleteInstall)]
289 {
290 ArgumentNullException.ThrowIfNull(model);
291 var earlyOut = ValidateEngineVersion(model.EngineVersion);
292 if (earlyOut != null)
293 return earlyOut;
294
295 var engineVersion = model.EngineVersion!;
297 instance =>
298 {
299 var byondManager = instance.EngineManager;
300 var activeVersion = byondManager.ActiveVersion;
301 if (activeVersion != null && engineVersion.Equals(activeVersion))
302 return ValueTask.FromResult<IActionResult?>(
303 Conflict(new ErrorMessageResponse(ErrorCode.EngineCannotDeleteActiveVersion)));
304
305 var versionNotInstalled = !byondManager.InstalledVersions.Any(x => x.Equals(engineVersion));
306
307 return ValueTask.FromResult<IActionResult?>(
309 ? this.Gone()
310 : null);
311 });
312
313 if (notInstalledResponse != null)
315
316 var isByondVersion = engineVersion.Engine!.Value == EngineType.Byond;
317
318 // run the install through the job manager
325 : EngineRights.InstallCustomOpenDreamVersion;
326
327 var job = Models.Job.Create(JobCode.EngineDelete, AuthenticationContext.User, Instance, cancelRight);
328 job.Description += $" {engineVersion}";
329
331 job,
332 (instanceCore, databaseContextFactory, job, progressReporter, jobCancellationToken)
333 => instanceCore!.EngineManager.DeleteVersion(progressReporter, engineVersion, jobCancellationToken),
335
336 var apiResponse = job.ToApi();
337 return Accepted(apiResponse);
338 }
339
346 {
347 if (version == null || !version.Engine.HasValue)
348 return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
349
351 var validSha = version.SourceSHA?.Length == Limits.MaximumCommitShaLength;
352 if ((isByond
353 && (version.Version == null
354 || validSha))
355 || (version.Engine.Value == EngineType.OpenDream &&
356 ((version.SourceSHA == null && version.Version == null)
357 || (version.Version != null && (version.Version.Revision != -1 || validSha)))))
358 return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
359
360 if (isByond)
361 {
363 if (version.Version.Build != -1)
364 return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure));
365 }
366
367 return null;
368 }
369 }
370}
Information about an engine installation.
virtual ? long Id
The ID of the entity.
Definition EntityId.cs:13
Metadata about a server instance.
Definition Instance.cs:9
Represents a PermissionSets permissions in an Instance.
EngineRights? EngineRights
The Rights.EngineRights of the InstancePermissionSet.
Sanity limits to prevent users from overloading.
Definition Limits.cs:9
const int MaximumCommitShaLength
Length limit for git commit SHAs.
Definition Limits.cs:28
A request to switch to a given EngineVersion.
Represents an engine installation job. FileTicketResponse.FileTicket is used to upload custom version...
Represents an installed Models.EngineVersion.
Represents an error message returned by the server.
Represents a long running job on the server. Model is read-only, updates attempt to cancel the job.
Definition JobResponse.cs:7
Routes to a server actions.
Definition Routes.cs:9
const string List
The postfix for list operations.
Definition Routes.cs:113
const string Engine
The engine controller.
Definition Routes.cs:53
ILogger< ApiController > Logger
The ILogger for the ApiController.
async ValueTask< IActionResult?> WithComponentInstanceNullable(Func< IInstanceCore, ValueTask< IActionResult?> > action, Models.Instance? instance=null)
Run a given action with the relevant IInstance.
readonly IInstanceManager instanceManager
The IInstanceManager for the ComponentInterfacingController.
async ValueTask< IActionResult > WithComponentInstance(Func< IInstanceCore, ValueTask< IActionResult > > action, Models.Instance? instance=null)
Run a given action with the relevant IInstance.
Controller for managing engine installations.
ValueTask< IActionResult > Read()
Gets the active EngineVersion.
readonly IFileTransferTicketProvider fileTransferService
The IFileTransferTicketProvider for the EngineController.
ValueTask< IActionResult > List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
Lists installed EngineVersions.
EngineController(IDatabaseContext databaseContext, IAuthenticationContext authenticationContext, ILogger< EngineController > logger, IInstanceManager instanceManager, IJobManager jobManager, IFileTransferTicketProvider fileTransferService, IApiHeadersProvider apiHeadersProvider)
Initializes a new instance of the EngineController class.
static Version NormalizeByondVersion(Version version)
Remove the Version.Build from a given version if present.
async ValueTask< IActionResult > Delete([FromBody] EngineVersionDeleteRequest model, CancellationToken cancellationToken)
Attempts to delete the BYOND version specified in a given model from the instance.
BadRequestObjectResult? ValidateEngineVersion(EngineVersion? version)
Validate and normalize a given version .
async ValueTask< IActionResult > Update([FromBody] EngineVersionRequest model, CancellationToken cancellationToken)
Changes the active engine version to the one specified in a given model .
readonly IJobManager jobManager
The IJobManager for the EngineController.
ComponentInterfacingController for operations that require an instance.
Operation exceptions thrown from the context of a Models.Job.
Manages the runtime of Jobs.
ValueTask RegisterOperation(Job job, JobEntrypoint operation, CancellationToken cancellationToken)
Registers a given Job and begins running it.
For creating and accessing authentication contexts.
Service for temporarily storing files to be downloaded or uploaded.
IFileUploadTicket CreateUpload(FileUploadStreamKind streamKind)
Create a IFileUploadTicket.
A FileTicketResponse that waits for a pending upload.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition ErrorCode.cs:12
JobCode
The different types of Response.JobResponse.
Definition JobCode.cs:9
EngineType
The type of engine the codebase is using.
Definition EngineType.cs:7
@ List
User may list files if the Models.Instance allows it.
EngineRights
Rights for engine version management.
FileUploadStreamKind
Determines the type of global::System.IO.Stream returned from IFileUploadTicket's created from IFileT...