Various fixups

This commit is contained in:
Jordan Brown
2018-07-30 10:33:54 -04:00
parent 7bd7043c74
commit 7dc4118936
7 changed files with 73 additions and 37 deletions
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Tgstation.Server.Api.Rights;
namespace Tgstation.Server.Api.Models
@@ -48,6 +49,13 @@ namespace Tgstation.Server.Api.Models
[Permissions(WriteRight = InstanceManagerRights.SetAutoUpdate)]
public int? AutoUpdateInterval { get; set; }
/// <summary>
/// The <see cref="Job"/> representing a change of <see cref="Path"/>
/// </summary>
[Permissions(DenyWrite = true)]
[NotMapped]
public Job MoveJob { get; set; }
/// <inheritdoc />
public Instance CloneMetadata() => new Instance
{
@@ -47,6 +47,10 @@ namespace Tgstation.Server.Api.Rights
/// <summary>
/// User can change <see cref="Models.Instance.AutoUpdateInterval"/>
/// </summary>
SetAutoUpdate = 256
SetAutoUpdate = 256,
/// <summary>
/// User can cancel move operations
/// </summary>
CancelMove = 512,
}
}
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
@@ -40,6 +41,11 @@ namespace Tgstation.Server.Host.Components
/// </summary>
readonly IApplication application;
/// <summary>
/// The <see cref="ILogger"/> for the <see cref="InstanceManager"/>
/// </summary>
readonly ILogger<InstanceManager> logger;
/// <summary>
/// Map of <see cref="Api.Models.Instance.Id"/>s to respective <see cref="IInstance"/>s
/// </summary>
@@ -56,12 +62,14 @@ namespace Tgstation.Server.Host.Components
/// <param name="ioManager">The value of <paramref name="ioManager"/></param>
/// <param name="databaseContextFactory">The value of <paramref name="databaseContextFactory"/></param>
/// <param name="application">The value of <see cref="application"/></param>
public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application)
/// <param name="logger">The value of <see cref="logger"/></param>
public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, ILogger<InstanceManager> logger)
{
this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
this.application = application ?? throw new ArgumentNullException(nameof(application));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
instances = new Dictionary<long, IInstance>();
interopConsumers = new Dictionary<string, IInteropConsumer>();
@@ -93,24 +101,11 @@ namespace Tgstation.Server.Host.Components
if (newPath == null)
throw new ArgumentNullException(nameof(newPath));
if (instance.Online.Value)
await OfflineInstance(instance, cancellationToken).ConfigureAwait(false);
Task instanceOnlineTask = null;
try
{
var oldPath = instance.Path;
await ioManager.CopyDirectory(oldPath, newPath, null, cancellationToken).ConfigureAwait(false);
instance.Path = ioManager.ResolvePath(newPath);
instanceOnlineTask = OnlineInstance(instance, default);
await ioManager.DeleteDirectory(oldPath, cancellationToken).ConfigureAwait(false);
}
finally
{
if (instance.Online.Value)
if (instanceOnlineTask == null)
await OnlineInstance(instance, default).ConfigureAwait(false);
else
await instanceOnlineTask.ConfigureAwait(false);
}
throw new InvalidOperationException("Cannot move an online instance!");
var oldPath = instance.Path;
await ioManager.CopyDirectory(oldPath, newPath, null, cancellationToken).ConfigureAwait(false);
instance.Path = ioManager.ResolvePath(newPath);
await ioManager.DeleteDirectory(oldPath, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
@@ -118,6 +113,7 @@ namespace Tgstation.Server.Host.Components
{
if (metadata == null)
throw new ArgumentNullException(nameof(metadata));
logger.LogInformation("Offlining instance ID {0}", metadata.Id);
IInstance instance;
lock (this)
{
@@ -140,6 +136,7 @@ namespace Tgstation.Server.Host.Components
{
if (metadata == null)
throw new ArgumentNullException(nameof(metadata));
logger.LogInformation("Onlining instance ID {0} ({1}) at {2}", metadata.Id, metadata.Name, metadata.Path);
var instance = instanceFactory.CreateInstance(metadata, this);
try
{
@@ -78,7 +78,7 @@ namespace Tgstation.Server.Host.Controllers
var byondManager = instanceManager.GetInstance(Instance).ByondManager;
//remove cruff fields
var installingVersion = new Version(model.Version.Major, model.Version.Major);
var installingVersion = new Version(model.Version.Major, model.Version.Minor);
var result = new Api.Models.Byond();
@@ -272,27 +272,58 @@ namespace Tgstation.Server.Host.Controllers
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
var oldAutoStart = originalModel.DreamDaemonSettings.AutoStart;
try
{
if (originalOnline && model.Online.Value == false)
await instanceManager.OfflineInstance(originalModel, cancellationToken).ConfigureAwait(false);
else if (!originalOnline && model.Online.Value == true)
{
//force autostart false here because we don't want any long running jobs right now
//remember to document this
originalModel.DreamDaemonSettings.AutoStart = false;
await instanceManager.OnlineInstance(originalModel, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception e)
{
logger.LogError("Error changing instance online state! Exception: {0}", e);
originalModel.Online = originalOnline;
originalModel.DreamDaemonSettings.AutoStart = oldAutoStart;
if (originalModelPath != null)
originalModel.Path = originalModelPath;
await DatabaseContext.Save(default).ConfigureAwait(false);
throw;
}
var api = originalModel.ToApi();
if (originalModelPath != null)
await ioManager.MoveDirectory(originalModelPath, model.Path, cancellationToken).ConfigureAwait(false);
{
var job = new Models.Job
{
Description = "Move instance location",
Instance = Instance,
CancelRightsType = RightsType.InstanceManager,
CancelRight = (int)InstanceManagerRights.CancelMove,
StartedBy = AuthenticationContext.User
};
return Json(originalModel.ToApi());
await jobManager.RegisterOperation(job, async (paramJob, serviceProvider, ct) => {
try
{
await instanceManager.MoveInstance(Instance, originalModel.Path, ct).ConfigureAwait(false);
}
catch
{
originalModel.Path = originalModelPath;
await DatabaseContext.Save(default).ConfigureAwait(false);
throw;
}
}, cancellationToken).ConfigureAwait(false);
api.MoveJob = job.ToApi();
}
return Json(api);
}
/// <inheritdoc />
@@ -37,18 +37,12 @@ namespace Tgstation.Server.Host.Controllers
[TgsAuthorize]
public override async Task<IActionResult> List(CancellationToken cancellationToken)
{
IQueryable<Job> query = DatabaseContext.Jobs;
if (Instance != null)
//you KNOW this will need pagination eventually right?
var jobs = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id).OrderByDescending(x => x.StartedAt).Select(x => new Api.Models.Job
{
if (AuthenticationContext.InstanceUser?.AnyRights != true)
return Forbid();
query = query.Where(x => x.Instance.Id == Instance.Id);
}
else
query = query.Where(x => x.Instance == null);
var jobs = await query.Where(x => x.StoppedAt == null).ToListAsync(cancellationToken).ConfigureAwait(false);
return Json(jobs.Select(x => x.ToApi()));
Id = x.Id
}).ToListAsync(cancellationToken).ConfigureAwait(false);
return Json(jobs);
}
/// <inheritdoc />
@@ -60,12 +54,12 @@ namespace Tgstation.Server.Host.Controllers
if (job == default(Job))
return NotFound();
if (job.StoppedAt != null)
return StatusCode((int)HttpStatusCode.Gone);
if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0)
return Forbid();
if(job.StoppedAt != null)
return StatusCode((int)HttpStatusCode.Gone);
await jobManager.CancelJob(job, AuthenticationContext.User, cancellationToken).ConfigureAwait(false);
return Ok();
}
@@ -172,6 +172,8 @@ namespace Tgstation.Server.Host.Core
var databaseContext = scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
job = new Job { Id = job.Id };
databaseContext.Jobs.Attach(job);
user = new User { Id = user.Id };
databaseContext.Users.Attach(user);
job.CancelledBy = user;
await databaseContext.Save(cancellationToken).ConfigureAwait(false);
}