Compare commits

..

2 Commits

7 changed files with 40 additions and 34 deletions

View File

@ -59,7 +59,7 @@ sealed class BackupScheduler : CancellableBackgroundTask {
}
try {
return await context.Actor.Request(new InstanceActor.BackupInstanceCommand(backupManager, CancellationToken));
return await context.Actor.Request(new InstanceActor.BackupInstanceCommand(backupManager));
} finally {
backupSemaphore.Release();
}

View File

@ -13,28 +13,32 @@ using Phantom.Utils.Logging;
namespace Phantom.Agent.Services.Instances;
sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
public readonly record struct Init(Guid InstanceGuid, string ShortName, InstanceServices Services, AgentState AgentState);
public readonly record struct Init(AgentState AgentState, Guid InstanceGuid, string ShortName, InstanceServices InstanceServices, InstanceTicketManager InstanceTicketManager, CancellationToken ShutdownCancellationToken);
public static Props<ICommand> Factory(Init init) {
return Props<ICommand>.Create(() => new InstanceActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume, MailboxType = UnboundedJumpAheadMailbox.Name });
}
private readonly Guid instanceGuid;
private readonly InstanceServices services;
private readonly AgentState agentState;
private readonly CancellationToken shutdownCancellationToken;
private readonly Guid instanceGuid;
private readonly InstanceServices instanceServices;
private readonly InstanceTicketManager instanceTicketManager;
private readonly InstanceContext context;
private IInstanceStatus currentStatus = InstanceStatus.NotRunning;
private InstanceRunningState? runningState = null;
private InstanceActor(Init init) {
this.instanceGuid = init.InstanceGuid;
this.services = init.Services;
this.agentState = init.AgentState;
this.instanceGuid = init.InstanceGuid;
this.instanceServices = init.InstanceServices;
this.instanceTicketManager = init.InstanceTicketManager;
this.shutdownCancellationToken = init.ShutdownCancellationToken;
var logger = PhantomLogger.Create<InstanceActor>(init.ShortName);
this.context = new InstanceContext(instanceGuid, init.ShortName, logger, services, SelfTyped);
this.context = new InstanceContext(instanceGuid, init.ShortName, logger, instanceServices, SelfTyped);
Receive<ReportInstanceStatusCommand>(ReportInstanceStatus);
ReceiveAsync<LaunchInstanceCommand>(LaunchInstance);
@ -52,7 +56,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
private void ReportCurrentStatus() {
agentState.UpdateInstance(new Instance(instanceGuid, currentStatus));
services.ControllerConnection.Send(new ReportInstanceStatusMessage(instanceGuid, currentStatus));
instanceServices.ControllerConnection.Send(new ReportInstanceStatusMessage(instanceGuid, currentStatus));
}
private void TransitionState(InstanceRunningState? newState) {
@ -60,10 +64,6 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
return;
}
if (runningState is not null && newState is null) {
context.Services.InstanceTicketManager.Release(runningState.Ticket);
}
runningState?.Dispose();
runningState = newState;
runningState?.Initialize();
@ -73,13 +73,13 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
public sealed record ReportInstanceStatusCommand : ICommand;
public sealed record LaunchInstanceCommand(InstanceConfiguration Configuration, IServerLauncher Launcher, InstanceTicketManager.Ticket Ticket, bool IsRestarting, CancellationToken CancellationToken) : ICommand;
public sealed record LaunchInstanceCommand(InstanceConfiguration Configuration, IServerLauncher Launcher, InstanceTicketManager.Ticket Ticket, bool IsRestarting) : ICommand;
public sealed record StopInstanceCommand(MinecraftStopStrategy StopStrategy, CancellationToken CancellationToken) : ICommand;
public sealed record StopInstanceCommand(MinecraftStopStrategy StopStrategy) : ICommand;
public sealed record SendCommandToInstanceCommand(string Command, CancellationToken CancellationToken) : ICommand, ICanReply<SendCommandToInstanceResult>;
public sealed record SendCommandToInstanceCommand(string Command) : ICommand, ICanReply<SendCommandToInstanceResult>;
public sealed record BackupInstanceCommand(BackupManager BackupManager, CancellationToken CancellationToken) : ICommand, ICanReply<BackupCreationResult>;
public sealed record BackupInstanceCommand(BackupManager BackupManager) : ICommand, ICanReply<BackupCreationResult>;
public sealed record HandleProcessEndedCommand(IInstanceStatus Status) : ICommand, IJumpAhead;
@ -93,9 +93,9 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
if (command.IsRestarting || runningState is null) {
SetAndReportStatus(command.IsRestarting ? InstanceStatus.Restarting : InstanceStatus.Launching);
var newState = await InstanceLaunchProcedure.Run(context, command.Configuration, command.Launcher, command.Ticket, SetAndReportStatus, command.CancellationToken);
var newState = await InstanceLaunchProcedure.Run(context, command.Configuration, command.Launcher, instanceTicketManager, command.Ticket, SetAndReportStatus, shutdownCancellationToken);
if (newState is null) {
context.Services.InstanceTicketManager.Release(command.Ticket);
instanceTicketManager.Release(command.Ticket);
}
TransitionState(newState);
@ -110,7 +110,8 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
IInstanceStatus oldStatus = currentStatus;
SetAndReportStatus(InstanceStatus.Stopping);
if (await InstanceStopProcedure.Run(context, command.StopStrategy, runningState, SetAndReportStatus, command.CancellationToken)) {
if (await InstanceStopProcedure.Run(context, command.StopStrategy, runningState, SetAndReportStatus, shutdownCancellationToken)) {
instanceTicketManager.Release(runningState.Ticket);
TransitionState(null);
}
else {
@ -123,7 +124,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
return SendCommandToInstanceResult.InstanceNotRunning;
}
else {
return await runningState.SendCommand(command.Command, command.CancellationToken);
return await runningState.SendCommand(command.Command, shutdownCancellationToken);
}
}
@ -132,7 +133,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
return new BackupCreationResult(BackupCreationResultKind.InstanceNotRunning);
}
else {
return await command.BackupManager.CreateBackup(context.ShortName, runningState.Process, command.CancellationToken);
return await command.BackupManager.CreateBackup(context.ShortName, runningState.Process, shutdownCancellationToken);
}
}
@ -140,12 +141,13 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
if (runningState is { Process.HasEnded: true }) {
SetAndReportStatus(command.Status);
context.ReportEvent(InstanceEvent.Stopped);
instanceTicketManager.Release(runningState.Ticket);
TransitionState(null);
}
}
private async Task Shutdown(ShutdownCommand command) {
await StopInstance(new StopInstanceCommand(MinecraftStopStrategy.Instant, CancellationToken.None));
await StopInstance(new StopInstanceCommand(MinecraftStopStrategy.Instant));
Context.Stop(Self);
}
}

View File

@ -20,7 +20,7 @@ namespace Phantom.Agent.Services.Instances;
sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand> {
private static readonly ILogger Logger = PhantomLogger.Create<InstanceManagerActor>();
public readonly record struct Init(ControllerConnection ControllerConnection, AgentFolders AgentFolders, AgentState AgentState, JavaRuntimeRepository JavaRuntimeRepository, InstanceTicketManager TicketManager, TaskManager TaskManager, BackupManager BackupManager);
public readonly record struct Init(ControllerConnection ControllerConnection, AgentFolders AgentFolders, AgentState AgentState, JavaRuntimeRepository JavaRuntimeRepository, InstanceTicketManager InstanceTicketManager, TaskManager TaskManager, BackupManager BackupManager);
public static Props<ICommand> Factory(Init init) {
return Props<ICommand>.Create(() => new InstanceManagerActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
@ -30,6 +30,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
private readonly string basePath;
private readonly InstanceServices instanceServices;
private readonly InstanceTicketManager instanceTicketManager;
private readonly Dictionary<Guid, InstanceInfo> instances = new ();
private readonly CancellationTokenSource shutdownCancellationTokenSource = new ();
@ -40,12 +41,13 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
private InstanceManagerActor(Init init) {
this.agentState = init.AgentState;
this.basePath = init.AgentFolders.InstancesFolderPath;
this.instanceTicketManager = init.InstanceTicketManager;
this.shutdownCancellationToken = shutdownCancellationTokenSource.Token;
var minecraftServerExecutables = new MinecraftServerExecutables(init.AgentFolders.ServerExecutableFolderPath);
var launchServices = new LaunchServices(minecraftServerExecutables, init.JavaRuntimeRepository);
this.instanceServices = new InstanceServices(init.ControllerConnection, init.TaskManager, init.TicketManager, init.BackupManager, launchServices);
this.instanceServices = new InstanceServices(init.ControllerConnection, init.TaskManager, init.BackupManager, launchServices);
ReceiveAndReply<ConfigureInstanceCommand, InstanceActionResult<ConfigureInstanceResult>>(ConfigureInstance);
ReceiveAndReply<LaunchInstanceCommand, InstanceActionResult<LaunchInstanceResult>>(LaunchInstance);
@ -116,7 +118,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
}
}
else {
var instanceInit = new InstanceActor.Init(instanceGuid, GetInstanceLoggerName(instanceGuid), instanceServices, agentState);
var instanceInit = new InstanceActor.Init(agentState, instanceGuid, GetInstanceLoggerName(instanceGuid), instanceServices, instanceTicketManager, shutdownCancellationToken);
instances[instanceGuid] = instance = new InstanceInfo(Context.ActorOf(InstanceActor.Factory(instanceInit), "Instance-" + instanceGuid), configuration, launcher);
Logger.Information("Created instance \"{Name}\" (GUID {Guid}).", configuration.InstanceName, instanceGuid);
@ -137,7 +139,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
return InstanceActionResult.General<LaunchInstanceResult>(InstanceActionGeneralResult.InstanceDoesNotExist);
}
var ticket = instanceServices.InstanceTicketManager.Reserve(instanceInfo.Configuration);
var ticket = instanceTicketManager.Reserve(instanceInfo.Configuration);
if (ticket is Result<InstanceTicketManager.Ticket, LaunchInstanceResult>.Fail fail) {
return InstanceActionResult.Concrete(fail.Error);
}
@ -153,7 +155,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
}
// TODO report status?
instanceInfo.Actor.Tell(new InstanceActor.LaunchInstanceCommand(instanceInfo.Configuration, instanceInfo.Launcher, ticket.Value, IsRestarting: false, shutdownCancellationToken));
instanceInfo.Actor.Tell(new InstanceActor.LaunchInstanceCommand(instanceInfo.Configuration, instanceInfo.Launcher, ticket.Value, IsRestarting: false));
return InstanceActionResult.Concrete(LaunchInstanceResult.LaunchInitiated);
}
@ -173,7 +175,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
}
}
instanceInfo.Actor.Tell(new InstanceActor.StopInstanceCommand(command.StopStrategy, shutdownCancellationToken));
instanceInfo.Actor.Tell(new InstanceActor.StopInstanceCommand(command.StopStrategy));
return InstanceActionResult.Concrete(StopInstanceResult.StopInitiated);
}
@ -184,7 +186,7 @@ sealed class InstanceManagerActor : ReceiveActor<InstanceManagerActor.ICommand>
}
try {
return InstanceActionResult.Concrete(await instanceInfo.Actor.Request(new InstanceActor.SendCommandToInstanceCommand(command.Command, shutdownCancellationToken), shutdownCancellationToken));
return InstanceActionResult.Concrete(await instanceInfo.Actor.Request(new InstanceActor.SendCommandToInstanceCommand(command.Command), shutdownCancellationToken));
} catch (OperationCanceledException) {
return InstanceActionResult.General<SendCommandToInstanceResult>(InstanceActionGeneralResult.AgentShuttingDown);
}

View File

@ -5,4 +5,4 @@ using Phantom.Utils.Tasks;
namespace Phantom.Agent.Services.Instances;
sealed record InstanceServices(ControllerConnection ControllerConnection, TaskManager TaskManager, InstanceTicketManager InstanceTicketManager, BackupManager BackupManager, LaunchServices LaunchServices);
sealed record InstanceServices(ControllerConnection ControllerConnection, TaskManager TaskManager, BackupManager BackupManager, LaunchServices LaunchServices);

View File

@ -7,12 +7,12 @@ using Phantom.Utils.Tasks;
namespace Phantom.Agent.Services.Instances.State;
static class InstanceLaunchProcedure {
public static async Task<InstanceRunningState?> Run(InstanceContext context, InstanceConfiguration configuration, IServerLauncher launcher, InstanceTicketManager.Ticket ticket, Action<IInstanceStatus> reportStatus, CancellationToken cancellationToken) {
public static async Task<InstanceRunningState?> Run(InstanceContext context, InstanceConfiguration configuration, IServerLauncher launcher, InstanceTicketManager ticketManager, InstanceTicketManager.Ticket ticket, Action<IInstanceStatus> reportStatus, CancellationToken cancellationToken) {
context.Logger.Information("Session starting...");
Result<InstanceProcess, InstanceLaunchFailReason> result;
if (context.Services.InstanceTicketManager.IsValid(ticket)) {
if (ticketManager.IsValid(ticket)) {
try {
result = await LaunchInstance(context, launcher, reportStatus, cancellationToken);
} catch (OperationCanceledException) {

View File

@ -72,7 +72,7 @@ sealed class InstanceRunningState : IDisposable {
else {
context.Logger.Information("Session ended unexpectedly, restarting...");
context.ReportEvent(InstanceEvent.Crashed);
context.Actor.Tell(new InstanceActor.LaunchInstanceCommand(configuration, launcher, Ticket, IsRestarting: true, cancellationToken));
context.Actor.Tell(new InstanceActor.LaunchInstanceCommand(configuration, launcher, Ticket, IsRestarting: true));
}
}

View File

@ -75,6 +75,8 @@ static class InstanceStopProcedure {
// Ignore.
} catch (ObjectDisposedException e) when (e.ObjectName == typeof(Process).FullName && process.HasEnded) {
// Ignore.
} catch (IOException e) when (e.HResult == -2147024664 /* The pipe is being closed */) {
// Ignore.
} catch (Exception e) {
context.Logger.Warning(e, "Caught exception while sending stop command.");
}