mirror of
https://github.com/chylex/Minecraft-Phantom-Panel.git
synced 2024-11-25 07:42:58 +01:00
Compare commits
2 Commits
873d036895
...
02828a91c6
Author | SHA1 | Date | |
---|---|---|---|
02828a91c6 | |||
7cdb0a1910 |
@ -8,9 +8,9 @@ namespace Phantom.Agent.Rpc;
|
|||||||
public sealed class ControllerConnection {
|
public sealed class ControllerConnection {
|
||||||
private static readonly ILogger Logger = PhantomLogger.Create(nameof(ControllerConnection));
|
private static readonly ILogger Logger = PhantomLogger.Create(nameof(ControllerConnection));
|
||||||
|
|
||||||
private readonly RpcConnectionToServer<IMessageToControllerListener> connection;
|
private readonly RpcConnectionToServer<IMessageToController> connection;
|
||||||
|
|
||||||
public ControllerConnection(RpcConnectionToServer<IMessageToControllerListener> connection) {
|
public ControllerConnection(RpcConnectionToServer<IMessageToController> connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
Logger.Information("Connection ready.");
|
Logger.Information("Connection ready.");
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,10 @@ sealed class KeepAliveLoop {
|
|||||||
|
|
||||||
private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromSeconds(10);
|
private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
private readonly RpcConnectionToServer<IMessageToControllerListener> connection;
|
private readonly RpcConnectionToServer<IMessageToController> connection;
|
||||||
private readonly CancellationTokenSource cancellationTokenSource = new ();
|
private readonly CancellationTokenSource cancellationTokenSource = new ();
|
||||||
|
|
||||||
public KeepAliveLoop(RpcConnectionToServer<IMessageToControllerListener> connection) {
|
public KeepAliveLoop(RpcConnectionToServer<IMessageToController> connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
Task.Run(Run);
|
Task.Run(Run);
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,21 @@ using NetMQ.Sockets;
|
|||||||
using Phantom.Common.Messages.Agent;
|
using Phantom.Common.Messages.Agent;
|
||||||
using Phantom.Common.Messages.Agent.BiDirectional;
|
using Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
using Phantom.Common.Messages.Agent.ToController;
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Rpc.Runtime;
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
using Phantom.Utils.Rpc.Sockets;
|
using Phantom.Utils.Rpc.Sockets;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Phantom.Agent.Rpc;
|
namespace Phantom.Agent.Rpc;
|
||||||
|
|
||||||
public sealed class RpcClientRuntime : RpcClientRuntime<IMessageToAgentListener, IMessageToControllerListener, ReplyMessage> {
|
public sealed class RpcClientRuntime : RpcClientRuntime<IMessageToAgent, IMessageToController, ReplyMessage> {
|
||||||
public static Task Launch(RpcClientSocket<IMessageToAgentListener, IMessageToControllerListener, ReplyMessage> socket, IMessageToAgentListener messageListener, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) {
|
public static Task Launch(RpcClientSocket<IMessageToAgent, IMessageToController, ReplyMessage> socket, ActorRef<IMessageToAgent> handlerActorRef, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) {
|
||||||
return new RpcClientRuntime(socket, messageListener, disconnectSemaphore, receiveCancellationToken).Launch();
|
return new RpcClientRuntime(socket, handlerActorRef, disconnectSemaphore, receiveCancellationToken).Launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
private RpcClientRuntime(RpcClientSocket<IMessageToAgentListener, IMessageToControllerListener, ReplyMessage> socket, IMessageToAgentListener messageListener, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) : base(socket, messageListener, disconnectSemaphore, receiveCancellationToken) {}
|
private RpcClientRuntime(RpcClientSocket<IMessageToAgent, IMessageToController, ReplyMessage> socket, ActorRef<IMessageToAgent> handlerActor, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) : base(socket, handlerActor, disconnectSemaphore, receiveCancellationToken) {}
|
||||||
|
|
||||||
protected override async Task RunWithConnection(ClientSocket socket, RpcConnectionToServer<IMessageToControllerListener> connection) {
|
protected override async Task RunWithConnection(ClientSocket socket, RpcConnectionToServer<IMessageToController> connection) {
|
||||||
var keepAliveLoop = new KeepAliveLoop(connection);
|
var keepAliveLoop = new KeepAliveLoop(connection);
|
||||||
try {
|
try {
|
||||||
await base.RunWithConnection(socket, connection);
|
await base.RunWithConnection(socket, connection);
|
||||||
|
@ -4,27 +4,41 @@ using Phantom.Common.Messages.Agent;
|
|||||||
using Phantom.Common.Messages.Agent.BiDirectional;
|
using Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
using Phantom.Common.Messages.Agent.ToAgent;
|
using Phantom.Common.Messages.Agent.ToAgent;
|
||||||
using Phantom.Common.Messages.Agent.ToController;
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Logging;
|
using Phantom.Utils.Logging;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
using Phantom.Utils.Rpc.Runtime;
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Phantom.Agent.Services.Rpc;
|
namespace Phantom.Agent.Services.Rpc;
|
||||||
|
|
||||||
public sealed class MessageListener : IMessageToAgentListener {
|
public sealed class ControllerMessageHandlerActor : ReceiveActor<IMessageToAgent> {
|
||||||
private static ILogger Logger { get; } = PhantomLogger.Create<MessageListener>();
|
private static ILogger Logger { get; } = PhantomLogger.Create<ControllerMessageHandlerActor>();
|
||||||
|
|
||||||
private readonly RpcConnectionToServer<IMessageToControllerListener> connection;
|
public readonly record struct Init(RpcConnectionToServer<IMessageToController> Connection, AgentServices Agent, CancellationTokenSource ShutdownTokenSource);
|
||||||
|
|
||||||
|
public static Props<IMessageToAgent> Factory(Init init) {
|
||||||
|
return Props<IMessageToAgent>.Create(() => new ControllerMessageHandlerActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly RpcConnectionToServer<IMessageToController> connection;
|
||||||
private readonly AgentServices agent;
|
private readonly AgentServices agent;
|
||||||
private readonly CancellationTokenSource shutdownTokenSource;
|
private readonly CancellationTokenSource shutdownTokenSource;
|
||||||
|
|
||||||
public MessageListener(RpcConnectionToServer<IMessageToControllerListener> connection, AgentServices agent, CancellationTokenSource shutdownTokenSource) {
|
private ControllerMessageHandlerActor(Init init) {
|
||||||
this.connection = connection;
|
this.connection = init.Connection;
|
||||||
this.agent = agent;
|
this.agent = init.Agent;
|
||||||
this.shutdownTokenSource = shutdownTokenSource;
|
this.shutdownTokenSource = init.ShutdownTokenSource;
|
||||||
|
|
||||||
|
ReceiveAsync<RegisterAgentSuccessMessage>(HandleRegisterAgentSuccess);
|
||||||
|
Receive<RegisterAgentFailureMessage>(HandleRegisterAgentFailure);
|
||||||
|
ReceiveAndReplyLater<ConfigureInstanceMessage, InstanceActionResult<ConfigureInstanceResult>>(HandleConfigureInstance);
|
||||||
|
ReceiveAndReplyLater<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(HandleLaunchInstance);
|
||||||
|
ReceiveAndReplyLater<StopInstanceMessage, InstanceActionResult<StopInstanceResult>>(HandleStopInstance);
|
||||||
|
ReceiveAndReplyLater<SendCommandToInstanceMessage, InstanceActionResult<SendCommandToInstanceResult>>(HandleSendCommandToInstance);
|
||||||
|
Receive<ReplyMessage>(HandleReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<NoReply> HandleRegisterAgentSuccess(RegisterAgentSuccessMessage message) {
|
private async Task HandleRegisterAgentSuccess(RegisterAgentSuccessMessage message) {
|
||||||
Logger.Information("Agent authentication successful.");
|
Logger.Information("Agent authentication successful.");
|
||||||
|
|
||||||
void ShutdownAfterConfigurationFailed(Guid instanceGuid, InstanceConfiguration configuration) {
|
void ShutdownAfterConfigurationFailed(Guid instanceGuid, InstanceConfiguration configuration) {
|
||||||
@ -36,7 +50,7 @@ public sealed class MessageListener : IMessageToAgentListener {
|
|||||||
var result = await HandleConfigureInstance(configureInstanceMessage, alwaysReportStatus: true);
|
var result = await HandleConfigureInstance(configureInstanceMessage, alwaysReportStatus: true);
|
||||||
if (!result.Is(ConfigureInstanceResult.Success)) {
|
if (!result.Is(ConfigureInstanceResult.Success)) {
|
||||||
ShutdownAfterConfigurationFailed(configureInstanceMessage.InstanceGuid, configureInstanceMessage.Configuration);
|
ShutdownAfterConfigurationFailed(configureInstanceMessage.InstanceGuid, configureInstanceMessage.Configuration);
|
||||||
return NoReply.Instance;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,11 +58,9 @@ public sealed class MessageListener : IMessageToAgentListener {
|
|||||||
|
|
||||||
await connection.Send(new AdvertiseJavaRuntimesMessage(agent.JavaRuntimeRepository.All));
|
await connection.Send(new AdvertiseJavaRuntimesMessage(agent.JavaRuntimeRepository.All));
|
||||||
await agent.InstanceSessionManager.RefreshAgentStatus();
|
await agent.InstanceSessionManager.RefreshAgentStatus();
|
||||||
|
|
||||||
return NoReply.Instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<NoReply> HandleRegisterAgentFailure(RegisterAgentFailureMessage message) {
|
private void HandleRegisterAgentFailure(RegisterAgentFailureMessage message) {
|
||||||
string errorMessage = message.FailureKind switch {
|
string errorMessage = message.FailureKind switch {
|
||||||
RegisterAgentFailure.ConnectionAlreadyHasAnAgent => "This connection already has an associated agent.",
|
RegisterAgentFailure.ConnectionAlreadyHasAnAgent => "This connection already has an associated agent.",
|
||||||
RegisterAgentFailure.InvalidToken => "Invalid token.",
|
RegisterAgentFailure.InvalidToken => "Invalid token.",
|
||||||
@ -59,32 +71,29 @@ public sealed class MessageListener : IMessageToAgentListener {
|
|||||||
|
|
||||||
PhantomLogger.Dispose();
|
PhantomLogger.Dispose();
|
||||||
Environment.Exit(1);
|
Environment.Exit(1);
|
||||||
|
|
||||||
return Task.FromResult(NoReply.Instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<InstanceActionResult<ConfigureInstanceResult>> HandleConfigureInstance(ConfigureInstanceMessage message, bool alwaysReportStatus) {
|
private Task<InstanceActionResult<ConfigureInstanceResult>> HandleConfigureInstance(ConfigureInstanceMessage message, bool alwaysReportStatus) {
|
||||||
return agent.InstanceSessionManager.Configure(message.InstanceGuid, message.Configuration, message.LaunchProperties, message.LaunchNow, alwaysReportStatus);
|
return agent.InstanceSessionManager.Configure(message.InstanceGuid, message.Configuration, message.LaunchProperties, message.LaunchNow, alwaysReportStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<InstanceActionResult<ConfigureInstanceResult>> HandleConfigureInstance(ConfigureInstanceMessage message) {
|
private async Task<InstanceActionResult<ConfigureInstanceResult>> HandleConfigureInstance(ConfigureInstanceMessage message) {
|
||||||
return await HandleConfigureInstance(message, alwaysReportStatus: false);
|
return await HandleConfigureInstance(message, alwaysReportStatus: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
private async Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
||||||
return await agent.InstanceSessionManager.Launch(message.InstanceGuid);
|
return await agent.InstanceSessionManager.Launch(message.InstanceGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<InstanceActionResult<StopInstanceResult>> HandleStopInstance(StopInstanceMessage message) {
|
private async Task<InstanceActionResult<StopInstanceResult>> HandleStopInstance(StopInstanceMessage message) {
|
||||||
return await agent.InstanceSessionManager.Stop(message.InstanceGuid, message.StopStrategy);
|
return await agent.InstanceSessionManager.Stop(message.InstanceGuid, message.StopStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<InstanceActionResult<SendCommandToInstanceResult>> HandleSendCommandToInstance(SendCommandToInstanceMessage message) {
|
private async Task<InstanceActionResult<SendCommandToInstanceResult>> HandleSendCommandToInstance(SendCommandToInstanceMessage message) {
|
||||||
return await agent.InstanceSessionManager.SendCommand(message.InstanceGuid, message.Command);
|
return await agent.InstanceSessionManager.SendCommand(message.InstanceGuid, message.Command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<NoReply> HandleReply(ReplyMessage message) {
|
private void HandleReply(ReplyMessage message) {
|
||||||
connection.Receive(message);
|
connection.Receive(message);
|
||||||
return Task.FromResult(NoReply.Instance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,7 @@ using Phantom.Agent.Services.Rpc;
|
|||||||
using Phantom.Common.Data.Agent;
|
using Phantom.Common.Data.Agent;
|
||||||
using Phantom.Common.Messages.Agent;
|
using Phantom.Common.Messages.Agent;
|
||||||
using Phantom.Common.Messages.Agent.ToController;
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Logging;
|
using Phantom.Utils.Logging;
|
||||||
using Phantom.Utils.Rpc;
|
using Phantom.Utils.Rpc;
|
||||||
using Phantom.Utils.Rpc.Sockets;
|
using Phantom.Utils.Rpc.Sockets;
|
||||||
@ -51,15 +52,19 @@ try {
|
|||||||
|
|
||||||
PhantomLogger.Root.InformationHeading("Launching Phantom Panel agent...");
|
PhantomLogger.Root.InformationHeading("Launching Phantom Panel agent...");
|
||||||
|
|
||||||
var rpcConfiguration = new RpcConfiguration("Rpc", controllerHost, controllerPort, controllerCertificate);
|
var rpcConfiguration = new RpcConfiguration("Agent", controllerHost, controllerPort, controllerCertificate);
|
||||||
var rpcSocket = RpcClientSocket.Connect(rpcConfiguration, AgentMessageRegistries.Definitions, new RegisterAgentMessage(agentToken, agentInfo));
|
var rpcSocket = RpcClientSocket.Connect(rpcConfiguration, AgentMessageRegistries.Definitions, new RegisterAgentMessage(agentToken, agentInfo));
|
||||||
|
|
||||||
var agentServices = new AgentServices(agentInfo, folders, new AgentServiceConfiguration(maxConcurrentBackupCompressionTasks), new ControllerConnection(rpcSocket.Connection));
|
var agentServices = new AgentServices(agentInfo, folders, new AgentServiceConfiguration(maxConcurrentBackupCompressionTasks), new ControllerConnection(rpcSocket.Connection));
|
||||||
await agentServices.Initialize();
|
await agentServices.Initialize();
|
||||||
|
|
||||||
|
using var actorSystem = ActorSystemFactory.Create("Agent");
|
||||||
|
|
||||||
|
var rpcMessageHandlerInit = new ControllerMessageHandlerActor.Init(rpcSocket.Connection, agentServices, shutdownCancellationTokenSource);
|
||||||
|
var rpcMessageHandlerActor = actorSystem.ActorOf(ControllerMessageHandlerActor.Factory(rpcMessageHandlerInit), "ControllerMessageHandler");
|
||||||
|
|
||||||
var rpcDisconnectSemaphore = new SemaphoreSlim(0, 1);
|
var rpcDisconnectSemaphore = new SemaphoreSlim(0, 1);
|
||||||
var rpcMessageListener = new MessageListener(rpcSocket.Connection, agentServices, shutdownCancellationTokenSource);
|
var rpcTask = RpcClientRuntime.Launch(rpcSocket, rpcMessageHandlerActor, rpcDisconnectSemaphore, shutdownCancellationToken);
|
||||||
var rpcTask = RpcClientRuntime.Launch(rpcSocket, rpcMessageListener, rpcDisconnectSemaphore, shutdownCancellationToken);
|
|
||||||
try {
|
try {
|
||||||
await rpcTask.WaitAsync(shutdownCancellationToken);
|
await rpcTask.WaitAsync(shutdownCancellationToken);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -8,10 +8,10 @@ using Phantom.Utils.Rpc.Message;
|
|||||||
namespace Phantom.Common.Messages.Agent;
|
namespace Phantom.Common.Messages.Agent;
|
||||||
|
|
||||||
public static class AgentMessageRegistries {
|
public static class AgentMessageRegistries {
|
||||||
public static MessageRegistry<IMessageToAgentListener> ToAgent { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToAgent)));
|
public static MessageRegistry<IMessageToAgent> ToAgent { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToAgent)));
|
||||||
public static MessageRegistry<IMessageToControllerListener> ToController { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToController)));
|
public static MessageRegistry<IMessageToController> ToController { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToController)));
|
||||||
|
|
||||||
public static IMessageDefinitions<IMessageToAgentListener, IMessageToControllerListener, ReplyMessage> Definitions { get; } = new MessageDefinitions();
|
public static IMessageDefinitions<IMessageToAgent, IMessageToController, ReplyMessage> Definitions { get; } = new MessageDefinitions();
|
||||||
|
|
||||||
static AgentMessageRegistries() {
|
static AgentMessageRegistries() {
|
||||||
ToAgent.Add<RegisterAgentSuccessMessage>(0);
|
ToAgent.Add<RegisterAgentSuccessMessage>(0);
|
||||||
@ -33,13 +33,9 @@ public static class AgentMessageRegistries {
|
|||||||
ToController.Add<ReplyMessage>(127);
|
ToController.Add<ReplyMessage>(127);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class MessageDefinitions : IMessageDefinitions<IMessageToAgentListener, IMessageToControllerListener, ReplyMessage> {
|
private sealed class MessageDefinitions : IMessageDefinitions<IMessageToAgent, IMessageToController, ReplyMessage> {
|
||||||
public MessageRegistry<IMessageToAgentListener> ToClient => ToAgent;
|
public MessageRegistry<IMessageToAgent> ToClient => ToAgent;
|
||||||
public MessageRegistry<IMessageToControllerListener> ToServer => ToController;
|
public MessageRegistry<IMessageToController> ToServer => ToController;
|
||||||
|
|
||||||
public bool IsRegistrationMessage(Type messageType) {
|
|
||||||
return messageType == typeof(RegisterAgentMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReplyMessage CreateReplyMessage(uint sequenceId, byte[] serializedReply) {
|
public ReplyMessage CreateReplyMessage(uint sequenceId, byte[] serializedReply) {
|
||||||
return new ReplyMessage(sequenceId, serializedReply);
|
return new ReplyMessage(sequenceId, serializedReply);
|
||||||
|
@ -7,17 +7,4 @@ namespace Phantom.Common.Messages.Agent.BiDirectional;
|
|||||||
public sealed partial record ReplyMessage(
|
public sealed partial record ReplyMessage(
|
||||||
[property: MemoryPackOrder(0)] uint SequenceId,
|
[property: MemoryPackOrder(0)] uint SequenceId,
|
||||||
[property: MemoryPackOrder(1)] byte[] SerializedReply
|
[property: MemoryPackOrder(1)] byte[] SerializedReply
|
||||||
) : IMessageToController, IMessageToAgent, IReply {
|
) : IMessageToController, IMessageToAgent, IReply;
|
||||||
private static readonly MessageQueueKey MessageQueueKey = new ("Reply");
|
|
||||||
|
|
||||||
[MemoryPackIgnore]
|
|
||||||
public MessageQueueKey QueueKey => MessageQueueKey;
|
|
||||||
|
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleReply(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<NoReply> Accept(IMessageToAgentListener listener) {
|
|
||||||
return listener.HandleReply(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
using Phantom.Utils.Rpc.Message;
|
namespace Phantom.Common.Messages.Agent;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent;
|
public interface IMessageToAgent {}
|
||||||
|
|
||||||
public interface IMessageToAgent<TReply> : IMessage<IMessageToAgentListener, TReply> {
|
|
||||||
MessageQueueKey IMessage<IMessageToAgentListener, TReply>.QueueKey => IMessageToAgent.DefaultQueueKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IMessageToAgent : IMessageToAgent<NoReply> {
|
|
||||||
internal static readonly MessageQueueKey DefaultQueueKey = new ("Agent.Default");
|
|
||||||
MessageQueueKey IMessage<IMessageToAgentListener, NoReply>.QueueKey => DefaultQueueKey;
|
|
||||||
}
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
using Phantom.Common.Data.Replies;
|
|
||||||
using Phantom.Common.Messages.Agent.BiDirectional;
|
|
||||||
using Phantom.Common.Messages.Agent.ToAgent;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent;
|
|
||||||
|
|
||||||
public interface IMessageToAgentListener {
|
|
||||||
Task<NoReply> HandleRegisterAgentSuccess(RegisterAgentSuccessMessage message);
|
|
||||||
Task<NoReply> HandleRegisterAgentFailure(RegisterAgentFailureMessage message);
|
|
||||||
Task<InstanceActionResult<ConfigureInstanceResult>> HandleConfigureInstance(ConfigureInstanceMessage message);
|
|
||||||
Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message);
|
|
||||||
Task<InstanceActionResult<StopInstanceResult>> HandleStopInstance(StopInstanceMessage message);
|
|
||||||
Task<InstanceActionResult<SendCommandToInstanceResult>> HandleSendCommandToInstance(SendCommandToInstanceMessage message);
|
|
||||||
Task<NoReply> HandleReply(ReplyMessage message);
|
|
||||||
}
|
|
@ -1,12 +1,3 @@
|
|||||||
using Phantom.Utils.Rpc.Message;
|
namespace Phantom.Common.Messages.Agent;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent;
|
public interface IMessageToController {}
|
||||||
|
|
||||||
public interface IMessageToController<TReply> : IMessage<IMessageToControllerListener, TReply> {
|
|
||||||
MessageQueueKey IMessage<IMessageToControllerListener, TReply>.QueueKey => IMessageToController.DefaultQueueKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IMessageToController : IMessageToController<NoReply> {
|
|
||||||
internal static readonly MessageQueueKey DefaultQueueKey = new ("Agent.Default");
|
|
||||||
MessageQueueKey IMessage<IMessageToControllerListener, NoReply>.QueueKey => DefaultQueueKey;
|
|
||||||
}
|
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
using Phantom.Common.Messages.Agent.BiDirectional;
|
|
||||||
using Phantom.Common.Messages.Agent.ToController;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent;
|
|
||||||
|
|
||||||
public interface IMessageToControllerListener {
|
|
||||||
Task<NoReply> HandleRegisterAgent(RegisterAgentMessage message);
|
|
||||||
Task<NoReply> HandleUnregisterAgent(UnregisterAgentMessage message);
|
|
||||||
Task<NoReply> HandleAgentIsAlive(AgentIsAliveMessage message);
|
|
||||||
Task<NoReply> HandleAdvertiseJavaRuntimes(AdvertiseJavaRuntimesMessage message);
|
|
||||||
Task<NoReply> HandleReportAgentStatus(ReportAgentStatusMessage message);
|
|
||||||
Task<NoReply> HandleReportInstanceStatus(ReportInstanceStatusMessage message);
|
|
||||||
Task<NoReply> HandleReportInstanceEvent(ReportInstanceEventMessage message);
|
|
||||||
Task<NoReply> HandleInstanceOutput(InstanceOutputMessage message);
|
|
||||||
Task<NoReply> HandleReply(ReplyMessage message);
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Instance;
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
@ -10,8 +11,4 @@ public sealed partial record ConfigureInstanceMessage(
|
|||||||
[property: MemoryPackOrder(1)] InstanceConfiguration Configuration,
|
[property: MemoryPackOrder(1)] InstanceConfiguration Configuration,
|
||||||
[property: MemoryPackOrder(2)] InstanceLaunchProperties LaunchProperties,
|
[property: MemoryPackOrder(2)] InstanceLaunchProperties LaunchProperties,
|
||||||
[property: MemoryPackOrder(3)] bool LaunchNow = false
|
[property: MemoryPackOrder(3)] bool LaunchNow = false
|
||||||
) : IMessageToAgent<InstanceActionResult<ConfigureInstanceResult>> {
|
) : IMessageToAgent, ICanReply<InstanceActionResult<ConfigureInstanceResult>>;
|
||||||
public Task<InstanceActionResult<ConfigureInstanceResult>> Accept(IMessageToAgentListener listener) {
|
|
||||||
return listener.HandleConfigureInstance(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record LaunchInstanceMessage(
|
public sealed partial record LaunchInstanceMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid InstanceGuid
|
[property: MemoryPackOrder(0)] Guid InstanceGuid
|
||||||
) : IMessageToAgent<InstanceActionResult<LaunchInstanceResult>> {
|
) : IMessageToAgent, ICanReply<InstanceActionResult<LaunchInstanceResult>>;
|
||||||
public Task<InstanceActionResult<LaunchInstanceResult>> Accept(IMessageToAgentListener listener) {
|
|
||||||
return listener.HandleLaunchInstance(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RegisterAgentFailureMessage(
|
public sealed partial record RegisterAgentFailureMessage(
|
||||||
[property: MemoryPackOrder(0)] RegisterAgentFailure FailureKind
|
[property: MemoryPackOrder(0)] RegisterAgentFailure FailureKind
|
||||||
) : IMessageToAgent {
|
) : IMessageToAgent;
|
||||||
public Task<NoReply> Accept(IMessageToAgentListener listener) {
|
|
||||||
return listener.HandleRegisterAgentFailure(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RegisterAgentSuccessMessage(
|
public sealed partial record RegisterAgentSuccessMessage(
|
||||||
[property: MemoryPackOrder(0)] ImmutableArray<ConfigureInstanceMessage> InitialInstanceConfigurations
|
[property: MemoryPackOrder(0)] ImmutableArray<ConfigureInstanceMessage> InitialInstanceConfigurations
|
||||||
) : IMessageToAgent {
|
) : IMessageToAgent;
|
||||||
public Task<NoReply> Accept(IMessageToAgentListener listener) {
|
|
||||||
return listener.HandleRegisterAgentSuccess(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
@ -7,8 +8,4 @@ namespace Phantom.Common.Messages.Agent.ToAgent;
|
|||||||
public sealed partial record SendCommandToInstanceMessage(
|
public sealed partial record SendCommandToInstanceMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(1)] string Command
|
[property: MemoryPackOrder(1)] string Command
|
||||||
) : IMessageToAgent<InstanceActionResult<SendCommandToInstanceResult>> {
|
) : IMessageToAgent, ICanReply<InstanceActionResult<SendCommandToInstanceResult>>;
|
||||||
public Task<InstanceActionResult<SendCommandToInstanceResult>> Accept(IMessageToAgentListener listener) {
|
|
||||||
return listener.HandleSendCommandToInstance(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Minecraft;
|
using Phantom.Common.Data.Minecraft;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToAgent;
|
namespace Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
|
||||||
@ -8,8 +9,4 @@ namespace Phantom.Common.Messages.Agent.ToAgent;
|
|||||||
public sealed partial record StopInstanceMessage(
|
public sealed partial record StopInstanceMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(1)] MinecraftStopStrategy StopStrategy
|
[property: MemoryPackOrder(1)] MinecraftStopStrategy StopStrategy
|
||||||
) : IMessageToAgent<InstanceActionResult<StopInstanceResult>> {
|
) : IMessageToAgent, ICanReply<InstanceActionResult<StopInstanceResult>>;
|
||||||
public Task<InstanceActionResult<StopInstanceResult>> Accept(IMessageToAgentListener listener) {
|
|
||||||
return listener.HandleStopInstance(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Java;
|
using Phantom.Common.Data.Java;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToController;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record AdvertiseJavaRuntimesMessage(
|
public sealed partial record AdvertiseJavaRuntimesMessage(
|
||||||
[property: MemoryPackOrder(0)] ImmutableArray<TaggedJavaRuntime> Runtimes
|
[property: MemoryPackOrder(0)] ImmutableArray<TaggedJavaRuntime> Runtimes
|
||||||
) : IMessageToController {
|
) : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleAdvertiseJavaRuntimes(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToController;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record AgentIsAliveMessage : IMessageToController {
|
public sealed partial record AgentIsAliveMessage : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleAgentIsAlive(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToController;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
@ -8,13 +7,4 @@ namespace Phantom.Common.Messages.Agent.ToController;
|
|||||||
public sealed partial record InstanceOutputMessage(
|
public sealed partial record InstanceOutputMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(1)] ImmutableArray<string> Lines
|
[property: MemoryPackOrder(1)] ImmutableArray<string> Lines
|
||||||
) : IMessageToController {
|
) : IMessageToController;
|
||||||
private static readonly MessageQueueKey MessageQueueKey = new ("Agent.InstanceOutput");
|
|
||||||
|
|
||||||
[MemoryPackIgnore]
|
|
||||||
public MessageQueueKey QueueKey => MessageQueueKey;
|
|
||||||
|
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleInstanceOutput(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data;
|
using Phantom.Common.Data;
|
||||||
using Phantom.Common.Data.Agent;
|
using Phantom.Common.Data.Agent;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToController;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
@ -9,8 +8,4 @@ namespace Phantom.Common.Messages.Agent.ToController;
|
|||||||
public sealed partial record RegisterAgentMessage(
|
public sealed partial record RegisterAgentMessage(
|
||||||
[property: MemoryPackOrder(0)] AuthToken AuthToken,
|
[property: MemoryPackOrder(0)] AuthToken AuthToken,
|
||||||
[property: MemoryPackOrder(1)] AgentInfo AgentInfo
|
[property: MemoryPackOrder(1)] AgentInfo AgentInfo
|
||||||
) : IMessageToController {
|
) : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleRegisterAgent(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data;
|
using Phantom.Common.Data;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToController;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
@ -8,8 +7,4 @@ namespace Phantom.Common.Messages.Agent.ToController;
|
|||||||
public sealed partial record ReportAgentStatusMessage(
|
public sealed partial record ReportAgentStatusMessage(
|
||||||
[property: MemoryPackOrder(0)] int RunningInstanceCount,
|
[property: MemoryPackOrder(0)] int RunningInstanceCount,
|
||||||
[property: MemoryPackOrder(1)] RamAllocationUnits RunningInstanceMemory
|
[property: MemoryPackOrder(1)] RamAllocationUnits RunningInstanceMemory
|
||||||
) : IMessageToController {
|
) : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleReportAgentStatus(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Instance;
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToController;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
@ -10,8 +9,4 @@ public sealed partial record ReportInstanceEventMessage(
|
|||||||
[property: MemoryPackOrder(1)] DateTime UtcTime,
|
[property: MemoryPackOrder(1)] DateTime UtcTime,
|
||||||
[property: MemoryPackOrder(2)] Guid InstanceGuid,
|
[property: MemoryPackOrder(2)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(3)] IInstanceEvent Event
|
[property: MemoryPackOrder(3)] IInstanceEvent Event
|
||||||
) : IMessageToController {
|
) : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleReportInstanceEvent(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Instance;
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToController;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
@ -8,8 +7,4 @@ namespace Phantom.Common.Messages.Agent.ToController;
|
|||||||
public sealed partial record ReportInstanceStatusMessage(
|
public sealed partial record ReportInstanceStatusMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(1)] IInstanceStatus InstanceStatus
|
[property: MemoryPackOrder(1)] IInstanceStatus InstanceStatus
|
||||||
) : IMessageToController {
|
) : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleReportInstanceStatus(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Agent.ToController;
|
namespace Phantom.Common.Messages.Agent.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record UnregisterAgentMessage : IMessageToController {
|
public sealed partial record UnregisterAgentMessage : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleUnregisterAgent(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -7,12 +7,4 @@ namespace Phantom.Common.Messages.Web.BiDirectional;
|
|||||||
public sealed partial record ReplyMessage(
|
public sealed partial record ReplyMessage(
|
||||||
[property: MemoryPackOrder(0)] uint SequenceId,
|
[property: MemoryPackOrder(0)] uint SequenceId,
|
||||||
[property: MemoryPackOrder(1)] byte[] SerializedReply
|
[property: MemoryPackOrder(1)] byte[] SerializedReply
|
||||||
) : IMessageToController, IMessageToWeb, IReply {
|
) : IMessageToController, IMessageToWeb, IReply;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleReply(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<NoReply> Accept(IMessageToWebListener listener) {
|
|
||||||
return listener.HandleReply(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,3 @@
|
|||||||
using Phantom.Utils.Rpc.Message;
|
namespace Phantom.Common.Messages.Web;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web;
|
public interface IMessageToController {}
|
||||||
|
|
||||||
public interface IMessageToController<TReply> : IMessage<IMessageToControllerListener, TReply> {
|
|
||||||
MessageQueueKey IMessage<IMessageToControllerListener, TReply>.QueueKey => IMessageToController.DefaultQueueKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IMessageToController : IMessageToController<NoReply> {
|
|
||||||
internal static readonly MessageQueueKey DefaultQueueKey = new ("Web.Default");
|
|
||||||
MessageQueueKey IMessage<IMessageToControllerListener, NoReply>.QueueKey => DefaultQueueKey;
|
|
||||||
}
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
using System.Collections.Immutable;
|
|
||||||
using Phantom.Common.Data.Java;
|
|
||||||
using Phantom.Common.Data.Minecraft;
|
|
||||||
using Phantom.Common.Data.Replies;
|
|
||||||
using Phantom.Common.Data.Web.AuditLog;
|
|
||||||
using Phantom.Common.Data.Web.EventLog;
|
|
||||||
using Phantom.Common.Data.Web.Instance;
|
|
||||||
using Phantom.Common.Data.Web.Users;
|
|
||||||
using Phantom.Common.Messages.Web.BiDirectional;
|
|
||||||
using Phantom.Common.Messages.Web.ToController;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web;
|
|
||||||
|
|
||||||
public interface IMessageToControllerListener {
|
|
||||||
Task<NoReply> HandleRegisterWeb(RegisterWebMessage message);
|
|
||||||
Task<NoReply> HandleUnregisterWeb(UnregisterWebMessage message);
|
|
||||||
Task<LogInSuccess?> HandleLogIn(LogInMessage message);
|
|
||||||
Task<CreateOrUpdateAdministratorUserResult> HandleCreateOrUpdateAdministratorUser(CreateOrUpdateAdministratorUserMessage message);
|
|
||||||
Task<CreateUserResult> HandleCreateUser(CreateUserMessage message);
|
|
||||||
Task<ImmutableArray<UserInfo>> HandleGetUsers(GetUsersMessage message);
|
|
||||||
Task<ImmutableArray<RoleInfo>> HandleGetRoles(GetRolesMessage message);
|
|
||||||
Task<ImmutableDictionary<Guid, ImmutableArray<Guid>>> HandleGetUserRoles(GetUserRolesMessage message);
|
|
||||||
Task<ChangeUserRolesResult> HandleChangeUserRoles(ChangeUserRolesMessage message);
|
|
||||||
Task<DeleteUserResult> HandleDeleteUser(DeleteUserMessage message);
|
|
||||||
Task<InstanceActionResult<CreateOrUpdateInstanceResult>> HandleCreateOrUpdateInstance(CreateOrUpdateInstanceMessage message);
|
|
||||||
Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message);
|
|
||||||
Task<InstanceActionResult<StopInstanceResult>> HandleStopInstance(StopInstanceMessage message);
|
|
||||||
Task<InstanceActionResult<SendCommandToInstanceResult>> HandleSendCommandToInstance(SendCommandToInstanceMessage message);
|
|
||||||
Task<ImmutableArray<MinecraftVersion>> HandleGetMinecraftVersions(GetMinecraftVersionsMessage message);
|
|
||||||
Task<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>> HandleGetAgentJavaRuntimes(GetAgentJavaRuntimesMessage message);
|
|
||||||
Task<ImmutableArray<AuditLogItem>> HandleGetAuditLog(GetAuditLogMessage message);
|
|
||||||
Task<ImmutableArray<EventLogItem>> HandleGetEventLog(GetEventLogMessage message);
|
|
||||||
Task<NoReply> HandleReply(ReplyMessage message);
|
|
||||||
}
|
|
@ -1,12 +1,3 @@
|
|||||||
using Phantom.Utils.Rpc.Message;
|
namespace Phantom.Common.Messages.Web;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web;
|
public interface IMessageToWeb {}
|
||||||
|
|
||||||
public interface IMessageToWeb<TReply> : IMessage<IMessageToWebListener, TReply> {
|
|
||||||
MessageQueueKey IMessage<IMessageToWebListener, TReply>.QueueKey => IMessageToWeb.DefaultQueueKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IMessageToWeb : IMessageToWeb<NoReply> {
|
|
||||||
internal static readonly MessageQueueKey DefaultQueueKey = new ("Web.Default");
|
|
||||||
MessageQueueKey IMessage<IMessageToWebListener, NoReply>.QueueKey => DefaultQueueKey;
|
|
||||||
}
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
using Phantom.Common.Messages.Web.BiDirectional;
|
|
||||||
using Phantom.Common.Messages.Web.ToWeb;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web;
|
|
||||||
|
|
||||||
public interface IMessageToWebListener {
|
|
||||||
Task<NoReply> HandleRegisterWebResult(RegisterWebResultMessage message);
|
|
||||||
Task<NoReply> HandleRefreshAgents(RefreshAgentsMessage message);
|
|
||||||
Task<NoReply> HandleRefreshInstances(RefreshInstancesMessage message);
|
|
||||||
Task<NoReply> HandleInstanceOutput(InstanceOutputMessage message);
|
|
||||||
Task<NoReply> HandleReply(ReplyMessage message);
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -10,8 +11,4 @@ public sealed partial record ChangeUserRolesMessage(
|
|||||||
[property: MemoryPackOrder(1)] Guid SubjectUserGuid,
|
[property: MemoryPackOrder(1)] Guid SubjectUserGuid,
|
||||||
[property: MemoryPackOrder(2)] ImmutableHashSet<Guid> AddToRoleGuids,
|
[property: MemoryPackOrder(2)] ImmutableHashSet<Guid> AddToRoleGuids,
|
||||||
[property: MemoryPackOrder(3)] ImmutableHashSet<Guid> RemoveFromRoleGuids
|
[property: MemoryPackOrder(3)] ImmutableHashSet<Guid> RemoveFromRoleGuids
|
||||||
) : IMessageToController<ChangeUserRolesResult> {
|
) : IMessageToController, ICanReply<ChangeUserRolesResult>;
|
||||||
public Task<ChangeUserRolesResult> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleChangeUserRoles(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -7,8 +8,4 @@ namespace Phantom.Common.Messages.Web.ToController;
|
|||||||
public sealed partial record CreateOrUpdateAdministratorUserMessage(
|
public sealed partial record CreateOrUpdateAdministratorUserMessage(
|
||||||
[property: MemoryPackOrder(0)] string Username,
|
[property: MemoryPackOrder(0)] string Username,
|
||||||
[property: MemoryPackOrder(1)] string Password
|
[property: MemoryPackOrder(1)] string Password
|
||||||
) : IMessageToController<CreateOrUpdateAdministratorUserResult> {
|
) : IMessageToController, ICanReply<CreateOrUpdateAdministratorUserResult>;
|
||||||
public Task<CreateOrUpdateAdministratorUserResult> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleCreateOrUpdateAdministratorUser(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using Phantom.Common.Data.Instance;
|
using Phantom.Common.Data.Instance;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
using Phantom.Common.Data.Web.Instance;
|
using Phantom.Common.Data.Web.Instance;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -10,8 +11,4 @@ public sealed partial record CreateOrUpdateInstanceMessage(
|
|||||||
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
||||||
[property: MemoryPackOrder(1)] Guid InstanceGuid,
|
[property: MemoryPackOrder(1)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(2)] InstanceConfiguration Configuration
|
[property: MemoryPackOrder(2)] InstanceConfiguration Configuration
|
||||||
) : IMessageToController<InstanceActionResult<CreateOrUpdateInstanceResult>> {
|
) : IMessageToController, ICanReply<InstanceActionResult<CreateOrUpdateInstanceResult>>;
|
||||||
public Task<InstanceActionResult<CreateOrUpdateInstanceResult>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleCreateOrUpdateInstance(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -8,8 +9,4 @@ public sealed partial record CreateUserMessage(
|
|||||||
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
||||||
[property: MemoryPackOrder(1)] string Username,
|
[property: MemoryPackOrder(1)] string Username,
|
||||||
[property: MemoryPackOrder(2)] string Password
|
[property: MemoryPackOrder(2)] string Password
|
||||||
) : IMessageToController<CreateUserResult> {
|
) : IMessageToController, ICanReply<CreateUserResult>;
|
||||||
public Task<CreateUserResult> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleCreateUser(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -7,8 +8,4 @@ namespace Phantom.Common.Messages.Web.ToController;
|
|||||||
public sealed partial record DeleteUserMessage(
|
public sealed partial record DeleteUserMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
||||||
[property: MemoryPackOrder(1)] Guid SubjectUserGuid
|
[property: MemoryPackOrder(1)] Guid SubjectUserGuid
|
||||||
) : IMessageToController<DeleteUserResult> {
|
) : IMessageToController, ICanReply<DeleteUserResult>;
|
||||||
public Task<DeleteUserResult> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleDeleteUser(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Java;
|
using Phantom.Common.Data.Java;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record GetAgentJavaRuntimesMessage : IMessageToController<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>> {
|
public sealed partial record GetAgentJavaRuntimesMessage : IMessageToController, ICanReply<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>;
|
||||||
public Task<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleGetAgentJavaRuntimes(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.AuditLog;
|
using Phantom.Common.Data.Web.AuditLog;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record GetAuditLogMessage(
|
public sealed partial record GetAuditLogMessage(
|
||||||
[property: MemoryPackOrder(0)] int Count
|
[property: MemoryPackOrder(0)] int Count
|
||||||
) : IMessageToController<ImmutableArray<AuditLogItem>> {
|
) : IMessageToController, ICanReply<ImmutableArray<AuditLogItem>>;
|
||||||
public Task<ImmutableArray<AuditLogItem>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleGetAuditLog(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.EventLog;
|
using Phantom.Common.Data.Web.EventLog;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record GetEventLogMessage(
|
public sealed partial record GetEventLogMessage(
|
||||||
[property: MemoryPackOrder(0)] int Count
|
[property: MemoryPackOrder(0)] int Count
|
||||||
) : IMessageToController<ImmutableArray<EventLogItem>> {
|
) : IMessageToController, ICanReply<ImmutableArray<EventLogItem>>;
|
||||||
public Task<ImmutableArray<EventLogItem>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleGetEventLog(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Minecraft;
|
using Phantom.Common.Data.Minecraft;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record GetMinecraftVersionsMessage : IMessageToController<ImmutableArray<MinecraftVersion>> {
|
public sealed partial record GetMinecraftVersionsMessage : IMessageToController, ICanReply<ImmutableArray<MinecraftVersion>>;
|
||||||
public Task<ImmutableArray<MinecraftVersion>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleGetMinecraftVersions(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record GetRolesMessage : IMessageToController<ImmutableArray<RoleInfo>> {
|
public sealed partial record GetRolesMessage : IMessageToController, ICanReply<ImmutableArray<RoleInfo>>;
|
||||||
public Task<ImmutableArray<RoleInfo>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleGetRoles(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record GetUserRolesMessage(
|
public sealed partial record GetUserRolesMessage(
|
||||||
[property: MemoryPackOrder(0)] ImmutableHashSet<Guid> UserGuids
|
[property: MemoryPackOrder(0)] ImmutableHashSet<Guid> UserGuids
|
||||||
) : IMessageToController<ImmutableDictionary<Guid, ImmutableArray<Guid>>> {
|
) : IMessageToController, ICanReply<ImmutableDictionary<Guid, ImmutableArray<Guid>>>;
|
||||||
public Task<ImmutableDictionary<Guid, ImmutableArray<Guid>>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleGetUserRoles(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record GetUsersMessage : IMessageToController<ImmutableArray<UserInfo>> {
|
public sealed partial record GetUsersMessage : IMessageToController, ICanReply<ImmutableArray<UserInfo>>;
|
||||||
public Task<ImmutableArray<UserInfo>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleGetUsers(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -8,8 +9,4 @@ public sealed partial record LaunchInstanceMessage(
|
|||||||
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
[property: MemoryPackOrder(0)] Guid LoggedInUserGuid,
|
||||||
[property: MemoryPackOrder(1)] Guid AgentGuid,
|
[property: MemoryPackOrder(1)] Guid AgentGuid,
|
||||||
[property: MemoryPackOrder(2)] Guid InstanceGuid
|
[property: MemoryPackOrder(2)] Guid InstanceGuid
|
||||||
) : IMessageToController<InstanceActionResult<LaunchInstanceResult>> {
|
) : IMessageToController, ICanReply<InstanceActionResult<LaunchInstanceResult>>;
|
||||||
public Task<InstanceActionResult<LaunchInstanceResult>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleLaunchInstance(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Users;
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -7,8 +8,4 @@ namespace Phantom.Common.Messages.Web.ToController;
|
|||||||
public sealed partial record LogInMessage(
|
public sealed partial record LogInMessage(
|
||||||
[property: MemoryPackOrder(0)] string Username,
|
[property: MemoryPackOrder(0)] string Username,
|
||||||
[property: MemoryPackOrder(1)] string Password
|
[property: MemoryPackOrder(1)] string Password
|
||||||
) : IMessageToController<LogInSuccess?> {
|
) : IMessageToController, ICanReply<LogInSuccess?>;
|
||||||
public Task<LogInSuccess?> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleLogIn(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data;
|
using Phantom.Common.Data;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RegisterWebMessage(
|
public sealed partial record RegisterWebMessage(
|
||||||
[property: MemoryPackOrder(0)] AuthToken AuthToken
|
[property: MemoryPackOrder(0)] AuthToken AuthToken
|
||||||
) : IMessageToController {
|
) : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleRegisterWeb(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -9,8 +10,4 @@ public sealed partial record SendCommandToInstanceMessage(
|
|||||||
[property: MemoryPackOrder(1)] Guid AgentGuid,
|
[property: MemoryPackOrder(1)] Guid AgentGuid,
|
||||||
[property: MemoryPackOrder(2)] Guid InstanceGuid,
|
[property: MemoryPackOrder(2)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(3)] string Command
|
[property: MemoryPackOrder(3)] string Command
|
||||||
) : IMessageToController<InstanceActionResult<SendCommandToInstanceResult>> {
|
) : IMessageToController, ICanReply<InstanceActionResult<SendCommandToInstanceResult>>;
|
||||||
public Task<InstanceActionResult<SendCommandToInstanceResult>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleSendCommandToInstance(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Minecraft;
|
using Phantom.Common.Data.Minecraft;
|
||||||
using Phantom.Common.Data.Replies;
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
@ -10,8 +11,4 @@ public sealed partial record StopInstanceMessage(
|
|||||||
[property: MemoryPackOrder(1)] Guid AgentGuid,
|
[property: MemoryPackOrder(1)] Guid AgentGuid,
|
||||||
[property: MemoryPackOrder(2)] Guid InstanceGuid,
|
[property: MemoryPackOrder(2)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(3)] MinecraftStopStrategy StopStrategy
|
[property: MemoryPackOrder(3)] MinecraftStopStrategy StopStrategy
|
||||||
) : IMessageToController<InstanceActionResult<StopInstanceResult>> {
|
) : IMessageToController, ICanReply<InstanceActionResult<StopInstanceResult>>;
|
||||||
public Task<InstanceActionResult<StopInstanceResult>> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleStopInstance(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToController;
|
namespace Phantom.Common.Messages.Web.ToController;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record UnregisterWebMessage : IMessageToController {
|
public sealed partial record UnregisterWebMessage : IMessageToController;
|
||||||
public Task<NoReply> Accept(IMessageToControllerListener listener) {
|
|
||||||
return listener.HandleUnregisterWeb(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToWeb;
|
namespace Phantom.Common.Messages.Web.ToWeb;
|
||||||
|
|
||||||
@ -8,13 +7,4 @@ namespace Phantom.Common.Messages.Web.ToWeb;
|
|||||||
public sealed partial record InstanceOutputMessage(
|
public sealed partial record InstanceOutputMessage(
|
||||||
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
[property: MemoryPackOrder(0)] Guid InstanceGuid,
|
||||||
[property: MemoryPackOrder(1)] ImmutableArray<string> Lines
|
[property: MemoryPackOrder(1)] ImmutableArray<string> Lines
|
||||||
) : IMessageToWeb {
|
) : IMessageToWeb;
|
||||||
private static readonly MessageQueueKey MessageQueueKey = new ("Web.InstanceOutput");
|
|
||||||
|
|
||||||
[MemoryPackIgnore]
|
|
||||||
public MessageQueueKey QueueKey => MessageQueueKey;
|
|
||||||
|
|
||||||
public Task<NoReply> Accept(IMessageToWebListener listener) {
|
|
||||||
return listener.HandleInstanceOutput(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Agent;
|
using Phantom.Common.Data.Web.Agent;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToWeb;
|
namespace Phantom.Common.Messages.Web.ToWeb;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RefreshAgentsMessage(
|
public sealed partial record RefreshAgentsMessage(
|
||||||
[property: MemoryPackOrder(0)] ImmutableArray<Agent> Agents
|
[property: MemoryPackOrder(0)] ImmutableArray<Agent> Agents
|
||||||
) : IMessageToWeb {
|
) : IMessageToWeb;
|
||||||
public Task<NoReply> Accept(IMessageToWebListener listener) {
|
|
||||||
return listener.HandleRefreshAgents(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Common.Data.Web.Instance;
|
using Phantom.Common.Data.Web.Instance;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToWeb;
|
namespace Phantom.Common.Messages.Web.ToWeb;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RefreshInstancesMessage(
|
public sealed partial record RefreshInstancesMessage(
|
||||||
[property: MemoryPackOrder(0)] ImmutableArray<Instance> Instances
|
[property: MemoryPackOrder(0)] ImmutableArray<Instance> Instances
|
||||||
) : IMessageToWeb {
|
) : IMessageToWeb;
|
||||||
public Task<NoReply> Accept(IMessageToWebListener listener) {
|
|
||||||
return listener.HandleRefreshInstances(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
namespace Phantom.Common.Messages.Web.ToWeb;
|
namespace Phantom.Common.Messages.Web.ToWeb;
|
||||||
|
|
||||||
[MemoryPackable(GenerateType.VersionTolerant)]
|
[MemoryPackable(GenerateType.VersionTolerant)]
|
||||||
public sealed partial record RegisterWebResultMessage(
|
public sealed partial record RegisterWebResultMessage(
|
||||||
[property: MemoryPackOrder(0)] bool Success
|
[property: MemoryPackOrder(0)] bool Success
|
||||||
) : IMessageToWeb {
|
) : IMessageToWeb;
|
||||||
public Task<NoReply> Accept(IMessageToWebListener listener) {
|
|
||||||
return listener.HandleRegisterWebResult(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -15,10 +15,10 @@ using Phantom.Utils.Rpc.Message;
|
|||||||
namespace Phantom.Common.Messages.Web;
|
namespace Phantom.Common.Messages.Web;
|
||||||
|
|
||||||
public static class WebMessageRegistries {
|
public static class WebMessageRegistries {
|
||||||
public static MessageRegistry<IMessageToControllerListener> ToController { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToController)));
|
public static MessageRegistry<IMessageToController> ToController { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToController)));
|
||||||
public static MessageRegistry<IMessageToWebListener> ToWeb { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToWeb)));
|
public static MessageRegistry<IMessageToWeb> ToWeb { get; } = new (PhantomLogger.Create("MessageRegistry", nameof(ToWeb)));
|
||||||
|
|
||||||
public static IMessageDefinitions<IMessageToWebListener, IMessageToControllerListener, ReplyMessage> Definitions { get; } = new MessageDefinitions();
|
public static IMessageDefinitions<IMessageToWeb, IMessageToController, ReplyMessage> Definitions { get; } = new MessageDefinitions();
|
||||||
|
|
||||||
static WebMessageRegistries() {
|
static WebMessageRegistries() {
|
||||||
ToController.Add<RegisterWebMessage>(0);
|
ToController.Add<RegisterWebMessage>(0);
|
||||||
@ -48,13 +48,9 @@ public static class WebMessageRegistries {
|
|||||||
ToWeb.Add<ReplyMessage>(127);
|
ToWeb.Add<ReplyMessage>(127);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class MessageDefinitions : IMessageDefinitions<IMessageToWebListener, IMessageToControllerListener, ReplyMessage> {
|
private sealed class MessageDefinitions : IMessageDefinitions<IMessageToWeb, IMessageToController, ReplyMessage> {
|
||||||
public MessageRegistry<IMessageToWebListener> ToClient => ToWeb;
|
public MessageRegistry<IMessageToWeb> ToClient => ToWeb;
|
||||||
public MessageRegistry<IMessageToControllerListener> ToServer => ToController;
|
public MessageRegistry<IMessageToController> ToServer => ToController;
|
||||||
|
|
||||||
public bool IsRegistrationMessage(Type messageType) {
|
|
||||||
return messageType == typeof(RegisterWebMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReplyMessage CreateReplyMessage(uint sequenceId, byte[] serializedReply) {
|
public ReplyMessage CreateReplyMessage(uint sequenceId, byte[] serializedReply) {
|
||||||
return new ReplyMessage(sequenceId, serializedReply);
|
return new ReplyMessage(sequenceId, serializedReply);
|
||||||
|
@ -169,9 +169,9 @@ sealed class AgentActor : ReceiveActor<AgentActor.ICommand> {
|
|||||||
|
|
||||||
private sealed record InitializeCommand : ICommand;
|
private sealed record InitializeCommand : ICommand;
|
||||||
|
|
||||||
public sealed record RegisterCommand(AgentConfiguration Configuration, RpcConnectionToClient<IMessageToAgentListener> Connection) : ICommand, ICanReply<ImmutableArray<ConfigureInstanceMessage>>;
|
public sealed record RegisterCommand(AgentConfiguration Configuration, RpcConnectionToClient<IMessageToAgent> Connection) : ICommand, ICanReply<ImmutableArray<ConfigureInstanceMessage>>;
|
||||||
|
|
||||||
public sealed record UnregisterCommand(RpcConnectionToClient<IMessageToAgentListener> Connection) : ICommand;
|
public sealed record UnregisterCommand(RpcConnectionToClient<IMessageToAgent> Connection) : ICommand;
|
||||||
|
|
||||||
private sealed record RefreshConnectionStatusCommand : ICommand;
|
private sealed record RefreshConnectionStatusCommand : ICommand;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Phantom.Common.Messages.Agent;
|
using Phantom.Common.Messages.Agent;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Logging;
|
using Phantom.Utils.Logging;
|
||||||
using Phantom.Utils.Rpc.Runtime;
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -11,14 +12,14 @@ sealed class AgentConnection {
|
|||||||
private readonly Guid agentGuid;
|
private readonly Guid agentGuid;
|
||||||
private string agentName;
|
private string agentName;
|
||||||
|
|
||||||
private RpcConnectionToClient<IMessageToAgentListener>? connection;
|
private RpcConnectionToClient<IMessageToAgent>? connection;
|
||||||
|
|
||||||
public AgentConnection(Guid agentGuid, string agentName) {
|
public AgentConnection(Guid agentGuid, string agentName) {
|
||||||
this.agentName = agentName;
|
this.agentName = agentName;
|
||||||
this.agentGuid = agentGuid;
|
this.agentGuid = agentGuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateConnection(RpcConnectionToClient<IMessageToAgentListener> newConnection, string newAgentName) {
|
public void UpdateConnection(RpcConnectionToClient<IMessageToAgent> newConnection, string newAgentName) {
|
||||||
lock (this) {
|
lock (this) {
|
||||||
connection?.Close();
|
connection?.Close();
|
||||||
connection = newConnection;
|
connection = newConnection;
|
||||||
@ -26,7 +27,7 @@ sealed class AgentConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CloseIfSame(RpcConnectionToClient<IMessageToAgentListener> expected) {
|
public bool CloseIfSame(RpcConnectionToClient<IMessageToAgent> expected) {
|
||||||
lock (this) {
|
lock (this) {
|
||||||
if (connection != null && connection.IsSame(expected)) {
|
if (connection != null && connection.IsSame(expected)) {
|
||||||
connection.Close();
|
connection.Close();
|
||||||
@ -48,7 +49,7 @@ sealed class AgentConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TReply?> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken) where TMessage : IMessageToAgent<TReply> where TReply : class {
|
public Task<TReply?> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken) where TMessage : IMessageToAgent, ICanReply<TReply> where TReply : class {
|
||||||
lock (this) {
|
lock (this) {
|
||||||
if (connection == null) {
|
if (connection == null) {
|
||||||
LogAgentOffline();
|
LogAgentOffline();
|
||||||
|
@ -38,7 +38,7 @@ sealed class AgentDatabaseStorageActor : ReceiveActor<AgentDatabaseStorageActor.
|
|||||||
private sealed record FlushChangesCommand : ICommand;
|
private sealed record FlushChangesCommand : ICommand;
|
||||||
|
|
||||||
private void StoreAgentConfiguration(StoreAgentConfigurationCommand command) {
|
private void StoreAgentConfiguration(StoreAgentConfigurationCommand command) {
|
||||||
this.configurationToStore = command.Configuration;
|
configurationToStore = command.Configuration;
|
||||||
ScheduleFlush(TimeSpan.FromSeconds(2));
|
ScheduleFlush(TimeSpan.FromSeconds(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,11 +72,9 @@ sealed class AgentDatabaseStorageActor : ReceiveActor<AgentDatabaseStorageActor.
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ScheduleFlush(TimeSpan delay) {
|
private void ScheduleFlush(TimeSpan delay) {
|
||||||
if (hasScheduledFlush) {
|
if (!hasScheduledFlush) {
|
||||||
return;
|
hasScheduledFlush = true;
|
||||||
|
Context.System.Scheduler.ScheduleTellOnce(delay, Self, new FlushChangesCommand(), Self);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasScheduledFlush = true;
|
|
||||||
Context.System.Scheduler.ScheduleTellOnce(delay, Self, new FlushChangesCommand(), Self);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace Phantom.Controller.Services.Agents;
|
|||||||
sealed class AgentManager {
|
sealed class AgentManager {
|
||||||
private static readonly ILogger Logger = PhantomLogger.Create<AgentManager>();
|
private static readonly ILogger Logger = PhantomLogger.Create<AgentManager>();
|
||||||
|
|
||||||
private readonly ActorSystem actorSystem;
|
private readonly IActorRefFactory actorSystem;
|
||||||
private readonly AuthToken authToken;
|
private readonly AuthToken authToken;
|
||||||
private readonly ControllerState controllerState;
|
private readonly ControllerState controllerState;
|
||||||
private readonly MinecraftVersions minecraftVersions;
|
private readonly MinecraftVersions minecraftVersions;
|
||||||
@ -28,7 +28,7 @@ sealed class AgentManager {
|
|||||||
private readonly ConcurrentDictionary<Guid, ActorRef<AgentActor.ICommand>> agentsByGuid = new ();
|
private readonly ConcurrentDictionary<Guid, ActorRef<AgentActor.ICommand>> agentsByGuid = new ();
|
||||||
private readonly Func<Guid, AgentConfiguration, ActorRef<AgentActor.ICommand>> addAgentActorFactory;
|
private readonly Func<Guid, AgentConfiguration, ActorRef<AgentActor.ICommand>> addAgentActorFactory;
|
||||||
|
|
||||||
public AgentManager(ActorSystem actorSystem, AuthToken authToken, ControllerState controllerState, MinecraftVersions minecraftVersions, IDbContextProvider dbProvider, CancellationToken cancellationToken) {
|
public AgentManager(IActorRefFactory actorSystem, AuthToken authToken, ControllerState controllerState, MinecraftVersions minecraftVersions, IDbContextProvider dbProvider, CancellationToken cancellationToken) {
|
||||||
this.actorSystem = actorSystem;
|
this.actorSystem = actorSystem;
|
||||||
this.authToken = authToken;
|
this.authToken = authToken;
|
||||||
this.controllerState = controllerState;
|
this.controllerState = controllerState;
|
||||||
@ -58,15 +58,15 @@ sealed class AgentManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> RegisterAgent(AuthToken authToken, AgentInfo agentInfo, RpcConnectionToClient<IMessageToAgentListener> connection) {
|
public async Task<bool> RegisterAgent(AuthToken authToken, AgentInfo agentInfo, RpcConnectionToClient<IMessageToAgent> connection) {
|
||||||
if (!this.authToken.FixedTimeEquals(authToken)) {
|
if (!this.authToken.FixedTimeEquals(authToken)) {
|
||||||
await connection.Send(new RegisterAgentFailureMessage(RegisterAgentFailure.InvalidToken));
|
await connection.Send(new RegisterAgentFailureMessage(RegisterAgentFailure.InvalidToken));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var agentProperties = AgentConfiguration.From(agentInfo);
|
var agentProperties = AgentConfiguration.From(agentInfo);
|
||||||
var agentActorRef = agentsByGuid.GetOrAdd(agentInfo.AgentGuid, addAgentActorFactory, agentProperties);
|
var agentActor = agentsByGuid.GetOrAdd(agentInfo.AgentGuid, addAgentActorFactory, agentProperties);
|
||||||
var configureInstanceMessages = await agentActorRef.Request(new AgentActor.RegisterCommand(agentProperties, connection), cancellationToken);
|
var configureInstanceMessages = await agentActor.Request(new AgentActor.RegisterCommand(agentProperties, connection), cancellationToken);
|
||||||
await connection.Send(new RegisterAgentSuccessMessage(configureInstanceMessages));
|
await connection.Send(new RegisterAgentSuccessMessage(configureInstanceMessages));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using Akka.Actor;
|
using Akka.Actor;
|
||||||
using Phantom.Common.Data;
|
using Phantom.Common.Data;
|
||||||
using Phantom.Common.Messages.Agent;
|
using Phantom.Common.Messages.Agent;
|
||||||
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
using Phantom.Common.Messages.Web;
|
using Phantom.Common.Messages.Web;
|
||||||
|
using Phantom.Common.Messages.Web.ToController;
|
||||||
using Phantom.Controller.Database;
|
using Phantom.Controller.Database;
|
||||||
using Phantom.Controller.Minecraft;
|
using Phantom.Controller.Minecraft;
|
||||||
using Phantom.Controller.Services.Agents;
|
using Phantom.Controller.Services.Agents;
|
||||||
@ -10,14 +12,15 @@ using Phantom.Controller.Services.Instances;
|
|||||||
using Phantom.Controller.Services.Rpc;
|
using Phantom.Controller.Services.Rpc;
|
||||||
using Phantom.Controller.Services.Users;
|
using Phantom.Controller.Services.Users;
|
||||||
using Phantom.Utils.Actor;
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Logging;
|
|
||||||
using Phantom.Utils.Rpc.Runtime;
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
using Phantom.Utils.Tasks;
|
using IMessageFromAgentToController = Phantom.Common.Messages.Agent.IMessageToController;
|
||||||
|
using IMessageFromWebToController = Phantom.Common.Messages.Web.IMessageToController;
|
||||||
|
|
||||||
namespace Phantom.Controller.Services;
|
namespace Phantom.Controller.Services;
|
||||||
|
|
||||||
public sealed class ControllerServices : IAsyncDisposable {
|
public sealed class ControllerServices : IDisposable {
|
||||||
private TaskManager TaskManager { get; }
|
public ActorSystem ActorSystem { get; }
|
||||||
|
|
||||||
private ControllerState ControllerState { get; }
|
private ControllerState ControllerState { get; }
|
||||||
private MinecraftVersions MinecraftVersions { get; }
|
private MinecraftVersions MinecraftVersions { get; }
|
||||||
|
|
||||||
@ -32,24 +35,23 @@ public sealed class ControllerServices : IAsyncDisposable {
|
|||||||
private UserRoleManager UserRoleManager { get; }
|
private UserRoleManager UserRoleManager { get; }
|
||||||
private UserLoginManager UserLoginManager { get; }
|
private UserLoginManager UserLoginManager { get; }
|
||||||
private AuditLogManager AuditLogManager { get; }
|
private AuditLogManager AuditLogManager { get; }
|
||||||
|
|
||||||
private readonly ActorSystem actorSystem;
|
public IRegistrationHandler<IMessageToAgent, IMessageFromAgentToController, RegisterAgentMessage> AgentRegistrationHandler { get; }
|
||||||
|
public IRegistrationHandler<IMessageToWeb, IMessageFromWebToController, RegisterWebMessage> WebRegistrationHandler { get; }
|
||||||
|
|
||||||
private readonly IDbContextProvider dbProvider;
|
private readonly IDbContextProvider dbProvider;
|
||||||
private readonly AuthToken webAuthToken;
|
|
||||||
private readonly CancellationToken cancellationToken;
|
private readonly CancellationToken cancellationToken;
|
||||||
|
|
||||||
public ControllerServices(IDbContextProvider dbProvider, AuthToken agentAuthToken, AuthToken webAuthToken, CancellationToken shutdownCancellationToken) {
|
public ControllerServices(IDbContextProvider dbProvider, AuthToken agentAuthToken, AuthToken webAuthToken, CancellationToken shutdownCancellationToken) {
|
||||||
this.dbProvider = dbProvider;
|
this.dbProvider = dbProvider;
|
||||||
this.webAuthToken = webAuthToken;
|
|
||||||
this.cancellationToken = shutdownCancellationToken;
|
this.cancellationToken = shutdownCancellationToken;
|
||||||
|
|
||||||
this.actorSystem = ActorSystemFactory.Create("Controller");
|
this.ActorSystem = ActorSystemFactory.Create("Controller");
|
||||||
|
|
||||||
this.TaskManager = new TaskManager(PhantomLogger.Create<TaskManager, ControllerServices>());
|
|
||||||
this.ControllerState = new ControllerState();
|
this.ControllerState = new ControllerState();
|
||||||
this.MinecraftVersions = new MinecraftVersions();
|
this.MinecraftVersions = new MinecraftVersions();
|
||||||
|
|
||||||
this.AgentManager = new AgentManager(actorSystem, agentAuthToken, ControllerState, MinecraftVersions, dbProvider, cancellationToken);
|
this.AgentManager = new AgentManager(ActorSystem, agentAuthToken, ControllerState, MinecraftVersions, dbProvider, cancellationToken);
|
||||||
this.InstanceLogManager = new InstanceLogManager();
|
this.InstanceLogManager = new InstanceLogManager();
|
||||||
|
|
||||||
this.UserManager = new UserManager(dbProvider);
|
this.UserManager = new UserManager(dbProvider);
|
||||||
@ -59,15 +61,10 @@ public sealed class ControllerServices : IAsyncDisposable {
|
|||||||
this.UserRoleManager = new UserRoleManager(dbProvider);
|
this.UserRoleManager = new UserRoleManager(dbProvider);
|
||||||
this.UserLoginManager = new UserLoginManager(UserManager, PermissionManager);
|
this.UserLoginManager = new UserLoginManager(UserManager, PermissionManager);
|
||||||
this.AuditLogManager = new AuditLogManager(dbProvider);
|
this.AuditLogManager = new AuditLogManager(dbProvider);
|
||||||
this.EventLogManager = new EventLogManager(dbProvider, TaskManager, shutdownCancellationToken);
|
this.EventLogManager = new EventLogManager(ActorSystem, dbProvider, shutdownCancellationToken);
|
||||||
}
|
|
||||||
|
this.AgentRegistrationHandler = new AgentRegistrationHandler(AgentManager, InstanceLogManager, EventLogManager);
|
||||||
public AgentMessageListener CreateAgentMessageListener(RpcConnectionToClient<IMessageToAgentListener> connection) {
|
this.WebRegistrationHandler = new WebRegistrationHandler(webAuthToken, ControllerState, InstanceLogManager, UserManager, RoleManager, UserRoleManager, UserLoginManager, AuditLogManager, AgentManager, MinecraftVersions, EventLogManager);
|
||||||
return new AgentMessageListener(connection, AgentManager, InstanceLogManager, EventLogManager, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebMessageListener CreateWebMessageListener(RpcConnectionToClient<IMessageToWebListener> connection) {
|
|
||||||
return new WebMessageListener(actorSystem, connection, webAuthToken, ControllerState, UserManager, RoleManager, UserRoleManager, UserLoginManager, AuditLogManager, AgentManager, InstanceLogManager, MinecraftVersions, EventLogManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Initialize() {
|
public async Task Initialize() {
|
||||||
@ -77,8 +74,7 @@ public sealed class ControllerServices : IAsyncDisposable {
|
|||||||
await RoleManager.Initialize();
|
await RoleManager.Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync() {
|
public void Dispose() {
|
||||||
await actorSystem.Terminate();
|
ActorSystem.Dispose();
|
||||||
actorSystem.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
using Phantom.Common.Data.Web.EventLog;
|
||||||
|
using Phantom.Controller.Database;
|
||||||
|
using Phantom.Controller.Database.Repositories;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Logging;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Services.Events;
|
||||||
|
|
||||||
|
sealed class EventLogDatabaseStorageActor : ReceiveActor<EventLogDatabaseStorageActor.ICommand> {
|
||||||
|
private static readonly ILogger Logger = PhantomLogger.Create<EventLogDatabaseStorageActor>();
|
||||||
|
|
||||||
|
public readonly record struct Init(IDbContextProvider DbProvider, CancellationToken CancellationToken);
|
||||||
|
|
||||||
|
public static Props<ICommand> Factory(Init init) {
|
||||||
|
return Props<ICommand>.Create(() => new EventLogDatabaseStorageActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IDbContextProvider dbProvider;
|
||||||
|
private readonly CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
private readonly LinkedList<StoreEventCommand> pendingCommands = new ();
|
||||||
|
private bool hasScheduledFlush = false;
|
||||||
|
|
||||||
|
private EventLogDatabaseStorageActor(Init init) {
|
||||||
|
this.dbProvider = init.DbProvider;
|
||||||
|
this.cancellationToken = init.CancellationToken;
|
||||||
|
|
||||||
|
Receive<StoreEventCommand>(StoreEvent);
|
||||||
|
ReceiveAsync<FlushChangesCommand>(FlushChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICommand {}
|
||||||
|
|
||||||
|
public sealed record StoreEventCommand(Guid EventGuid, DateTime UtcTime, Guid? AgentGuid, EventLogEventType EventType, string SubjectId, Dictionary<string, object?>? Extra = null) : ICommand;
|
||||||
|
|
||||||
|
private sealed record FlushChangesCommand : ICommand;
|
||||||
|
|
||||||
|
private void StoreEvent(StoreEventCommand command) {
|
||||||
|
pendingCommands.AddLast(command);
|
||||||
|
ScheduleFlush(TimeSpan.FromMilliseconds(500));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task FlushChanges(FlushChangesCommand command) {
|
||||||
|
hasScheduledFlush = false;
|
||||||
|
|
||||||
|
if (pendingCommands.Count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await using var db = dbProvider.Lazy();
|
||||||
|
var eventLogRepository = new EventLogRepository(db);
|
||||||
|
|
||||||
|
foreach (var (eventGuid, dateTime, agentGuid, eventLogEventType, subjectId, extra) in pendingCommands) {
|
||||||
|
eventLogRepository.AddItem(eventGuid, dateTime, agentGuid, eventLogEventType, subjectId, extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.Ctx.SaveChangesAsync(cancellationToken);
|
||||||
|
} catch (Exception e) {
|
||||||
|
ScheduleFlush(TimeSpan.FromSeconds(10));
|
||||||
|
Logger.Error(e, "Could not store {EventCount} event(s) in database.", pendingCommands.Count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Information("Stored {EventCount} event(s) in database.", pendingCommands.Count);
|
||||||
|
|
||||||
|
pendingCommands.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScheduleFlush(TimeSpan delay) {
|
||||||
|
if (!hasScheduledFlush) {
|
||||||
|
hasScheduledFlush = true;
|
||||||
|
Context.System.Scheduler.ScheduleTellOnce(delay, Self, new FlushChangesCommand(), Self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,27 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using Akka.Actor;
|
||||||
using Phantom.Common.Data.Web.EventLog;
|
using Phantom.Common.Data.Web.EventLog;
|
||||||
using Phantom.Controller.Database;
|
using Phantom.Controller.Database;
|
||||||
using Phantom.Controller.Database.Repositories;
|
using Phantom.Controller.Database.Repositories;
|
||||||
using Phantom.Utils.Tasks;
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
namespace Phantom.Controller.Services.Events;
|
namespace Phantom.Controller.Services.Events;
|
||||||
|
|
||||||
sealed partial class EventLogManager {
|
sealed partial class EventLogManager {
|
||||||
|
private readonly ActorRef<EventLogDatabaseStorageActor.ICommand> databaseStorageActor;
|
||||||
private readonly IDbContextProvider dbProvider;
|
private readonly IDbContextProvider dbProvider;
|
||||||
private readonly TaskManager taskManager;
|
|
||||||
private readonly CancellationToken cancellationToken;
|
private readonly CancellationToken cancellationToken;
|
||||||
|
|
||||||
public EventLogManager(IDbContextProvider dbProvider, TaskManager taskManager, CancellationToken cancellationToken) {
|
public EventLogManager(IActorRefFactory actorSystem, IDbContextProvider dbProvider, CancellationToken cancellationToken) {
|
||||||
|
this.databaseStorageActor = actorSystem.ActorOf(EventLogDatabaseStorageActor.Factory(new EventLogDatabaseStorageActor.Init(dbProvider, cancellationToken)), "EventLogDatabaseStorage");
|
||||||
this.dbProvider = dbProvider;
|
this.dbProvider = dbProvider;
|
||||||
this.taskManager = taskManager;
|
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnqueueItem(Guid eventGuid, DateTime utcTime, Guid? agentGuid, EventLogEventType eventType, string subjectId, Dictionary<string, object?>? extra = null) {
|
private void EnqueueItem(Guid eventGuid, DateTime utcTime, Guid? agentGuid, EventLogEventType eventType, string subjectId, Dictionary<string, object?>? extra = null) {
|
||||||
taskManager.Run("Store event log item to database", () => AddItem(eventGuid, utcTime, agentGuid, eventType, subjectId, extra));
|
databaseStorageActor.Tell(new EventLogDatabaseStorageActor.StoreEventCommand(eventGuid, utcTime, agentGuid, eventType, subjectId, extra));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddItem(Guid eventGuid, DateTime utcTime, Guid? agentGuid, EventLogEventType eventType, string subjectId, Dictionary<string, object?>? extra = null) {
|
|
||||||
await using var db = dbProvider.Lazy();
|
|
||||||
new EventLogRepository(db).AddItem(eventGuid, utcTime, agentGuid, eventType, subjectId, extra);
|
|
||||||
await db.Ctx.SaveChangesAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ImmutableArray<EventLogItem>> GetMostRecentItems(int count) {
|
public async Task<ImmutableArray<EventLogItem>> GetMostRecentItems(int count) {
|
||||||
await using var db = dbProvider.Lazy();
|
await using var db = dbProvider.Lazy();
|
||||||
return await new EventLogRepository(db).GetMostRecentItems(count, cancellationToken);
|
return await new EventLogRepository(db).GetMostRecentItems(count, cancellationToken);
|
||||||
|
@ -11,13 +11,13 @@ using Phantom.Utils.Actor;
|
|||||||
namespace Phantom.Controller.Services.Instances;
|
namespace Phantom.Controller.Services.Instances;
|
||||||
|
|
||||||
sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
|
sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
|
||||||
public readonly record struct Init(Instance Instance, ActorRef<AgentActor.ICommand> AgentActorRef, AgentConnection AgentConnection, IDbContextProvider DbProvider, CancellationToken CancellationToken);
|
public readonly record struct Init(Instance Instance, ActorRef<AgentActor.ICommand> AgentActor, AgentConnection AgentConnection, IDbContextProvider DbProvider, CancellationToken CancellationToken);
|
||||||
|
|
||||||
public static Props<ICommand> Factory(Init init) {
|
public static Props<ICommand> Factory(Init init) {
|
||||||
return Props<ICommand>.Create(() => new InstanceActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
return Props<ICommand>.Create(() => new InstanceActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ActorRef<AgentActor.ICommand> agentActorRef;
|
private readonly ActorRef<AgentActor.ICommand> agentActor;
|
||||||
private readonly AgentConnection agentConnection;
|
private readonly AgentConnection agentConnection;
|
||||||
private readonly CancellationToken cancellationToken;
|
private readonly CancellationToken cancellationToken;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
|
|||||||
private readonly ActorRef<InstanceDatabaseStorageActor.ICommand> databaseStorageActor;
|
private readonly ActorRef<InstanceDatabaseStorageActor.ICommand> databaseStorageActor;
|
||||||
|
|
||||||
private InstanceActor(Init init) {
|
private InstanceActor(Init init) {
|
||||||
this.agentActorRef = init.AgentActorRef;
|
this.agentActor = init.AgentActor;
|
||||||
this.agentConnection = init.AgentConnection;
|
this.agentConnection = init.AgentConnection;
|
||||||
this.cancellationToken = init.CancellationToken;
|
this.cancellationToken = init.CancellationToken;
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void NotifyInstanceUpdated() {
|
private void NotifyInstanceUpdated() {
|
||||||
agentActorRef.Tell(new AgentActor.ReceiveInstanceDataCommand(new Instance(instanceGuid, configuration, status, launchAutomatically)));
|
agentActor.Tell(new AgentActor.ReceiveInstanceDataCommand(new Instance(instanceGuid, configuration, status, launchAutomatically)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetLaunchAutomatically(bool newValue) {
|
private void SetLaunchAutomatically(bool newValue) {
|
||||||
@ -56,7 +56,7 @@ sealed class InstanceActor : ReceiveActor<InstanceActor.ICommand> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<InstanceActionResult<TReply>> SendInstanceActionMessage<TMessage, TReply>(TMessage message) where TMessage : IMessageToAgent<InstanceActionResult<TReply>> {
|
private async Task<InstanceActionResult<TReply>> SendInstanceActionMessage<TMessage, TReply>(TMessage message) where TMessage : IMessageToAgent, ICanReply<InstanceActionResult<TReply>> {
|
||||||
var reply = await agentConnection.Send<TMessage, InstanceActionResult<TReply>>(message, TimeSpan.FromSeconds(10), cancellationToken);
|
var reply = await agentConnection.Send<TMessage, InstanceActionResult<TReply>>(message, TimeSpan.FromSeconds(10), cancellationToken);
|
||||||
return reply.DidNotReplyIfNull();
|
return reply.DidNotReplyIfNull();
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Akka" />
|
|
||||||
<PackageReference Include="BCrypt.Net-Next.StrongName" />
|
<PackageReference Include="BCrypt.Net-Next.StrongName" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
using Akka.Actor;
|
||||||
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Common.Messages.Agent;
|
||||||
|
using Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
|
using Phantom.Common.Messages.Agent.ToAgent;
|
||||||
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Controller.Services.Agents;
|
||||||
|
using Phantom.Controller.Services.Events;
|
||||||
|
using Phantom.Controller.Services.Instances;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Services.Rpc;
|
||||||
|
|
||||||
|
sealed class AgentMessageHandlerActor : ReceiveActor<IMessageToController> {
|
||||||
|
public readonly record struct Init(Guid AgentGuid, RpcConnectionToClient<IMessageToAgent> Connection, AgentRegistrationHandler AgentRegistrationHandler, AgentManager AgentManager, InstanceLogManager InstanceLogManager, EventLogManager EventLogManager);
|
||||||
|
|
||||||
|
public static Props<IMessageToController> Factory(Init init) {
|
||||||
|
return Props<IMessageToController>.Create(() => new AgentMessageHandlerActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
||||||
|
}
|
||||||
|
|
||||||
|
public IStash Stash { get; set; } = null!;
|
||||||
|
|
||||||
|
private readonly Guid agentGuid;
|
||||||
|
private readonly RpcConnectionToClient<IMessageToAgent> connection;
|
||||||
|
private readonly AgentRegistrationHandler agentRegistrationHandler;
|
||||||
|
private readonly AgentManager agentManager;
|
||||||
|
private readonly InstanceLogManager instanceLogManager;
|
||||||
|
private readonly EventLogManager eventLogManager;
|
||||||
|
|
||||||
|
private AgentMessageHandlerActor(Init init) {
|
||||||
|
this.agentGuid = init.AgentGuid;
|
||||||
|
this.connection = init.Connection;
|
||||||
|
this.agentRegistrationHandler = init.AgentRegistrationHandler;
|
||||||
|
this.agentManager = init.AgentManager;
|
||||||
|
this.instanceLogManager = init.InstanceLogManager;
|
||||||
|
this.eventLogManager = init.EventLogManager;
|
||||||
|
|
||||||
|
ReceiveAsync<RegisterAgentMessage>(HandleRegisterAgent);
|
||||||
|
Receive<UnregisterAgentMessage>(HandleUnregisterAgent);
|
||||||
|
Receive<AgentIsAliveMessage>(HandleAgentIsAlive);
|
||||||
|
Receive<AdvertiseJavaRuntimesMessage>(HandleAdvertiseJavaRuntimes);
|
||||||
|
Receive<ReportAgentStatusMessage>(HandleReportAgentStatus);
|
||||||
|
Receive<ReportInstanceStatusMessage>(HandleReportInstanceStatus);
|
||||||
|
Receive<ReportInstanceEventMessage>(HandleReportInstanceEvent);
|
||||||
|
Receive<InstanceOutputMessage>(HandleInstanceOutput);
|
||||||
|
Receive<ReplyMessage>(HandleReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleRegisterAgent(RegisterAgentMessage message) {
|
||||||
|
if (agentGuid != message.AgentInfo.AgentGuid) {
|
||||||
|
await connection.Send(new RegisterAgentFailureMessage(RegisterAgentFailure.ConnectionAlreadyHasAnAgent));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await agentRegistrationHandler.TryRegisterImpl(connection, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleUnregisterAgent(UnregisterAgentMessage message) {
|
||||||
|
agentManager.TellAgent(agentGuid, new AgentActor.UnregisterCommand(connection));
|
||||||
|
connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleAgentIsAlive(AgentIsAliveMessage message) {
|
||||||
|
agentManager.TellAgent(agentGuid, new AgentActor.NotifyIsAliveCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleAdvertiseJavaRuntimes(AdvertiseJavaRuntimesMessage message) {
|
||||||
|
agentManager.TellAgent(agentGuid, new AgentActor.UpdateJavaRuntimesCommand(message.Runtimes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleReportAgentStatus(ReportAgentStatusMessage message) {
|
||||||
|
agentManager.TellAgent(agentGuid, new AgentActor.UpdateStatsCommand(message.RunningInstanceCount, message.RunningInstanceMemory));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleReportInstanceStatus(ReportInstanceStatusMessage message) {
|
||||||
|
agentManager.TellAgent(agentGuid, new AgentActor.UpdateInstanceStatusCommand(message.InstanceGuid, message.InstanceStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleReportInstanceEvent(ReportInstanceEventMessage message) {
|
||||||
|
message.Event.Accept(eventLogManager.CreateInstanceEventVisitor(message.EventGuid, message.UtcTime, agentGuid, message.InstanceGuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleInstanceOutput(InstanceOutputMessage message) {
|
||||||
|
instanceLogManager.ReceiveLines(message.InstanceGuid, message.Lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleReply(ReplyMessage message) {
|
||||||
|
connection.Receive(message);
|
||||||
|
}
|
||||||
|
}
|
@ -1,92 +0,0 @@
|
|||||||
using Phantom.Common.Data.Replies;
|
|
||||||
using Phantom.Common.Messages.Agent;
|
|
||||||
using Phantom.Common.Messages.Agent.BiDirectional;
|
|
||||||
using Phantom.Common.Messages.Agent.ToAgent;
|
|
||||||
using Phantom.Common.Messages.Agent.ToController;
|
|
||||||
using Phantom.Controller.Services.Agents;
|
|
||||||
using Phantom.Controller.Services.Events;
|
|
||||||
using Phantom.Controller.Services.Instances;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
using Phantom.Utils.Rpc.Runtime;
|
|
||||||
using Phantom.Utils.Tasks;
|
|
||||||
|
|
||||||
namespace Phantom.Controller.Services.Rpc;
|
|
||||||
|
|
||||||
public sealed class AgentMessageListener : IMessageToControllerListener {
|
|
||||||
private readonly RpcConnectionToClient<IMessageToAgentListener> connection;
|
|
||||||
private readonly AgentManager agentManager;
|
|
||||||
private readonly InstanceLogManager instanceLogManager;
|
|
||||||
private readonly EventLogManager eventLogManager;
|
|
||||||
private readonly CancellationToken cancellationToken;
|
|
||||||
|
|
||||||
private readonly TaskCompletionSource<Guid> agentGuidWaiter = AsyncTasks.CreateCompletionSource<Guid>();
|
|
||||||
|
|
||||||
internal AgentMessageListener(RpcConnectionToClient<IMessageToAgentListener> connection, AgentManager agentManager, InstanceLogManager instanceLogManager, EventLogManager eventLogManager, CancellationToken cancellationToken) {
|
|
||||||
this.connection = connection;
|
|
||||||
this.agentManager = agentManager;
|
|
||||||
this.instanceLogManager = instanceLogManager;
|
|
||||||
this.eventLogManager = eventLogManager;
|
|
||||||
this.cancellationToken = cancellationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<NoReply> HandleRegisterAgent(RegisterAgentMessage message) {
|
|
||||||
if (agentGuidWaiter.Task.IsCompleted && agentGuidWaiter.Task.Result != message.AgentInfo.AgentGuid) {
|
|
||||||
connection.SetAuthorizationResult(false);
|
|
||||||
await connection.Send(new RegisterAgentFailureMessage(RegisterAgentFailure.ConnectionAlreadyHasAnAgent));
|
|
||||||
}
|
|
||||||
else if (await agentManager.RegisterAgent(message.AuthToken, message.AgentInfo, connection)) {
|
|
||||||
connection.SetAuthorizationResult(true);
|
|
||||||
agentGuidWaiter.SetResult(message.AgentInfo.AgentGuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NoReply.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Guid> WaitForAgentGuid() {
|
|
||||||
return await agentGuidWaiter.Task.WaitAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<NoReply> HandleUnregisterAgent(UnregisterAgentMessage message) {
|
|
||||||
if (agentGuidWaiter.Task.IsCompleted) {
|
|
||||||
agentManager.TellAgent(agentGuidWaiter.Task.Result, new AgentActor.UnregisterCommand(connection));
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.Close();
|
|
||||||
return Task.FromResult(NoReply.Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<NoReply> HandleAgentIsAlive(AgentIsAliveMessage message) {
|
|
||||||
agentManager.TellAgent(await WaitForAgentGuid(), new AgentActor.NotifyIsAliveCommand());
|
|
||||||
return NoReply.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<NoReply> HandleAdvertiseJavaRuntimes(AdvertiseJavaRuntimesMessage message) {
|
|
||||||
agentManager.TellAgent(await WaitForAgentGuid(), new AgentActor.UpdateJavaRuntimesCommand(message.Runtimes));
|
|
||||||
return NoReply.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<NoReply> HandleReportAgentStatus(ReportAgentStatusMessage message) {
|
|
||||||
agentManager.TellAgent(await WaitForAgentGuid(), new AgentActor.UpdateStatsCommand(message.RunningInstanceCount, message.RunningInstanceMemory));
|
|
||||||
return NoReply.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<NoReply> HandleReportInstanceStatus(ReportInstanceStatusMessage message) {
|
|
||||||
agentManager.TellAgent(await WaitForAgentGuid(), new AgentActor.UpdateInstanceStatusCommand(message.InstanceGuid, message.InstanceStatus));
|
|
||||||
return NoReply.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<NoReply> HandleReportInstanceEvent(ReportInstanceEventMessage message) {
|
|
||||||
message.Event.Accept(eventLogManager.CreateInstanceEventVisitor(message.EventGuid, message.UtcTime, await WaitForAgentGuid(), message.InstanceGuid));
|
|
||||||
return NoReply.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<NoReply> HandleInstanceOutput(InstanceOutputMessage message) {
|
|
||||||
instanceLogManager.ReceiveLines(message.InstanceGuid, message.Lines);
|
|
||||||
return Task.FromResult(NoReply.Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<NoReply> HandleReply(ReplyMessage message) {
|
|
||||||
connection.Receive(message);
|
|
||||||
return Task.FromResult(NoReply.Instance);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,34 @@
|
|||||||
|
using Phantom.Common.Messages.Agent;
|
||||||
|
using Phantom.Common.Messages.Agent.ToController;
|
||||||
|
using Phantom.Controller.Services.Agents;
|
||||||
|
using Phantom.Controller.Services.Events;
|
||||||
|
using Phantom.Controller.Services.Instances;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Services.Rpc;
|
||||||
|
|
||||||
|
sealed class AgentRegistrationHandler : IRegistrationHandler<IMessageToAgent, IMessageToController, RegisterAgentMessage> {
|
||||||
|
private readonly AgentManager agentManager;
|
||||||
|
private readonly InstanceLogManager instanceLogManager;
|
||||||
|
private readonly EventLogManager eventLogManager;
|
||||||
|
|
||||||
|
public AgentRegistrationHandler(AgentManager agentManager, InstanceLogManager instanceLogManager, EventLogManager eventLogManager) {
|
||||||
|
this.agentManager = agentManager;
|
||||||
|
this.instanceLogManager = instanceLogManager;
|
||||||
|
this.eventLogManager = eventLogManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<Props<IMessageToController>?> IRegistrationHandler<IMessageToAgent, IMessageToController, RegisterAgentMessage>.TryRegister(RpcConnectionToClient<IMessageToAgent> connection, RegisterAgentMessage message) {
|
||||||
|
return await TryRegisterImpl(connection, message) ? CreateMessageHandlerActorProps(message.AgentInfo.AgentGuid, connection) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> TryRegisterImpl(RpcConnectionToClient<IMessageToAgent> connection, RegisterAgentMessage message) {
|
||||||
|
return agentManager.RegisterAgent(message.AuthToken, message.AgentInfo, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Props<IMessageToController> CreateMessageHandlerActorProps(Guid agentGuid, RpcConnectionToClient<IMessageToAgent> connection) {
|
||||||
|
var init = new AgentMessageHandlerActor.Init(agentGuid, connection, this, agentManager, instanceLogManager, eventLogManager);
|
||||||
|
return AgentMessageHandlerActor.Factory(init);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using Phantom.Common.Data.Web.Agent;
|
||||||
|
using Phantom.Common.Data.Web.Instance;
|
||||||
|
using Phantom.Common.Messages.Web;
|
||||||
|
using Phantom.Common.Messages.Web.ToWeb;
|
||||||
|
using Phantom.Controller.Services.Instances;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Services.Rpc;
|
||||||
|
|
||||||
|
sealed class WebMessageDataUpdateSenderActor : ReceiveActor<WebMessageDataUpdateSenderActor.ICommand> {
|
||||||
|
public readonly record struct Init(RpcConnectionToClient<IMessageToWeb> Connection, ControllerState ControllerState, InstanceLogManager InstanceLogManager);
|
||||||
|
|
||||||
|
public static Props<ICommand> Factory(Init init) {
|
||||||
|
return Props<ICommand>.Create(() => new WebMessageDataUpdateSenderActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly RpcConnectionToClient<IMessageToWeb> connection;
|
||||||
|
private readonly ControllerState controllerState;
|
||||||
|
private readonly InstanceLogManager instanceLogManager;
|
||||||
|
private readonly ActorRef<ICommand> selfCached;
|
||||||
|
|
||||||
|
private WebMessageDataUpdateSenderActor(Init init) {
|
||||||
|
this.connection = init.Connection;
|
||||||
|
this.controllerState = init.ControllerState;
|
||||||
|
this.instanceLogManager = init.InstanceLogManager;
|
||||||
|
this.selfCached = SelfTyped;
|
||||||
|
|
||||||
|
ReceiveAsync<RefreshAgentsCommand>(RefreshAgents);
|
||||||
|
ReceiveAsync<RefreshInstancesCommand>(RefreshInstances);
|
||||||
|
ReceiveAsync<ReceiveInstanceLogsCommand>(ReceiveInstanceLogs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PreStart() {
|
||||||
|
controllerState.AgentsByGuidReceiver.Register(SelfTyped, static state => new RefreshAgentsCommand(state));
|
||||||
|
controllerState.InstancesByGuidReceiver.Register(SelfTyped, static state => new RefreshInstancesCommand(state));
|
||||||
|
|
||||||
|
instanceLogManager.LogsReceived += OnInstanceLogsReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PostStop() {
|
||||||
|
instanceLogManager.LogsReceived -= OnInstanceLogsReceived;
|
||||||
|
|
||||||
|
controllerState.AgentsByGuidReceiver.Unregister(SelfTyped);
|
||||||
|
controllerState.InstancesByGuidReceiver.Unregister(SelfTyped);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInstanceLogsReceived(object? sender, InstanceLogManager.Event e) {
|
||||||
|
selfCached.Tell(new ReceiveInstanceLogsCommand(e.InstanceGuid, e.Lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICommand {}
|
||||||
|
|
||||||
|
private sealed record RefreshAgentsCommand(ImmutableDictionary<Guid, Agent> Agents) : ICommand;
|
||||||
|
|
||||||
|
private sealed record RefreshInstancesCommand(ImmutableDictionary<Guid, Instance> Instances) : ICommand;
|
||||||
|
|
||||||
|
private sealed record ReceiveInstanceLogsCommand(Guid InstanceGuid, ImmutableArray<string> Lines) : ICommand;
|
||||||
|
|
||||||
|
private Task RefreshAgents(RefreshAgentsCommand command) {
|
||||||
|
return connection.Send(new RefreshAgentsMessage(command.Agents.Values.ToImmutableArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task RefreshInstances(RefreshInstancesCommand command) {
|
||||||
|
return connection.Send(new RefreshInstancesMessage(command.Instances.Values.ToImmutableArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task ReceiveInstanceLogs(ReceiveInstanceLogsCommand command) {
|
||||||
|
return connection.Send(new InstanceOutputMessage(command.InstanceGuid, command.Lines));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using Phantom.Common.Data.Java;
|
||||||
|
using Phantom.Common.Data.Minecraft;
|
||||||
|
using Phantom.Common.Data.Replies;
|
||||||
|
using Phantom.Common.Data.Web.AuditLog;
|
||||||
|
using Phantom.Common.Data.Web.EventLog;
|
||||||
|
using Phantom.Common.Data.Web.Instance;
|
||||||
|
using Phantom.Common.Data.Web.Users;
|
||||||
|
using Phantom.Common.Messages.Agent.BiDirectional;
|
||||||
|
using Phantom.Common.Messages.Web;
|
||||||
|
using Phantom.Common.Messages.Web.ToController;
|
||||||
|
using Phantom.Controller.Minecraft;
|
||||||
|
using Phantom.Controller.Services.Agents;
|
||||||
|
using Phantom.Controller.Services.Events;
|
||||||
|
using Phantom.Controller.Services.Instances;
|
||||||
|
using Phantom.Controller.Services.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Services.Rpc;
|
||||||
|
|
||||||
|
sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
|
||||||
|
public readonly record struct Init(
|
||||||
|
RpcConnectionToClient<IMessageToWeb> Connection,
|
||||||
|
WebRegistrationHandler WebRegistrationHandler,
|
||||||
|
ControllerState ControllerState,
|
||||||
|
InstanceLogManager InstanceLogManager,
|
||||||
|
UserManager UserManager,
|
||||||
|
RoleManager RoleManager,
|
||||||
|
UserRoleManager UserRoleManager,
|
||||||
|
UserLoginManager UserLoginManager,
|
||||||
|
AuditLogManager AuditLogManager,
|
||||||
|
AgentManager AgentManager,
|
||||||
|
MinecraftVersions MinecraftVersions,
|
||||||
|
EventLogManager EventLogManager
|
||||||
|
);
|
||||||
|
|
||||||
|
public static Props<IMessageToController> Factory(Init init) {
|
||||||
|
return Props<IMessageToController>.Create(() => new WebMessageHandlerActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly RpcConnectionToClient<IMessageToWeb> connection;
|
||||||
|
private readonly WebRegistrationHandler webRegistrationHandler;
|
||||||
|
private readonly ControllerState controllerState;
|
||||||
|
private readonly UserManager userManager;
|
||||||
|
private readonly RoleManager roleManager;
|
||||||
|
private readonly UserRoleManager userRoleManager;
|
||||||
|
private readonly UserLoginManager userLoginManager;
|
||||||
|
private readonly AuditLogManager auditLogManager;
|
||||||
|
private readonly AgentManager agentManager;
|
||||||
|
private readonly MinecraftVersions minecraftVersions;
|
||||||
|
private readonly EventLogManager eventLogManager;
|
||||||
|
|
||||||
|
private WebMessageHandlerActor(Init init) {
|
||||||
|
this.connection = init.Connection;
|
||||||
|
this.webRegistrationHandler = init.WebRegistrationHandler;
|
||||||
|
this.controllerState = init.ControllerState;
|
||||||
|
this.userManager = init.UserManager;
|
||||||
|
this.roleManager = init.RoleManager;
|
||||||
|
this.userRoleManager = init.UserRoleManager;
|
||||||
|
this.userLoginManager = init.UserLoginManager;
|
||||||
|
this.auditLogManager = init.AuditLogManager;
|
||||||
|
this.agentManager = init.AgentManager;
|
||||||
|
this.minecraftVersions = init.MinecraftVersions;
|
||||||
|
this.eventLogManager = init.EventLogManager;
|
||||||
|
|
||||||
|
var senderActorInit = new WebMessageDataUpdateSenderActor.Init(connection, controllerState, init.InstanceLogManager);
|
||||||
|
Context.ActorOf(WebMessageDataUpdateSenderActor.Factory(senderActorInit), "DataUpdateSender");
|
||||||
|
|
||||||
|
ReceiveAsync<RegisterWebMessage>(HandleRegisterWeb);
|
||||||
|
Receive<UnregisterWebMessage>(HandleUnregisterWeb);
|
||||||
|
ReceiveAndReplyLater<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(HandleCreateOrUpdateAdministratorUser);
|
||||||
|
ReceiveAndReplyLater<CreateUserMessage, CreateUserResult>(HandleCreateUser);
|
||||||
|
ReceiveAndReplyLater<GetUsersMessage, ImmutableArray<UserInfo>>(HandleGetUsers);
|
||||||
|
ReceiveAndReplyLater<GetRolesMessage, ImmutableArray<RoleInfo>>(HandleGetRoles);
|
||||||
|
ReceiveAndReplyLater<GetUserRolesMessage, ImmutableDictionary<Guid, ImmutableArray<Guid>>>(HandleGetUserRoles);
|
||||||
|
ReceiveAndReplyLater<ChangeUserRolesMessage, ChangeUserRolesResult>(HandleChangeUserRoles);
|
||||||
|
ReceiveAndReplyLater<DeleteUserMessage, DeleteUserResult>(HandleDeleteUser);
|
||||||
|
ReceiveAndReplyLater<CreateOrUpdateInstanceMessage, InstanceActionResult<CreateOrUpdateInstanceResult>>(HandleCreateOrUpdateInstance);
|
||||||
|
ReceiveAndReplyLater<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(HandleLaunchInstance);
|
||||||
|
ReceiveAndReplyLater<StopInstanceMessage, InstanceActionResult<StopInstanceResult>>(HandleStopInstance);
|
||||||
|
ReceiveAndReplyLater<SendCommandToInstanceMessage, InstanceActionResult<SendCommandToInstanceResult>>(HandleSendCommandToInstance);
|
||||||
|
ReceiveAndReplyLater<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(HandleGetMinecraftVersions);
|
||||||
|
ReceiveAndReply<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(HandleGetAgentJavaRuntimes);
|
||||||
|
ReceiveAndReplyLater<GetAuditLogMessage, ImmutableArray<AuditLogItem>>(HandleGetAuditLog);
|
||||||
|
ReceiveAndReplyLater<GetEventLogMessage, ImmutableArray<EventLogItem>>(HandleGetEventLog);
|
||||||
|
ReceiveAndReplyLater<LogInMessage, LogInSuccess?>(HandleLogIn);
|
||||||
|
Receive<ReplyMessage>(HandleReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleRegisterWeb(RegisterWebMessage message) {
|
||||||
|
await webRegistrationHandler.TryRegisterImpl(connection, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleUnregisterWeb(UnregisterWebMessage message) {
|
||||||
|
connection.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<CreateOrUpdateAdministratorUserResult> HandleCreateOrUpdateAdministratorUser(CreateOrUpdateAdministratorUserMessage message) {
|
||||||
|
return userManager.CreateOrUpdateAdministrator(message.Username, message.Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<CreateUserResult> HandleCreateUser(CreateUserMessage message) {
|
||||||
|
return userManager.Create(message.LoggedInUserGuid, message.Username, message.Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<ImmutableArray<UserInfo>> HandleGetUsers(GetUsersMessage message) {
|
||||||
|
return userManager.GetAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<ImmutableArray<RoleInfo>> HandleGetRoles(GetRolesMessage message) {
|
||||||
|
return roleManager.GetAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<ImmutableDictionary<Guid, ImmutableArray<Guid>>> HandleGetUserRoles(GetUserRolesMessage message) {
|
||||||
|
return userRoleManager.GetUserRoles(message.UserGuids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<ChangeUserRolesResult> HandleChangeUserRoles(ChangeUserRolesMessage message) {
|
||||||
|
return userRoleManager.ChangeUserRoles(message.LoggedInUserGuid, message.SubjectUserGuid, message.AddToRoleGuids, message.RemoveFromRoleGuids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<DeleteUserResult> HandleDeleteUser(DeleteUserMessage message) {
|
||||||
|
return userManager.DeleteByGuid(message.LoggedInUserGuid, message.SubjectUserGuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<InstanceActionResult<CreateOrUpdateInstanceResult>> HandleCreateOrUpdateInstance(CreateOrUpdateInstanceMessage message) {
|
||||||
|
return agentManager.DoInstanceAction<AgentActor.CreateOrUpdateInstanceCommand, CreateOrUpdateInstanceResult>(message.Configuration.AgentGuid, new AgentActor.CreateOrUpdateInstanceCommand(message.LoggedInUserGuid, message.InstanceGuid, message.Configuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
||||||
|
return agentManager.DoInstanceAction<AgentActor.LaunchInstanceCommand, LaunchInstanceResult>(message.AgentGuid, new AgentActor.LaunchInstanceCommand(message.InstanceGuid, message.LoggedInUserGuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<InstanceActionResult<StopInstanceResult>> HandleStopInstance(StopInstanceMessage message) {
|
||||||
|
return agentManager.DoInstanceAction<AgentActor.StopInstanceCommand, StopInstanceResult>(message.AgentGuid, new AgentActor.StopInstanceCommand(message.InstanceGuid, message.LoggedInUserGuid, message.StopStrategy));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<InstanceActionResult<SendCommandToInstanceResult>> HandleSendCommandToInstance(SendCommandToInstanceMessage message) {
|
||||||
|
return agentManager.DoInstanceAction<AgentActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(message.AgentGuid, new AgentActor.SendCommandToInstanceCommand(message.InstanceGuid, message.LoggedInUserGuid, message.Command));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<ImmutableArray<MinecraftVersion>> HandleGetMinecraftVersions(GetMinecraftVersionsMessage message) {
|
||||||
|
return minecraftVersions.GetVersions(CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>> HandleGetAgentJavaRuntimes(GetAgentJavaRuntimesMessage message) {
|
||||||
|
return controllerState.AgentJavaRuntimesByGuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<ImmutableArray<AuditLogItem>> HandleGetAuditLog(GetAuditLogMessage message) {
|
||||||
|
return auditLogManager.GetMostRecentItems(message.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<ImmutableArray<EventLogItem>> HandleGetEventLog(GetEventLogMessage message) {
|
||||||
|
return eventLogManager.GetMostRecentItems(message.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<LogInSuccess?> HandleLogIn(LogInMessage message) {
|
||||||
|
return userLoginManager.LogIn(message.Username, message.Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleReply(ReplyMessage message) {
|
||||||
|
connection.Receive(message);
|
||||||
|
}
|
||||||
|
}
|
@ -1,229 +0,0 @@
|
|||||||
using System.Collections.Immutable;
|
|
||||||
using Akka.Actor;
|
|
||||||
using Phantom.Common.Data;
|
|
||||||
using Phantom.Common.Data.Java;
|
|
||||||
using Phantom.Common.Data.Minecraft;
|
|
||||||
using Phantom.Common.Data.Replies;
|
|
||||||
using Phantom.Common.Data.Web.AuditLog;
|
|
||||||
using Phantom.Common.Data.Web.EventLog;
|
|
||||||
using Phantom.Common.Data.Web.Instance;
|
|
||||||
using Phantom.Common.Data.Web.Users;
|
|
||||||
using Phantom.Common.Messages.Web;
|
|
||||||
using Phantom.Common.Messages.Web.BiDirectional;
|
|
||||||
using Phantom.Common.Messages.Web.ToController;
|
|
||||||
using Phantom.Common.Messages.Web.ToWeb;
|
|
||||||
using Phantom.Controller.Minecraft;
|
|
||||||
using Phantom.Controller.Services.Agents;
|
|
||||||
using Phantom.Controller.Services.Events;
|
|
||||||
using Phantom.Controller.Services.Instances;
|
|
||||||
using Phantom.Controller.Services.Users;
|
|
||||||
using Phantom.Utils.Actor;
|
|
||||||
using Phantom.Utils.Logging;
|
|
||||||
using Phantom.Utils.Rpc.Message;
|
|
||||||
using Phantom.Utils.Rpc.Runtime;
|
|
||||||
using Serilog;
|
|
||||||
using Agent = Phantom.Common.Data.Web.Agent.Agent;
|
|
||||||
|
|
||||||
namespace Phantom.Controller.Services.Rpc;
|
|
||||||
|
|
||||||
public sealed class WebMessageListener : IMessageToControllerListener {
|
|
||||||
private static readonly ILogger Logger = PhantomLogger.Create<WebMessageListener>();
|
|
||||||
|
|
||||||
private static int listenerSequenceId = 0;
|
|
||||||
|
|
||||||
private readonly ActorRef<ICommand> actor;
|
|
||||||
private readonly RpcConnectionToClient<IMessageToWebListener> connection;
|
|
||||||
private readonly AuthToken authToken;
|
|
||||||
private readonly ControllerState controllerState;
|
|
||||||
private readonly UserManager userManager;
|
|
||||||
private readonly RoleManager roleManager;
|
|
||||||
private readonly UserRoleManager userRoleManager;
|
|
||||||
private readonly UserLoginManager userLoginManager;
|
|
||||||
private readonly AuditLogManager auditLogManager;
|
|
||||||
private readonly AgentManager agentManager;
|
|
||||||
private readonly InstanceLogManager instanceLogManager;
|
|
||||||
private readonly MinecraftVersions minecraftVersions;
|
|
||||||
private readonly EventLogManager eventLogManager;
|
|
||||||
|
|
||||||
internal WebMessageListener(
|
|
||||||
IActorRefFactory actorSystem,
|
|
||||||
RpcConnectionToClient<IMessageToWebListener> connection,
|
|
||||||
AuthToken authToken,
|
|
||||||
ControllerState controllerState,
|
|
||||||
UserManager userManager,
|
|
||||||
RoleManager roleManager,
|
|
||||||
UserRoleManager userRoleManager,
|
|
||||||
UserLoginManager userLoginManager,
|
|
||||||
AuditLogManager auditLogManager,
|
|
||||||
AgentManager agentManager,
|
|
||||||
InstanceLogManager instanceLogManager,
|
|
||||||
MinecraftVersions minecraftVersions,
|
|
||||||
EventLogManager eventLogManager
|
|
||||||
) {
|
|
||||||
this.actor = actorSystem.ActorOf(Actor.Factory(this), "Web-" + Interlocked.Increment(ref listenerSequenceId));
|
|
||||||
this.connection = connection;
|
|
||||||
this.authToken = authToken;
|
|
||||||
this.controllerState = controllerState;
|
|
||||||
this.userManager = userManager;
|
|
||||||
this.roleManager = roleManager;
|
|
||||||
this.userRoleManager = userRoleManager;
|
|
||||||
this.userLoginManager = userLoginManager;
|
|
||||||
this.auditLogManager = auditLogManager;
|
|
||||||
this.agentManager = agentManager;
|
|
||||||
this.instanceLogManager = instanceLogManager;
|
|
||||||
this.minecraftVersions = minecraftVersions;
|
|
||||||
this.eventLogManager = eventLogManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class Actor : ReceiveActor<ICommand> {
|
|
||||||
public static Props<ICommand> Factory(WebMessageListener listener) {
|
|
||||||
return Props<ICommand>.Create(() => new Actor(listener), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly WebMessageListener listener;
|
|
||||||
|
|
||||||
private Actor(WebMessageListener listener) {
|
|
||||||
this.listener = listener;
|
|
||||||
|
|
||||||
Receive<StartConnectionCommand>(StartConnection);
|
|
||||||
Receive<StopConnectionCommand>(StopConnection);
|
|
||||||
Receive<RefreshAgentsCommand>(RefreshAgents);
|
|
||||||
Receive<RefreshInstancesCommand>(RefreshInstances);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartConnection(StartConnectionCommand command) {
|
|
||||||
listener.controllerState.AgentsByGuidReceiver.Register(SelfTyped, static state => new RefreshAgentsCommand(state));
|
|
||||||
listener.controllerState.InstancesByGuidReceiver.Register(SelfTyped, static state => new RefreshInstancesCommand(state));
|
|
||||||
|
|
||||||
listener.instanceLogManager.LogsReceived += HandleInstanceLogsReceived;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopConnection(StopConnectionCommand command) {
|
|
||||||
listener.instanceLogManager.LogsReceived -= HandleInstanceLogsReceived;
|
|
||||||
|
|
||||||
listener.controllerState.AgentsByGuidReceiver.Unregister(SelfTyped);
|
|
||||||
listener.controllerState.InstancesByGuidReceiver.Unregister(SelfTyped);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshAgents(RefreshAgentsCommand command) {
|
|
||||||
var message = new RefreshAgentsMessage(command.Agents.Values.ToImmutableArray());
|
|
||||||
listener.connection.Send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshInstances(RefreshInstancesCommand command) {
|
|
||||||
var message = new RefreshInstancesMessage(command.Instances.Values.ToImmutableArray());
|
|
||||||
listener.connection.Send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleInstanceLogsReceived(object? sender, InstanceLogManager.Event e) {
|
|
||||||
listener.connection.Send(new InstanceOutputMessage(e.InstanceGuid, e.Lines));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface ICommand {}
|
|
||||||
|
|
||||||
private sealed record StartConnectionCommand : ICommand;
|
|
||||||
|
|
||||||
private sealed record StopConnectionCommand : ICommand;
|
|
||||||
|
|
||||||
private sealed record RefreshAgentsCommand(ImmutableDictionary<Guid, Agent> Agents) : ICommand;
|
|
||||||
|
|
||||||
private sealed record RefreshInstancesCommand(ImmutableDictionary<Guid, Instance> Instances) : ICommand;
|
|
||||||
|
|
||||||
public async Task<NoReply> HandleRegisterWeb(RegisterWebMessage message) {
|
|
||||||
if (authToken.FixedTimeEquals(message.AuthToken)) {
|
|
||||||
Logger.Information("Web authorized successfully.");
|
|
||||||
connection.SetAuthorizationResult(true);
|
|
||||||
await connection.Send(new RegisterWebResultMessage(true));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Logger.Warning("Web failed to authorize, invalid token.");
|
|
||||||
connection.SetAuthorizationResult(false);
|
|
||||||
await connection.Send(new RegisterWebResultMessage(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!connection.IsClosed) {
|
|
||||||
actor.Tell(new StartConnectionCommand());
|
|
||||||
}
|
|
||||||
|
|
||||||
return NoReply.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<NoReply> HandleUnregisterWeb(UnregisterWebMessage message) {
|
|
||||||
if (!connection.IsClosed) {
|
|
||||||
connection.Close();
|
|
||||||
actor.Tell(new StopConnectionCommand());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(NoReply.Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<CreateOrUpdateAdministratorUserResult> HandleCreateOrUpdateAdministratorUser(CreateOrUpdateAdministratorUserMessage message) {
|
|
||||||
return userManager.CreateOrUpdateAdministrator(message.Username, message.Password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<CreateUserResult> HandleCreateUser(CreateUserMessage message) {
|
|
||||||
return userManager.Create(message.LoggedInUserGuid, message.Username, message.Password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ImmutableArray<UserInfo>> HandleGetUsers(GetUsersMessage message) {
|
|
||||||
return userManager.GetAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ImmutableArray<RoleInfo>> HandleGetRoles(GetRolesMessage message) {
|
|
||||||
return roleManager.GetAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ImmutableDictionary<Guid, ImmutableArray<Guid>>> HandleGetUserRoles(GetUserRolesMessage message) {
|
|
||||||
return userRoleManager.GetUserRoles(message.UserGuids);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ChangeUserRolesResult> HandleChangeUserRoles(ChangeUserRolesMessage message) {
|
|
||||||
return userRoleManager.ChangeUserRoles(message.LoggedInUserGuid, message.SubjectUserGuid, message.AddToRoleGuids, message.RemoveFromRoleGuids);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<DeleteUserResult> HandleDeleteUser(DeleteUserMessage message) {
|
|
||||||
return userManager.DeleteByGuid(message.LoggedInUserGuid, message.SubjectUserGuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<InstanceActionResult<CreateOrUpdateInstanceResult>> HandleCreateOrUpdateInstance(CreateOrUpdateInstanceMessage message) {
|
|
||||||
return agentManager.DoInstanceAction<AgentActor.CreateOrUpdateInstanceCommand, CreateOrUpdateInstanceResult>(message.Configuration.AgentGuid, new AgentActor.CreateOrUpdateInstanceCommand(message.LoggedInUserGuid, message.InstanceGuid, message.Configuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<InstanceActionResult<LaunchInstanceResult>> HandleLaunchInstance(LaunchInstanceMessage message) {
|
|
||||||
return agentManager.DoInstanceAction<AgentActor.LaunchInstanceCommand, LaunchInstanceResult>(message.AgentGuid, new AgentActor.LaunchInstanceCommand(message.InstanceGuid, message.LoggedInUserGuid));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<InstanceActionResult<StopInstanceResult>> HandleStopInstance(StopInstanceMessage message) {
|
|
||||||
return agentManager.DoInstanceAction<AgentActor.StopInstanceCommand, StopInstanceResult>(message.AgentGuid, new AgentActor.StopInstanceCommand(message.InstanceGuid, message.LoggedInUserGuid, message.StopStrategy));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<InstanceActionResult<SendCommandToInstanceResult>> HandleSendCommandToInstance(SendCommandToInstanceMessage message) {
|
|
||||||
return agentManager.DoInstanceAction<AgentActor.SendCommandToInstanceCommand, SendCommandToInstanceResult>(message.AgentGuid, new AgentActor.SendCommandToInstanceCommand(message.InstanceGuid, message.LoggedInUserGuid, message.Command));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ImmutableArray<MinecraftVersion>> HandleGetMinecraftVersions(GetMinecraftVersionsMessage message) {
|
|
||||||
return minecraftVersions.GetVersions(CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>> HandleGetAgentJavaRuntimes(GetAgentJavaRuntimesMessage message) {
|
|
||||||
return Task.FromResult(controllerState.AgentJavaRuntimesByGuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ImmutableArray<AuditLogItem>> HandleGetAuditLog(GetAuditLogMessage message) {
|
|
||||||
return auditLogManager.GetMostRecentItems(message.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ImmutableArray<EventLogItem>> HandleGetEventLog(GetEventLogMessage message) {
|
|
||||||
return eventLogManager.GetMostRecentItems(message.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<LogInSuccess?> HandleLogIn(LogInMessage message) {
|
|
||||||
return userLoginManager.LogIn(message.Username, message.Password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<NoReply> HandleReply(ReplyMessage message) {
|
|
||||||
connection.Receive(message);
|
|
||||||
return Task.FromResult(NoReply.Instance);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,67 @@
|
|||||||
|
using Phantom.Common.Data;
|
||||||
|
using Phantom.Common.Messages.Web;
|
||||||
|
using Phantom.Common.Messages.Web.ToController;
|
||||||
|
using Phantom.Common.Messages.Web.ToWeb;
|
||||||
|
using Phantom.Controller.Minecraft;
|
||||||
|
using Phantom.Controller.Services.Agents;
|
||||||
|
using Phantom.Controller.Services.Events;
|
||||||
|
using Phantom.Controller.Services.Instances;
|
||||||
|
using Phantom.Controller.Services.Users;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Logging;
|
||||||
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace Phantom.Controller.Services.Rpc;
|
||||||
|
|
||||||
|
sealed class WebRegistrationHandler : IRegistrationHandler<IMessageToWeb, IMessageToController, RegisterWebMessage> {
|
||||||
|
private static readonly ILogger Logger = PhantomLogger.Create<WebRegistrationHandler>();
|
||||||
|
|
||||||
|
private readonly AuthToken webAuthToken;
|
||||||
|
private readonly ControllerState controllerState;
|
||||||
|
private readonly InstanceLogManager instanceLogManager;
|
||||||
|
private readonly UserManager userManager;
|
||||||
|
private readonly RoleManager roleManager;
|
||||||
|
private readonly UserRoleManager userRoleManager;
|
||||||
|
private readonly UserLoginManager userLoginManager;
|
||||||
|
private readonly AuditLogManager auditLogManager;
|
||||||
|
private readonly AgentManager agentManager;
|
||||||
|
private readonly MinecraftVersions minecraftVersions;
|
||||||
|
private readonly EventLogManager eventLogManager;
|
||||||
|
|
||||||
|
public WebRegistrationHandler(AuthToken webAuthToken, ControllerState controllerState, InstanceLogManager instanceLogManager, UserManager userManager, RoleManager roleManager, UserRoleManager userRoleManager, UserLoginManager userLoginManager, AuditLogManager auditLogManager, AgentManager agentManager, MinecraftVersions minecraftVersions, EventLogManager eventLogManager) {
|
||||||
|
this.webAuthToken = webAuthToken;
|
||||||
|
this.controllerState = controllerState;
|
||||||
|
this.userManager = userManager;
|
||||||
|
this.roleManager = roleManager;
|
||||||
|
this.userRoleManager = userRoleManager;
|
||||||
|
this.userLoginManager = userLoginManager;
|
||||||
|
this.auditLogManager = auditLogManager;
|
||||||
|
this.agentManager = agentManager;
|
||||||
|
this.minecraftVersions = minecraftVersions;
|
||||||
|
this.eventLogManager = eventLogManager;
|
||||||
|
this.instanceLogManager = instanceLogManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<Props<IMessageToController>?> IRegistrationHandler<IMessageToWeb, IMessageToController, RegisterWebMessage>.TryRegister(RpcConnectionToClient<IMessageToWeb> connection, RegisterWebMessage message) {
|
||||||
|
return await TryRegisterImpl(connection, message) ? CreateMessageHandlerActorProps(connection) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> TryRegisterImpl(RpcConnectionToClient<IMessageToWeb> connection, RegisterWebMessage message) {
|
||||||
|
if (webAuthToken.FixedTimeEquals(message.AuthToken)) {
|
||||||
|
Logger.Information("Web authorized successfully.");
|
||||||
|
await connection.Send(new RegisterWebResultMessage(true));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Logger.Warning("Web failed to authorize, invalid token.");
|
||||||
|
await connection.Send(new RegisterWebResultMessage(false));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Props<IMessageToController> CreateMessageHandlerActorProps(RpcConnectionToClient<IMessageToWeb> connection) {
|
||||||
|
var init = new WebMessageHandlerActor.Init(connection, this, controllerState, instanceLogManager, userManager, roleManager, userRoleManager, userLoginManager, auditLogManager, agentManager, minecraftVersions, eventLogManager);
|
||||||
|
return WebMessageHandlerActor.Factory(init);
|
||||||
|
}
|
||||||
|
}
|
@ -54,24 +54,23 @@ try {
|
|||||||
PhantomLogger.Root.InformationHeading("Launching Phantom Panel server...");
|
PhantomLogger.Root.InformationHeading("Launching Phantom Panel server...");
|
||||||
|
|
||||||
var dbContextFactory = new ApplicationDbContextFactory(sqlConnectionString);
|
var dbContextFactory = new ApplicationDbContextFactory(sqlConnectionString);
|
||||||
|
|
||||||
await using (var controllerServices = new ControllerServices(dbContextFactory, agentKeyData.AuthToken, webKeyData.AuthToken, shutdownCancellationToken)) {
|
|
||||||
await controllerServices.Initialize();
|
|
||||||
|
|
||||||
static RpcConfiguration ConfigureRpc(string serviceName, string host, ushort port, ConnectionKeyData connectionKey) {
|
using var controllerServices = new ControllerServices(dbContextFactory, agentKeyData.AuthToken, webKeyData.AuthToken, shutdownCancellationToken);
|
||||||
return new RpcConfiguration("Rpc:" + serviceName, host, port, connectionKey.Certificate);
|
await controllerServices.Initialize();
|
||||||
}
|
|
||||||
|
|
||||||
var rpcTaskManager = new TaskManager(PhantomLogger.Create<TaskManager>("Rpc"));
|
static RpcConfiguration ConfigureRpc(string serviceName, string host, ushort port, ConnectionKeyData connectionKey) {
|
||||||
try {
|
return new RpcConfiguration(serviceName, host, port, connectionKey.Certificate);
|
||||||
await Task.WhenAll(
|
}
|
||||||
RpcServerRuntime.Launch(ConfigureRpc("Agent", agentRpcServerHost, agentRpcServerPort, agentKeyData), AgentMessageRegistries.Definitions, controllerServices.CreateAgentMessageListener, shutdownCancellationToken),
|
|
||||||
RpcServerRuntime.Launch(ConfigureRpc("Web", webRpcServerHost, webRpcServerPort, webKeyData), WebMessageRegistries.Definitions, controllerServices.CreateWebMessageListener, shutdownCancellationToken)
|
var rpcTaskManager = new TaskManager(PhantomLogger.Create<TaskManager>("Rpc"));
|
||||||
);
|
try {
|
||||||
} finally {
|
await Task.WhenAll(
|
||||||
await rpcTaskManager.Stop();
|
RpcServerRuntime.Launch(ConfigureRpc("Agent", agentRpcServerHost, agentRpcServerPort, agentKeyData), AgentMessageRegistries.Definitions, controllerServices.AgentRegistrationHandler, controllerServices.ActorSystem, shutdownCancellationToken),
|
||||||
NetMQConfig.Cleanup();
|
RpcServerRuntime.Launch(ConfigureRpc("Web", webRpcServerHost, webRpcServerPort, webKeyData), WebMessageRegistries.Definitions, controllerServices.WebRegistrationHandler, controllerServices.ActorSystem, shutdownCancellationToken)
|
||||||
}
|
);
|
||||||
|
} finally {
|
||||||
|
await rpcTaskManager.Stop();
|
||||||
|
NetMQConfig.Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -5,6 +5,7 @@ namespace Phantom.Utils.Actor;
|
|||||||
public readonly struct ActorConfiguration {
|
public readonly struct ActorConfiguration {
|
||||||
public SupervisorStrategy? SupervisorStrategy { get; init; }
|
public SupervisorStrategy? SupervisorStrategy { get; init; }
|
||||||
public string? MailboxType { get; init; }
|
public string? MailboxType { get; init; }
|
||||||
|
public int? StashCapacity { get; init; }
|
||||||
|
|
||||||
internal Props Apply(Props props) {
|
internal Props Apply(Props props) {
|
||||||
if (SupervisorStrategy != null) {
|
if (SupervisorStrategy != null) {
|
||||||
@ -14,6 +15,10 @@ public readonly struct ActorConfiguration {
|
|||||||
if (MailboxType != null) {
|
if (MailboxType != null) {
|
||||||
props = props.WithMailbox(MailboxType);
|
props = props.WithMailbox(MailboxType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StashCapacity != null) {
|
||||||
|
props = props.WithStashCapacity(StashCapacity.Value);
|
||||||
|
}
|
||||||
|
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
@ -28,4 +28,8 @@ public readonly struct ActorRef<TMessage> {
|
|||||||
public Task<TReply> Request<TReply>(ICanReply<TReply> message, CancellationToken cancellationToken = default) {
|
public Task<TReply> Request<TReply>(ICanReply<TReply> message, CancellationToken cancellationToken = default) {
|
||||||
return Request(message, timeout: null, cancellationToken);
|
return Request(message, timeout: null, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<bool> Stop(TimeSpan? timeout = null) {
|
||||||
|
return actorRef.GracefulStop(timeout ?? Timeout.InfiniteTimeSpan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
namespace Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
public interface IMessage<TListener, TReply> {
|
|
||||||
MessageQueueKey QueueKey { get; }
|
|
||||||
Task<TReply> Accept(TListener listener);
|
|
||||||
}
|
|
@ -1,9 +1,6 @@
|
|||||||
namespace Phantom.Utils.Rpc.Message;
|
namespace Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
public interface IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> where TReplyMessage : IMessage<TClientListener, NoReply>, IMessage<TServerListener, NoReply> {
|
public interface IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> : IReplyMessageFactory<TReplyMessage> where TReplyMessage : TClientMessage, TServerMessage {
|
||||||
MessageRegistry<TClientListener> ToClient { get; }
|
MessageRegistry<TClientMessage> ToClient { get; }
|
||||||
MessageRegistry<TServerListener> ToServer { get; }
|
MessageRegistry<TServerMessage> ToServer { get; }
|
||||||
|
|
||||||
bool IsRegistrationMessage(Type messageType);
|
|
||||||
TReplyMessage CreateReplyMessage(uint sequenceId, byte[] serializedReply);
|
|
||||||
}
|
}
|
||||||
|
5
Utils/Phantom.Utils.Rpc/Message/IReplyMessageFactory.cs
Normal file
5
Utils/Phantom.Utils.Rpc/Message/IReplyMessageFactory.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
public interface IReplyMessageFactory<TReplyMessage> {
|
||||||
|
TReplyMessage CreateReplyMessage(uint sequenceId, byte[] serializedReply);
|
||||||
|
}
|
5
Utils/Phantom.Utils.Rpc/Message/IReplySender.cs
Normal file
5
Utils/Phantom.Utils.Rpc/Message/IReplySender.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
interface IReplySender {
|
||||||
|
Task SendReply(uint sequenceId, byte[] serializedReply);
|
||||||
|
}
|
@ -1,41 +1,35 @@
|
|||||||
using Phantom.Utils.Logging;
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Logging;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Message;
|
namespace Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
abstract class MessageHandler<TListener> {
|
sealed class MessageHandler<TMessageBase> {
|
||||||
protected ILogger Logger { get; }
|
private readonly ILogger logger;
|
||||||
|
private readonly ActorRef<TMessageBase> handlerActor;
|
||||||
|
private readonly IReplySender replySender;
|
||||||
|
|
||||||
private readonly TListener listener;
|
public MessageHandler(string loggerName, ActorRef<TMessageBase> handlerActor, IReplySender replySender) {
|
||||||
private readonly MessageQueues messageQueues;
|
this.logger = PhantomLogger.Create("MessageHandler", loggerName);
|
||||||
|
this.handlerActor = handlerActor;
|
||||||
|
this.replySender = replySender;
|
||||||
|
}
|
||||||
|
|
||||||
protected MessageHandler(string loggerName, TListener listener) {
|
public void Tell(TMessageBase message) {
|
||||||
this.Logger = PhantomLogger.Create("MessageHandler", loggerName);
|
handlerActor.Tell(message);
|
||||||
this.listener = listener;
|
|
||||||
this.messageQueues = new MessageQueues(loggerName + ":Receive");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Enqueue<TMessage, TReply>(uint sequenceId, TMessage message) where TMessage : IMessage<TListener, TReply> {
|
public Task TellAndReply<TMessage, TReply>(TMessage message, uint sequenceId) where TMessage : ICanReply<TReply> {
|
||||||
messageQueues.Enqueue(message.QueueKey, () => TryHandle<TMessage, TReply>(sequenceId, message));
|
return handlerActor.Request(message).ContinueWith(task => {
|
||||||
}
|
if (task.IsCompletedSuccessfully) {
|
||||||
|
return replySender.SendReply(sequenceId, MessageSerializer.Serialize(task.Result));
|
||||||
private async Task TryHandle<TMessage, TReply>(uint sequenceId, TMessage message) where TMessage : IMessage<TListener, TReply> {
|
}
|
||||||
TReply reply;
|
|
||||||
try {
|
if (task.IsFaulted) {
|
||||||
reply = await message.Accept(listener);
|
logger.Error(task.Exception, "Failed to handle message {Type}.", message.GetType().Name);
|
||||||
} catch (Exception e) {
|
}
|
||||||
Logger.Error(e, "Failed to handle message {Type}.", message.GetType().Name);
|
|
||||||
return;
|
return task;
|
||||||
}
|
}, TaskScheduler.Default);
|
||||||
|
|
||||||
if (reply is not NoReply) {
|
|
||||||
await SendReply(sequenceId, MessageSerializer.Serialize(reply));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Task SendReply(uint sequenceId, byte[] serializedReply);
|
|
||||||
|
|
||||||
internal Task StopReceiving() {
|
|
||||||
return messageQueues.Stop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
namespace Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
public sealed class MessageQueueKey {
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public MessageQueueKey(string name) {
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
using Phantom.Utils.Logging;
|
|
||||||
using Phantom.Utils.Tasks;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
sealed class MessageQueues {
|
|
||||||
private readonly ILogger logger;
|
|
||||||
private readonly TaskManager taskManager;
|
|
||||||
private readonly Dictionary<MessageQueueKey, RpcQueue> queues = new ();
|
|
||||||
|
|
||||||
private Task? stopTask;
|
|
||||||
|
|
||||||
public MessageQueues(string loggerName) {
|
|
||||||
this.logger = PhantomLogger.Create<MessageQueues>(loggerName);
|
|
||||||
this.taskManager = new TaskManager(PhantomLogger.Create<TaskManager>(loggerName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private RpcQueue GetOrCreateQueue(MessageQueueKey key) {
|
|
||||||
if (!queues.TryGetValue(key, out var queue)) {
|
|
||||||
queues[key] = queue = new RpcQueue(taskManager, "Message queue for " + key.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Enqueue(MessageQueueKey key, Func<Task> task) {
|
|
||||||
lock (this) {
|
|
||||||
return stopTask == null ? GetOrCreateQueue(key).Enqueue(task) : Task.FromException(new OperationCanceledException());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<T> Enqueue<T>(MessageQueueKey key, Func<Task<T>> task) {
|
|
||||||
lock (this) {
|
|
||||||
return stopTask == null ? GetOrCreateQueue(key).Enqueue(task) : Task.FromException<T>(new OperationCanceledException());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Task Stop() {
|
|
||||||
lock (this) {
|
|
||||||
if (stopTask == null) {
|
|
||||||
logger.Debug("Stopping " + queues.Count + " message queue(s)...");
|
|
||||||
|
|
||||||
stopTask = Task.WhenAll(queues.Values.Select(static queue => queue.Stop()))
|
|
||||||
.ContinueWith(_ => logger.Debug("All queues stopped."));
|
|
||||||
|
|
||||||
queues.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return stopTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +1,49 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Message;
|
namespace Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
public sealed class MessageRegistry<TListener> {
|
public sealed class MessageRegistry<TMessageBase> {
|
||||||
private const int DefaultBufferSize = 512;
|
private const int DefaultBufferSize = 512;
|
||||||
|
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
private readonly Dictionary<Type, ushort> typeToCodeMapping = new ();
|
private readonly Dictionary<Type, ushort> typeToCodeMapping = new ();
|
||||||
private readonly Dictionary<ushort, Type> codeToTypeMapping = new ();
|
private readonly Dictionary<ushort, Type> codeToTypeMapping = new ();
|
||||||
private readonly Dictionary<ushort, Action<ReadOnlyMemory<byte>, ushort, MessageHandler<TListener>>> codeToHandlerMapping = new ();
|
private readonly Dictionary<ushort, Action<ReadOnlyMemory<byte>, ushort, MessageHandler<TMessageBase>>> codeToHandlerMapping = new ();
|
||||||
|
|
||||||
public MessageRegistry(ILogger logger) {
|
public MessageRegistry(ILogger logger) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add<TMessage>(ushort code) where TMessage : IMessage<TListener, NoReply> {
|
public void Add<TMessage>(ushort code) where TMessage : TMessageBase {
|
||||||
AddTypeCodeMapping<TMessage, NoReply>(code);
|
if (HasReplyType(typeof(TMessage))) {
|
||||||
|
throw new ArgumentException("This overload is for messages without a reply");
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTypeCodeMapping<TMessage>(code);
|
||||||
codeToHandlerMapping.Add(code, DeserializationHandler<TMessage>);
|
codeToHandlerMapping.Add(code, DeserializationHandler<TMessage>);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add<TMessage, TReply>(ushort code) where TMessage : IMessage<TListener, TReply> {
|
public void Add<TMessage, TReply>(ushort code) where TMessage : TMessageBase, ICanReply<TReply> {
|
||||||
if (typeof(TReply) == typeof(NoReply)) {
|
AddTypeCodeMapping<TMessage>(code);
|
||||||
throw new InvalidOperationException("This overload of Add must not be used with NoReply as the reply type!");
|
|
||||||
}
|
|
||||||
|
|
||||||
AddTypeCodeMapping<TMessage, TReply>(code);
|
|
||||||
codeToHandlerMapping.Add(code, DeserializationHandler<TMessage, TReply>);
|
codeToHandlerMapping.Add(code, DeserializationHandler<TMessage, TReply>);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTypeCodeMapping<TMessage, TReply>(ushort code) where TMessage : IMessage<TListener, TReply> {
|
private void AddTypeCodeMapping<TMessage>(ushort code) where TMessage : TMessageBase {
|
||||||
typeToCodeMapping.Add(typeof(TMessage), code);
|
typeToCodeMapping.Add(typeof(TMessage), code);
|
||||||
codeToTypeMapping.Add(code, typeof(TMessage));
|
codeToTypeMapping.Add(code, typeof(TMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool HasReplyType(Type messageType) {
|
||||||
|
string replyInterfaceName = typeof(ICanReply<object>).FullName!;
|
||||||
|
replyInterfaceName = replyInterfaceName[..(replyInterfaceName.IndexOf('`') + 1)];
|
||||||
|
|
||||||
|
return messageType.GetInterfaces().Any(type => type.FullName is {} name && name.StartsWith(replyInterfaceName, StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
internal bool TryGetType(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out Type? type) {
|
internal bool TryGetType(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out Type? type) {
|
||||||
try {
|
try {
|
||||||
var code = MessageSerializer.ReadCode(ref data);
|
var code = MessageSerializer.ReadCode(ref data);
|
||||||
@ -46,13 +54,27 @@ public sealed class MessageRegistry<TListener> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Write<TMessage>(TMessage message) where TMessage : IMessage<TListener, NoReply> {
|
public ReadOnlySpan<byte> Write<TMessage>(TMessage message) where TMessage : TMessageBase {
|
||||||
return Write<TMessage, NoReply>(0, message);
|
if (!GetMessageCode<TMessage>(out var code)) {
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = new ArrayBufferWriter<byte>(DefaultBufferSize);
|
||||||
|
|
||||||
|
try {
|
||||||
|
MessageSerializer.WriteCode(buffer, code);
|
||||||
|
MessageSerializer.Serialize(buffer, message);
|
||||||
|
|
||||||
|
CheckWrittenBufferLength<TMessage>(buffer);
|
||||||
|
return buffer.WrittenSpan;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogWriteFailure<TMessage>(e);
|
||||||
|
return default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Write<TMessage, TReply>(uint sequenceId, TMessage message) where TMessage : IMessage<TListener, TReply> {
|
public ReadOnlySpan<byte> Write<TMessage, TReply>(uint sequenceId, TMessage message) where TMessage : TMessageBase, ICanReply<TReply> {
|
||||||
if (!typeToCodeMapping.TryGetValue(typeof(TMessage), out ushort code)) {
|
if (!GetMessageCode<TMessage>(out var code)) {
|
||||||
logger.Error("Unknown message type {Type}.", typeof(TMessage));
|
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,30 +82,49 @@ public sealed class MessageRegistry<TListener> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
MessageSerializer.WriteCode(buffer, code);
|
MessageSerializer.WriteCode(buffer, code);
|
||||||
|
MessageSerializer.WriteSequenceId(buffer, sequenceId);
|
||||||
if (typeof(TReply) != typeof(NoReply)) {
|
|
||||||
MessageSerializer.WriteSequenceId(buffer, sequenceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageSerializer.Serialize(buffer, message);
|
MessageSerializer.Serialize(buffer, message);
|
||||||
|
|
||||||
if (buffer.WrittenCount > DefaultBufferSize && logger.IsEnabled(LogEventLevel.Verbose)) {
|
CheckWrittenBufferLength<TMessage>(buffer);
|
||||||
logger.Verbose("Serializing {Type} exceeded default buffer size: {WrittenSize} B > {DefaultBufferSize} B", typeof(TMessage).Name, buffer.WrittenCount, DefaultBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.WrittenSpan;
|
return buffer.WrittenSpan;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.Error(e, "Failed to serialize message {Type}.", typeof(TMessage).Name);
|
LogWriteFailure<TMessage>(e);
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Handle(ReadOnlyMemory<byte> data, MessageHandler<TListener> handler) {
|
private bool GetMessageCode<TMessage>(out ushort code) where TMessage : TMessageBase {
|
||||||
ushort code;
|
if (typeToCodeMapping.TryGetValue(typeof(TMessage), out code)) {
|
||||||
try {
|
return true;
|
||||||
code = MessageSerializer.ReadCode(ref data);
|
}
|
||||||
} catch (Exception e) {
|
else {
|
||||||
logger.Error(e, "Failed to deserialize message code.");
|
logger.Error("Unknown message type {Type}.", typeof(TMessage));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckWrittenBufferLength<TMessage>(ArrayBufferWriter<byte> buffer) where TMessage : TMessageBase {
|
||||||
|
if (buffer.WrittenCount > DefaultBufferSize && logger.IsEnabled(LogEventLevel.Verbose)) {
|
||||||
|
logger.Verbose("Serializing {Type} exceeded default buffer size: {WrittenSize} B > {DefaultBufferSize} B", typeof(TMessage).Name, buffer.WrittenCount, DefaultBufferSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogWriteFailure<TMessage>(Exception e) where TMessage : TMessageBase {
|
||||||
|
logger.Error(e, "Failed to serialize message {Type}.", typeof(TMessage).Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool Read<TMessage>(ReadOnlyMemory<byte> data, out TMessage message) where TMessage : TMessageBase {
|
||||||
|
if (ReadTypeCode(ref data, out ushort code) && codeToTypeMapping.TryGetValue(code, out var expectedType) && expectedType == typeof(TMessage) && ReadMessage(data, out message)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message = default!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Handle(ReadOnlyMemory<byte> data, MessageHandler<TMessageBase> handler) {
|
||||||
|
if (!ReadTypeCode(ref data, out var code)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,31 +136,48 @@ public sealed class MessageRegistry<TListener> {
|
|||||||
handle(data, code, handler);
|
handle(data, code, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeserializationHandler<TMessage>(ReadOnlyMemory<byte> data, ushort code, MessageHandler<TListener> handler) where TMessage : IMessage<TListener, NoReply> {
|
private bool ReadTypeCode(ref ReadOnlyMemory<byte> data, out ushort code) {
|
||||||
DeserializeAndEnqueueMessage<TMessage, NoReply>(data, code, handler, 0);
|
try {
|
||||||
|
code = MessageSerializer.ReadCode(ref data);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
code = default;
|
||||||
|
logger.Error(e, "Failed to deserialize message code.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeserializationHandler<TMessage, TReply>(ReadOnlyMemory<byte> data, ushort code, MessageHandler<TListener> handler) where TMessage : IMessage<TListener, TReply> {
|
private bool ReadSequenceId<TMessage, TReply>(ref ReadOnlyMemory<byte> data, out uint sequenceId) where TMessage : TMessageBase, ICanReply<TReply> {
|
||||||
uint sequenceId;
|
|
||||||
try {
|
try {
|
||||||
sequenceId = MessageSerializer.ReadSequenceId(ref data);
|
sequenceId = MessageSerializer.ReadSequenceId(ref data);
|
||||||
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.Error(e, "Failed to deserialize sequence ID of message with code {Code}.", code);
|
sequenceId = default;
|
||||||
return;
|
logger.Error(e, "Failed to deserialize sequence ID of message {Type}.", typeof(TMessage).Name);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeserializeAndEnqueueMessage<TMessage, TReply>(data, code, handler, sequenceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeserializeAndEnqueueMessage<TMessage, TReply>(ReadOnlyMemory<byte> data, ushort code, MessageHandler<TListener> handler, uint sequenceId) where TMessage : IMessage<TListener, TReply> {
|
private bool ReadMessage<TMessage>(ReadOnlyMemory<byte> data, out TMessage message) where TMessage : TMessageBase {
|
||||||
TMessage message;
|
|
||||||
try {
|
try {
|
||||||
message = MessageSerializer.Deserialize<TMessage>(data);
|
message = MessageSerializer.Deserialize<TMessage>(data);
|
||||||
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.Error(e, "Failed to deserialize message with code {Code}.", code);
|
message = default!;
|
||||||
return;
|
logger.Error(e, "Failed to deserialize message {Type}.", typeof(TMessage).Name);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handler.Enqueue<TMessage, TReply>(sequenceId, message);
|
private void DeserializationHandler<TMessage>(ReadOnlyMemory<byte> data, ushort code, MessageHandler<TMessageBase> handler) where TMessage : TMessageBase {
|
||||||
|
if (ReadMessage<TMessage>(data, out var message)) {
|
||||||
|
handler.Tell(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeserializationHandler<TMessage, TReply>(ReadOnlyMemory<byte> data, ushort code, MessageHandler<TMessageBase> handler) where TMessage : TMessageBase, ICanReply<TReply> {
|
||||||
|
if (ReadSequenceId<TMessage, TReply>(ref data, out var sequenceId) && ReadMessage<TMessage>(data, out var message)) {
|
||||||
|
handler.TellAndReply<TMessage, TReply>(message, sequenceId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ static class MessageSerializer {
|
|||||||
public static T Deserialize<T>(ReadOnlyMemory<byte> memory) {
|
public static T Deserialize<T>(ReadOnlyMemory<byte> memory) {
|
||||||
return MemoryPackSerializer.Deserialize<T>(memory.Span) ?? throw new NullReferenceException();
|
return MemoryPackSerializer.Deserialize<T>(memory.Span) ?? throw new NullReferenceException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteCode(IBufferWriter<byte> destination, ushort value) {
|
public static void WriteCode(IBufferWriter<byte> destination, ushort value) {
|
||||||
Span<byte> buffer = stackalloc byte[2];
|
Span<byte> buffer = stackalloc byte[2];
|
||||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer, value);
|
BinaryPrimitives.WriteUInt16LittleEndian(buffer, value);
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
namespace Phantom.Utils.Rpc.Message;
|
|
||||||
|
|
||||||
public readonly struct NoReply {
|
|
||||||
public static NoReply Instance { get; } = new ();
|
|
||||||
}
|
|
17
Utils/Phantom.Utils.Rpc/Message/ReplySender.cs
Normal file
17
Utils/Phantom.Utils.Rpc/Message/ReplySender.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
|
namespace Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
sealed class ReplySender<TMessageBase, TReplyMessage> : IReplySender where TReplyMessage : TMessageBase {
|
||||||
|
private readonly RpcConnection<TMessageBase> connection;
|
||||||
|
private readonly IReplyMessageFactory<TReplyMessage> replyMessageFactory;
|
||||||
|
|
||||||
|
public ReplySender(RpcConnection<TMessageBase> connection, IReplyMessageFactory<TReplyMessage> replyMessageFactory) {
|
||||||
|
this.connection = connection;
|
||||||
|
this.replyMessageFactory = replyMessageFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SendReply(uint sequenceId, byte[] serializedReply) {
|
||||||
|
return connection.Send(replyMessageFactory.CreateReplyMessage(sequenceId, serializedReply));
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Phantom.Utils.Actor\Phantom.Utils.Actor.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Utils\Phantom.Utils.csproj" />
|
<ProjectReference Include="..\Phantom.Utils\Phantom.Utils.csproj" />
|
||||||
<ProjectReference Include="..\Phantom.Utils.Logging\Phantom.Utils.Logging.csproj" />
|
<ProjectReference Include="..\Phantom.Utils.Logging\Phantom.Utils.Logging.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Phantom.Utils.Rpc;
|
namespace Phantom.Utils.Rpc;
|
||||||
|
|
||||||
public sealed record RpcConfiguration(string LoggerName, string Host, ushort Port, NetMQCertificate ServerCertificate) {
|
public sealed record RpcConfiguration(string ServiceName, string Host, ushort Port, NetMQCertificate ServerCertificate) {
|
||||||
public string TcpUrl => "tcp://" + Host + ":" + Port;
|
internal string LoggerName => "Rpc:" + ServiceName;
|
||||||
|
internal string TcpUrl => "tcp://" + Host + ":" + Port;
|
||||||
}
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
using System.Threading.Channels;
|
|
||||||
using Phantom.Utils.Tasks;
|
|
||||||
|
|
||||||
namespace Phantom.Utils.Rpc;
|
|
||||||
|
|
||||||
sealed class RpcQueue {
|
|
||||||
private readonly Channel<Func<Task>> channel = Channel.CreateUnbounded<Func<Task>>(new UnboundedChannelOptions {
|
|
||||||
SingleReader = true,
|
|
||||||
SingleWriter = false,
|
|
||||||
AllowSynchronousContinuations = false
|
|
||||||
});
|
|
||||||
|
|
||||||
private readonly Task processingTask;
|
|
||||||
|
|
||||||
public RpcQueue(TaskManager taskManager, string taskName) {
|
|
||||||
this.processingTask = taskManager.Run(taskName, Process);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Enqueue(Action action) {
|
|
||||||
return Enqueue(() => {
|
|
||||||
action();
|
|
||||||
return Task.CompletedTask;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Enqueue(Func<Task> task) {
|
|
||||||
var completionSource = AsyncTasks.CreateCompletionSource();
|
|
||||||
|
|
||||||
if (!channel.Writer.TryWrite(() => task().ContinueWith(t => completionSource.SetResultFrom(t)))) {
|
|
||||||
completionSource.SetCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
return completionSource.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<T> Enqueue<T>(Func<Task<T>> task) {
|
|
||||||
var completionSource = AsyncTasks.CreateCompletionSource<T>();
|
|
||||||
|
|
||||||
if (!channel.Writer.TryWrite(() => task().ContinueWith(t => completionSource.SetResultFrom(t)))) {
|
|
||||||
completionSource.SetCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
return completionSource.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Process() {
|
|
||||||
try {
|
|
||||||
await foreach (var task in channel.Reader.ReadAllAsync()) {
|
|
||||||
await task();
|
|
||||||
}
|
|
||||||
} catch (OperationCanceledException) {
|
|
||||||
// Ignore.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Stop() {
|
|
||||||
channel.Writer.Complete();
|
|
||||||
return processingTask;
|
|
||||||
}
|
|
||||||
}
|
|
7
Utils/Phantom.Utils.Rpc/Runtime/IRegistrationHandler.cs
Normal file
7
Utils/Phantom.Utils.Rpc/Runtime/IRegistrationHandler.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
using Phantom.Utils.Actor;
|
||||||
|
|
||||||
|
namespace Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
|
public interface IRegistrationHandler<TClientMessage, TServerMessage, TRegistrationMessage> where TRegistrationMessage : TServerMessage {
|
||||||
|
Task<Props<TServerMessage>?> TryRegister(RpcConnectionToClient<TClientMessage> connection, TRegistrationMessage message);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using NetMQ.Sockets;
|
using NetMQ.Sockets;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
using Phantom.Utils.Rpc.Sockets;
|
using Phantom.Utils.Rpc.Sockets;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -6,18 +7,18 @@ using Serilog.Events;
|
|||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Runtime;
|
namespace Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
public abstract class RpcClientRuntime<TClientListener, TServerListener, TReplyMessage> : RpcRuntime<ClientSocket> where TReplyMessage : IMessage<TClientListener, NoReply>, IMessage<TServerListener, NoReply> {
|
public abstract class RpcClientRuntime<TClientMessage, TServerMessage, TReplyMessage> : RpcRuntime<ClientSocket> where TReplyMessage : TClientMessage, TServerMessage {
|
||||||
private readonly RpcConnectionToServer<TServerListener> connection;
|
private readonly RpcConnectionToServer<TServerMessage> connection;
|
||||||
private readonly IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions;
|
private readonly IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions;
|
||||||
private readonly TClientListener messageListener;
|
private readonly ActorRef<TClientMessage> handlerActor;
|
||||||
|
|
||||||
private readonly SemaphoreSlim disconnectSemaphore;
|
private readonly SemaphoreSlim disconnectSemaphore;
|
||||||
private readonly CancellationToken receiveCancellationToken;
|
private readonly CancellationToken receiveCancellationToken;
|
||||||
|
|
||||||
protected RpcClientRuntime(RpcClientSocket<TClientListener, TServerListener, TReplyMessage> socket, TClientListener messageListener, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) : base(socket) {
|
protected RpcClientRuntime(RpcClientSocket<TClientMessage, TServerMessage, TReplyMessage> socket, ActorRef<TClientMessage> handlerActor, SemaphoreSlim disconnectSemaphore, CancellationToken receiveCancellationToken) : base(socket) {
|
||||||
this.connection = socket.Connection;
|
this.connection = socket.Connection;
|
||||||
this.messageDefinitions = socket.MessageDefinitions;
|
this.messageDefinitions = socket.MessageDefinitions;
|
||||||
this.messageListener = messageListener;
|
this.handlerActor = handlerActor;
|
||||||
this.disconnectSemaphore = disconnectSemaphore;
|
this.disconnectSemaphore = disconnectSemaphore;
|
||||||
this.receiveCancellationToken = receiveCancellationToken;
|
this.receiveCancellationToken = receiveCancellationToken;
|
||||||
}
|
}
|
||||||
@ -26,8 +27,9 @@ public abstract class RpcClientRuntime<TClientListener, TServerListener, TReplyM
|
|||||||
return RunWithConnection(socket, connection);
|
return RunWithConnection(socket, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual async Task RunWithConnection(ClientSocket socket, RpcConnectionToServer<TServerListener> connection) {
|
protected virtual async Task RunWithConnection(ClientSocket socket, RpcConnectionToServer<TServerMessage> connection) {
|
||||||
var handler = new Handler(LoggerName, connection, messageDefinitions, messageListener);
|
var replySender = new ReplySender<TServerMessage, TReplyMessage>(connection, messageDefinitions);
|
||||||
|
var messageHandler = new MessageHandler<TClientMessage>(LoggerName, handlerActor, replySender);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (!receiveCancellationToken.IsCancellationRequested) {
|
while (!receiveCancellationToken.IsCancellationRequested) {
|
||||||
@ -36,13 +38,13 @@ public abstract class RpcClientRuntime<TClientListener, TServerListener, TReplyM
|
|||||||
LogMessageType(RuntimeLogger, data);
|
LogMessageType(RuntimeLogger, data);
|
||||||
|
|
||||||
if (data.Length > 0) {
|
if (data.Length > 0) {
|
||||||
messageDefinitions.ToClient.Handle(data, handler);
|
messageDefinitions.ToClient.Handle(data, messageHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (OperationCanceledException) {
|
} catch (OperationCanceledException) {
|
||||||
// Ignore.
|
// Ignore.
|
||||||
} finally {
|
} finally {
|
||||||
await handler.StopReceiving();
|
await handlerActor.Stop();
|
||||||
RuntimeLogger.Debug("ZeroMQ client stopped receiving messages.");
|
RuntimeLogger.Debug("ZeroMQ client stopped receiving messages.");
|
||||||
|
|
||||||
await disconnectSemaphore.WaitAsync(CancellationToken.None);
|
await disconnectSemaphore.WaitAsync(CancellationToken.None);
|
||||||
@ -50,12 +52,6 @@ public abstract class RpcClientRuntime<TClientListener, TServerListener, TReplyM
|
|||||||
}
|
}
|
||||||
|
|
||||||
private protected sealed override async Task Disconnect(ClientSocket socket) {
|
private protected sealed override async Task Disconnect(ClientSocket socket) {
|
||||||
try {
|
|
||||||
await connection.StopSending().WaitAsync(TimeSpan.FromSeconds(10), CancellationToken.None);
|
|
||||||
} catch (TimeoutException) {
|
|
||||||
RuntimeLogger.Error("Timed out waiting for message sending queue.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await SendDisconnectMessage(socket, RuntimeLogger);
|
await SendDisconnectMessage(socket, RuntimeLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,18 +69,4 @@ public abstract class RpcClientRuntime<TClientListener, TServerListener, TReplyM
|
|||||||
logger.Verbose("Received {Bytes} B message.", data.Length);
|
logger.Verbose("Received {Bytes} B message.", data.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class Handler : MessageHandler<TClientListener> {
|
|
||||||
private readonly RpcConnectionToServer<TServerListener> connection;
|
|
||||||
private readonly IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions;
|
|
||||||
|
|
||||||
public Handler(string loggerName, RpcConnectionToServer<TServerListener> connection, IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions, TClientListener listener) : base(loggerName, listener) {
|
|
||||||
this.connection = connection;
|
|
||||||
this.messageDefinitions = messageDefinitions;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task SendReply(uint sequenceId, byte[] serializedReply) {
|
|
||||||
return connection.Send(messageDefinitions.CreateReplyMessage(sequenceId, serializedReply));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,27 @@
|
|||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Runtime;
|
namespace Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
public abstract class RpcConnection<TListener> {
|
public abstract class RpcConnection<TMessageBase> {
|
||||||
private readonly MessageRegistry<TListener> messageRegistry;
|
private readonly MessageRegistry<TMessageBase> messageRegistry;
|
||||||
private readonly MessageQueues sendingQueues;
|
|
||||||
private readonly MessageReplyTracker replyTracker;
|
private readonly MessageReplyTracker replyTracker;
|
||||||
|
|
||||||
internal RpcConnection(string loggerName, MessageRegistry<TListener> messageRegistry, MessageReplyTracker replyTracker) {
|
internal RpcConnection(MessageRegistry<TMessageBase> messageRegistry, MessageReplyTracker replyTracker) {
|
||||||
this.messageRegistry = messageRegistry;
|
this.messageRegistry = messageRegistry;
|
||||||
this.sendingQueues = new MessageQueues(loggerName + ":Send");
|
|
||||||
this.replyTracker = replyTracker;
|
this.replyTracker = replyTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
private protected abstract ValueTask Send(byte[] bytes);
|
private protected abstract ValueTask Send(byte[] bytes);
|
||||||
|
|
||||||
public Task Send<TMessage>(TMessage message) where TMessage : IMessage<TListener, NoReply> {
|
public async Task Send<TMessage>(TMessage message) where TMessage : TMessageBase {
|
||||||
return sendingQueues.Enqueue(message.QueueKey, () => SendTask(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<TReply> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken) where TMessage : IMessage<TListener, TReply> {
|
|
||||||
return sendingQueues.Enqueue(message.QueueKey, () => SendTask<TMessage, TReply>(message, waitForReplyTime, waitForReplyCancellationToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendTask<TMessage>(TMessage message) where TMessage : IMessage<TListener, NoReply> {
|
|
||||||
var bytes = messageRegistry.Write(message).ToArray();
|
var bytes = messageRegistry.Write(message).ToArray();
|
||||||
if (bytes.Length > 0) {
|
if (bytes.Length > 0) {
|
||||||
await Send(bytes);
|
await Send(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<TReply> SendTask<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken) where TMessage : IMessage<TListener, TReply> {
|
public async Task<TReply> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken) where TMessage : TMessageBase, ICanReply<TReply> {
|
||||||
var sequenceId = replyTracker.RegisterReply();
|
var sequenceId = replyTracker.RegisterReply();
|
||||||
|
|
||||||
var bytes = messageRegistry.Write<TMessage, TReply>(sequenceId, message).ToArray();
|
var bytes = messageRegistry.Write<TMessage, TReply>(sequenceId, message).ToArray();
|
||||||
@ -46,8 +37,4 @@ public abstract class RpcConnection<TListener> {
|
|||||||
public void Receive(IReply message) {
|
public void Receive(IReply message) {
|
||||||
replyTracker.ReceiveReply(message.SequenceId, message.SerializedReply);
|
replyTracker.ReceiveReply(message.SequenceId, message.SerializedReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task StopSending() {
|
|
||||||
return sendingQueues.Stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,29 +4,19 @@ using Phantom.Utils.Rpc.Message;
|
|||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Runtime;
|
namespace Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
public sealed class RpcConnectionToClient<TListener> : RpcConnection<TListener> {
|
public sealed class RpcConnectionToClient<TMessageBase> : RpcConnection<TMessageBase> {
|
||||||
private readonly ServerSocket socket;
|
private readonly ServerSocket socket;
|
||||||
private readonly uint routingId;
|
private readonly uint routingId;
|
||||||
|
|
||||||
private readonly TaskCompletionSource<bool> authorizationCompletionSource = new ();
|
|
||||||
|
|
||||||
internal event EventHandler<RpcClientConnectionClosedEventArgs>? Closed;
|
internal event EventHandler<RpcClientConnectionClosedEventArgs>? Closed;
|
||||||
public bool IsClosed { get; private set; }
|
public bool IsClosed { get; private set; }
|
||||||
|
|
||||||
internal RpcConnectionToClient(string loggerName, ServerSocket socket, uint routingId, MessageRegistry<TListener> messageRegistry, MessageReplyTracker replyTracker) : base(loggerName, messageRegistry, replyTracker) {
|
internal RpcConnectionToClient(ServerSocket socket, uint routingId, MessageRegistry<TMessageBase> messageRegistry, MessageReplyTracker replyTracker) : base(messageRegistry, replyTracker) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.routingId = routingId;
|
this.routingId = routingId;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task<bool> GetAuthorization() {
|
public bool IsSame(RpcConnectionToClient<TMessageBase> other) {
|
||||||
return authorizationCompletionSource.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetAuthorizationResult(bool isAuthorized) {
|
|
||||||
authorizationCompletionSource.SetResult(isAuthorized);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSame(RpcConnectionToClient<TListener> other) {
|
|
||||||
return this.routingId == other.routingId && this.socket == other.socket;
|
return this.routingId == other.routingId && this.socket == other.socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,13 +5,13 @@ using Phantom.Utils.Tasks;
|
|||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Runtime;
|
namespace Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
public sealed class RpcConnectionToServer<TListener> : RpcConnection<TListener> {
|
public sealed class RpcConnectionToServer<TMessageBase> : RpcConnection<TMessageBase> {
|
||||||
private readonly ClientSocket socket;
|
private readonly ClientSocket socket;
|
||||||
private readonly TaskCompletionSource isReady = AsyncTasks.CreateCompletionSource();
|
private readonly TaskCompletionSource isReady = AsyncTasks.CreateCompletionSource();
|
||||||
|
|
||||||
public Task IsReady => isReady.Task;
|
public Task IsReady => isReady.Task;
|
||||||
|
|
||||||
internal RpcConnectionToServer(string loggerName, ClientSocket socket, MessageRegistry<TListener> messageRegistry, MessageReplyTracker replyTracker) : base(loggerName, messageRegistry, replyTracker) {
|
internal RpcConnectionToServer(ClientSocket socket, MessageRegistry<TMessageBase> messageRegistry, MessageReplyTracker replyTracker) : base(messageRegistry, replyTracker) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75
Utils/Phantom.Utils.Rpc/Runtime/RpcReceiverActor.cs
Normal file
75
Utils/Phantom.Utils.Rpc/Runtime/RpcReceiverActor.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
using Akka.Actor;
|
||||||
|
using Akka.Event;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Rpc.Message;
|
||||||
|
|
||||||
|
namespace Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
|
sealed class RpcReceiverActor<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage> : ReceiveActor<RpcReceiverActor<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>.ReceiveMessageCommand>, IWithStash where TRegistrationMessage : TServerMessage where TReplyMessage : TClientMessage, TServerMessage {
|
||||||
|
public readonly record struct Init(
|
||||||
|
string LoggerName,
|
||||||
|
IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> MessageDefinitions,
|
||||||
|
IRegistrationHandler<TClientMessage, TServerMessage, TRegistrationMessage> RegistrationHandler,
|
||||||
|
RpcConnectionToClient<TClientMessage> Connection
|
||||||
|
);
|
||||||
|
|
||||||
|
public static Props<ReceiveMessageCommand> Factory(Init init) {
|
||||||
|
return Props<ReceiveMessageCommand>.Create(() => new RpcReceiverActor<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>(init), new ActorConfiguration {
|
||||||
|
SupervisorStrategy = SupervisorStrategies.Resume,
|
||||||
|
StashCapacity = 100
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public IStash Stash { get; set; } = null!;
|
||||||
|
|
||||||
|
private readonly string loggerName;
|
||||||
|
private readonly IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions;
|
||||||
|
private readonly IRegistrationHandler<TClientMessage, TServerMessage, TRegistrationMessage> registrationHandler;
|
||||||
|
private readonly RpcConnectionToClient<TClientMessage> connection;
|
||||||
|
|
||||||
|
private RpcReceiverActor(Init init) {
|
||||||
|
this.loggerName = init.LoggerName;
|
||||||
|
this.messageDefinitions = init.MessageDefinitions;
|
||||||
|
this.registrationHandler = init.RegistrationHandler;
|
||||||
|
this.connection = init.Connection;
|
||||||
|
|
||||||
|
ReceiveAsync<ReceiveMessageCommand>(ReceiveMessageUnauthorized);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record ReceiveMessageCommand(Type MessageType, ReadOnlyMemory<byte> Data);
|
||||||
|
|
||||||
|
private async Task ReceiveMessageUnauthorized(ReceiveMessageCommand command) {
|
||||||
|
if (command.MessageType == typeof(TRegistrationMessage)) {
|
||||||
|
await HandleRegistrationMessage(command);
|
||||||
|
}
|
||||||
|
else if (Stash.IsFull) {
|
||||||
|
Context.GetLogger().Warning("Stash is full, dropping message: {MessageType}", command.MessageType);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Stash.Stash();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleRegistrationMessage(ReceiveMessageCommand command) {
|
||||||
|
if (!messageDefinitions.ToServer.Read(command.Data, out TRegistrationMessage message)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var props = await registrationHandler.TryRegister(connection, message);
|
||||||
|
if (props == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handlerActor = Context.ActorOf(props, "Handler");
|
||||||
|
var replySender = new ReplySender<TClientMessage, TReplyMessage>(connection, messageDefinitions);
|
||||||
|
BecomeAuthorized(new MessageHandler<TServerMessage>(loggerName, handlerActor, replySender));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BecomeAuthorized(MessageHandler<TServerMessage> handler) {
|
||||||
|
Stash.UnstashAll();
|
||||||
|
|
||||||
|
Become(() => {
|
||||||
|
Receive<ReceiveMessageCommand>(command => messageDefinitions.ToServer.Handle(command.Data, handler));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +1,44 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using Akka.Actor;
|
||||||
using NetMQ.Sockets;
|
using NetMQ.Sockets;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Logging;
|
using Phantom.Utils.Logging;
|
||||||
using Phantom.Utils.Rpc.Message;
|
using Phantom.Utils.Rpc.Message;
|
||||||
using Phantom.Utils.Rpc.Sockets;
|
using Phantom.Utils.Rpc.Sockets;
|
||||||
using Phantom.Utils.Tasks;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Runtime;
|
namespace Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
public static class RpcServerRuntime {
|
public static class RpcServerRuntime {
|
||||||
public static Task Launch<TClientListener, TServerListener, TReplyMessage>(RpcConfiguration config, IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions, Func<RpcConnectionToClient<TClientListener>, TServerListener> listenerFactory, CancellationToken cancellationToken) where TReplyMessage : IMessage<TClientListener, NoReply>, IMessage<TServerListener, NoReply> {
|
public static Task Launch<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>(
|
||||||
return RpcServerRuntime<TClientListener, TServerListener, TReplyMessage>.Launch(config, messageDefinitions, listenerFactory, cancellationToken);
|
RpcConfiguration config,
|
||||||
|
IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions,
|
||||||
|
IRegistrationHandler<TClientMessage, TServerMessage, TRegistrationMessage> registrationHandler,
|
||||||
|
IActorRefFactory actorSystem,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
) where TRegistrationMessage : TServerMessage where TReplyMessage : TClientMessage, TServerMessage {
|
||||||
|
return RpcServerRuntime<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>.Launch(config, messageDefinitions, registrationHandler, actorSystem, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class RpcServerRuntime<TClientListener, TServerListener, TReplyMessage> : RpcRuntime<ServerSocket> where TReplyMessage : IMessage<TClientListener, NoReply>, IMessage<TServerListener, NoReply> {
|
internal sealed class RpcServerRuntime<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage> : RpcRuntime<ServerSocket> where TRegistrationMessage : TServerMessage where TReplyMessage : TClientMessage, TServerMessage {
|
||||||
internal static Task Launch(RpcConfiguration config, IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions, Func<RpcConnectionToClient<TClientListener>, TServerListener> listenerFactory, CancellationToken cancellationToken) {
|
internal static Task Launch(RpcConfiguration config, IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions, IRegistrationHandler<TClientMessage, TServerMessage, TRegistrationMessage> registrationHandler, IActorRefFactory actorSystem, CancellationToken cancellationToken) {
|
||||||
var socket = RpcServerSocket.Connect(config);
|
var socket = RpcServerSocket.Connect(config);
|
||||||
return new RpcServerRuntime<TClientListener, TServerListener, TReplyMessage>(socket, messageDefinitions, listenerFactory, cancellationToken).Launch();
|
return new RpcServerRuntime<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>(socket, messageDefinitions, registrationHandler, actorSystem, cancellationToken).Launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions;
|
private readonly string serviceName;
|
||||||
private readonly Func<RpcConnectionToClient<TClientListener>, TServerListener> listenerFactory;
|
private readonly IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions;
|
||||||
private readonly TaskManager taskManager;
|
private readonly IRegistrationHandler<TClientMessage, TServerMessage, TRegistrationMessage> registrationHandler;
|
||||||
|
private readonly IActorRefFactory actorSystem;
|
||||||
private readonly CancellationToken cancellationToken;
|
private readonly CancellationToken cancellationToken;
|
||||||
|
|
||||||
private RpcServerRuntime(RpcServerSocket socket, IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions, Func<RpcConnectionToClient<TClientListener>, TServerListener> listenerFactory, CancellationToken cancellationToken) : base(socket) {
|
private RpcServerRuntime(RpcServerSocket socket, IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions, IRegistrationHandler<TClientMessage, TServerMessage, TRegistrationMessage> registrationHandler, IActorRefFactory actorSystem, CancellationToken cancellationToken) : base(socket) {
|
||||||
|
this.serviceName = socket.Config.ServiceName;
|
||||||
this.messageDefinitions = messageDefinitions;
|
this.messageDefinitions = messageDefinitions;
|
||||||
this.listenerFactory = listenerFactory;
|
this.registrationHandler = registrationHandler;
|
||||||
this.taskManager = new TaskManager(PhantomLogger.Create<TaskManager>(socket.Config.LoggerName + ":Runtime"));
|
this.actorSystem = actorSystem;
|
||||||
this.cancellationToken = cancellationToken;
|
this.cancellationToken = cancellationToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,31 +66,30 @@ internal sealed class RpcServerRuntime<TClientListener, TServerListener, TReplyM
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!clients.TryGetValue(routingId, out var client)) {
|
if (!clients.TryGetValue(routingId, out var client)) {
|
||||||
if (!messageDefinitions.IsRegistrationMessage(messageType)) {
|
if (messageType != typeof(TRegistrationMessage)) {
|
||||||
RuntimeLogger.Warning("Received {MessageType} ({Bytes} B) from unregistered client {RoutingId}.", messageType.Name, data.Length, routingId);
|
RuntimeLogger.Warning("Received {MessageType} ({Bytes} B) from unregistered client {RoutingId}.", messageType.Name, data.Length, routingId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientLoggerName = LoggerName + ":" + routingId;
|
var clientLoggerName = LoggerName + ":" + routingId;
|
||||||
var processingQueue = new RpcQueue(taskManager, "Process messages from " + routingId);
|
var clientActorName = "Rpc-" + serviceName + "-" + routingId;
|
||||||
var connection = new RpcConnectionToClient<TClientListener>(clientLoggerName, socket, routingId, messageDefinitions.ToClient, ReplyTracker);
|
|
||||||
|
|
||||||
|
// TODO add pings and tear down connection after too much inactivity
|
||||||
|
var connection = new RpcConnectionToClient<TClientMessage>(socket, routingId, messageDefinitions.ToClient, ReplyTracker);
|
||||||
connection.Closed += OnConnectionClosed;
|
connection.Closed += OnConnectionClosed;
|
||||||
|
|
||||||
client = new Client(clientLoggerName, connection, processingQueue, messageDefinitions, listenerFactory(connection), taskManager);
|
client = new Client(clientLoggerName, clientActorName, connection, actorSystem, messageDefinitions, registrationHandler);
|
||||||
clients[routingId] = client;
|
clients[routingId] = client;
|
||||||
client.EnqueueRegistrationMessage(messageType, data);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
client.Enqueue(messageType, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.Enqueue(messageType, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var client in clients.Values) {
|
foreach (var client in clients.Values) {
|
||||||
client.Connection.Close();
|
client.Connection.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return taskManager.Stop();
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogUnknownMessage(uint routingId, ReadOnlyMemory<byte> data) {
|
private void LogUnknownMessage(uint routingId, ReadOnlyMemory<byte> data) {
|
||||||
@ -91,66 +100,38 @@ internal sealed class RpcServerRuntime<TClientListener, TServerListener, TReplyM
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class Client : MessageHandler<TServerListener> {
|
private sealed class Client {
|
||||||
public RpcConnectionToClient<TClientListener> Connection { get; }
|
public RpcConnectionToClient<TClientMessage> Connection { get; }
|
||||||
|
|
||||||
private readonly RpcQueue processingQueue;
|
private readonly ILogger logger;
|
||||||
private readonly IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions;
|
private readonly ActorRef<RpcReceiverActor<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>.ReceiveMessageCommand> receiverActor;
|
||||||
private readonly TaskManager taskManager;
|
|
||||||
|
public Client(string loggerName, string actorName, RpcConnectionToClient<TClientMessage> connection, IActorRefFactory actorSystem, IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions, IRegistrationHandler<TClientMessage, TServerMessage, TRegistrationMessage> registrationHandler) {
|
||||||
public Client(string loggerName, RpcConnectionToClient<TClientListener> connection, RpcQueue processingQueue, IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions, TServerListener listener, TaskManager taskManager) : base(loggerName, listener) {
|
|
||||||
this.Connection = connection;
|
this.Connection = connection;
|
||||||
this.Connection.Closed += OnConnectionClosed;
|
this.Connection.Closed += OnConnectionClosed;
|
||||||
|
|
||||||
this.processingQueue = processingQueue;
|
|
||||||
this.messageDefinitions = messageDefinitions;
|
|
||||||
this.taskManager = taskManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void EnqueueRegistrationMessage(Type messageType, ReadOnlyMemory<byte> data) {
|
this.logger = PhantomLogger.Create(loggerName);
|
||||||
LogMessageType(messageType, data);
|
|
||||||
processingQueue.Enqueue(() => Handle(data));
|
var receiverActorInit = new RpcReceiverActor<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>.Init(loggerName, messageDefinitions, registrationHandler, Connection);
|
||||||
|
this.receiverActor = actorSystem.ActorOf(RpcReceiverActor<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>.Factory(receiverActorInit), actorName + "-Receiver");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Enqueue(Type messageType, ReadOnlyMemory<byte> data) {
|
internal void Enqueue(Type messageType, ReadOnlyMemory<byte> data) {
|
||||||
LogMessageType(messageType, data);
|
LogMessageType(messageType, data);
|
||||||
processingQueue.Enqueue(() => WaitForAuthorizationAndHandle(data));
|
receiverActor.Tell(new RpcReceiverActor<TClientMessage, TServerMessage, TRegistrationMessage, TReplyMessage>.ReceiveMessageCommand(messageType, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogMessageType(Type messageType, ReadOnlyMemory<byte> data) {
|
private void LogMessageType(Type messageType, ReadOnlyMemory<byte> data) {
|
||||||
if (Logger.IsEnabled(LogEventLevel.Verbose)) {
|
if (logger.IsEnabled(LogEventLevel.Verbose)) {
|
||||||
Logger.Verbose("Received {MessageType} ({Bytes} B).", messageType.Name, data.Length);
|
logger.Verbose("Received {MessageType} ({Bytes} B).", messageType.Name, data.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Handle(ReadOnlyMemory<byte> data) {
|
|
||||||
messageDefinitions.ToServer.Handle(data, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task WaitForAuthorizationAndHandle(ReadOnlyMemory<byte> data) {
|
|
||||||
if (await Connection.GetAuthorization()) {
|
|
||||||
Handle(data);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Logger.Warning("Dropped message after failed registration.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task SendReply(uint sequenceId, byte[] serializedReply) {
|
|
||||||
return Connection.Send(messageDefinitions.CreateReplyMessage(sequenceId, serializedReply));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnConnectionClosed(object? sender, RpcClientConnectionClosedEventArgs e) {
|
private void OnConnectionClosed(object? sender, RpcClientConnectionClosedEventArgs e) {
|
||||||
Connection.Closed -= OnConnectionClosed;
|
Connection.Closed -= OnConnectionClosed;
|
||||||
|
|
||||||
Logger.Debug("Closing connection...");
|
logger.Debug("Closing connection...");
|
||||||
|
receiverActor.Stop();
|
||||||
taskManager.Run("Closing connection to " + e.RoutingId, async () => {
|
|
||||||
await StopReceiving();
|
|
||||||
await processingQueue.Stop();
|
|
||||||
await Connection.StopSending();
|
|
||||||
Logger.Debug("Connection closed.");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,13 @@ using Phantom.Utils.Rpc.Runtime;
|
|||||||
namespace Phantom.Utils.Rpc.Sockets;
|
namespace Phantom.Utils.Rpc.Sockets;
|
||||||
|
|
||||||
public static class RpcClientSocket {
|
public static class RpcClientSocket {
|
||||||
public static RpcClientSocket<TClientListener, TServerListener, TReplyMessage> Connect<TClientListener, TServerListener, TReplyMessage, THelloMessage>(RpcConfiguration config, IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions, THelloMessage helloMessage) where THelloMessage : IMessage<TServerListener, NoReply> where TReplyMessage : IMessage<TClientListener, NoReply>, IMessage<TServerListener, NoReply> {
|
public static RpcClientSocket<TClientMessage, TServerMessage, TReplyMessage> Connect<TClientMessage, TServerMessage, TReplyMessage, THelloMessage>(RpcConfiguration config, IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions, THelloMessage helloMessage) where THelloMessage : TServerMessage where TReplyMessage : TClientMessage, TServerMessage {
|
||||||
return RpcClientSocket<TClientListener, TServerListener, TReplyMessage>.Connect(config, messageDefinitions, helloMessage);
|
return RpcClientSocket<TClientMessage, TServerMessage, TReplyMessage>.Connect(config, messageDefinitions, helloMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class RpcClientSocket<TClientListener, TServerListener, TReplyMessage> : RpcSocket<ClientSocket> where TReplyMessage : IMessage<TClientListener, NoReply>, IMessage<TServerListener, NoReply> {
|
public sealed class RpcClientSocket<TClientMessage, TServerMessage, TReplyMessage> : RpcSocket<ClientSocket> where TReplyMessage : TClientMessage, TServerMessage {
|
||||||
internal static RpcClientSocket<TClientListener, TServerListener, TReplyMessage> Connect<THelloMessage>(RpcConfiguration config, IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions, THelloMessage helloMessage) where THelloMessage : IMessage<TServerListener, NoReply> {
|
internal static RpcClientSocket<TClientMessage, TServerMessage, TReplyMessage> Connect<THelloMessage>(RpcConfiguration config, IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions, THelloMessage helloMessage) where THelloMessage : TServerMessage {
|
||||||
var socket = new ClientSocket();
|
var socket = new ClientSocket();
|
||||||
var options = socket.Options;
|
var options = socket.Options;
|
||||||
|
|
||||||
@ -29,14 +29,14 @@ public sealed class RpcClientSocket<TClientListener, TServerListener, TReplyMess
|
|||||||
socket.Connect(url);
|
socket.Connect(url);
|
||||||
logger.Information("ZeroMQ client ready.");
|
logger.Information("ZeroMQ client ready.");
|
||||||
|
|
||||||
return new RpcClientSocket<TClientListener, TServerListener, TReplyMessage>(socket, config, messageDefinitions);
|
return new RpcClientSocket<TClientMessage, TServerMessage, TReplyMessage>(socket, config, messageDefinitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RpcConnectionToServer<TServerListener> Connection { get; }
|
public RpcConnectionToServer<TServerMessage> Connection { get; }
|
||||||
internal IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> MessageDefinitions { get; }
|
internal IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> MessageDefinitions { get; }
|
||||||
|
|
||||||
private RpcClientSocket(ClientSocket socket, RpcConfiguration config, IMessageDefinitions<TClientListener, TServerListener, TReplyMessage> messageDefinitions) : base(socket, config) {
|
private RpcClientSocket(ClientSocket socket, RpcConfiguration config, IMessageDefinitions<TClientMessage, TServerMessage, TReplyMessage> messageDefinitions) : base(socket, config) {
|
||||||
MessageDefinitions = messageDefinitions;
|
MessageDefinitions = messageDefinitions;
|
||||||
Connection = new RpcConnectionToServer<TServerListener>(config.LoggerName, socket, messageDefinitions.ToServer, ReplyTracker);
|
Connection = new RpcConnectionToServer<TServerMessage>(socket, messageDefinitions.ToServer, ReplyTracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ using Phantom.Utils.Logging;
|
|||||||
|
|
||||||
namespace Phantom.Utils.Rpc.Sockets;
|
namespace Phantom.Utils.Rpc.Sockets;
|
||||||
|
|
||||||
public sealed class RpcServerSocket : RpcSocket<ServerSocket> {
|
sealed class RpcServerSocket : RpcSocket<ServerSocket> {
|
||||||
public static RpcServerSocket Connect(RpcConfiguration config) {
|
public static RpcServerSocket Connect(RpcConfiguration config) {
|
||||||
var socket = new ServerSocket();
|
var socket = new ServerSocket();
|
||||||
var options = socket.Options;
|
var options = socket.Options;
|
||||||
|
@ -8,28 +8,4 @@ public static class AsyncTasks {
|
|||||||
public static TaskCompletionSource<T> CreateCompletionSource<T>() {
|
public static TaskCompletionSource<T> CreateCompletionSource<T>() {
|
||||||
return new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
return new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetResultFrom(this TaskCompletionSource completionSource, Task task) {
|
|
||||||
if (task.IsFaulted) {
|
|
||||||
completionSource.SetException(task.Exception.InnerExceptions);
|
|
||||||
}
|
|
||||||
else if (task.IsCanceled) {
|
|
||||||
completionSource.SetCanceled();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
completionSource.SetResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetResultFrom<T>(this TaskCompletionSource<T> completionSource, Task<T> task) {
|
|
||||||
if (task.IsFaulted) {
|
|
||||||
completionSource.SetException(task.Exception.InnerExceptions);
|
|
||||||
}
|
|
||||||
else if (task.IsCanceled) {
|
|
||||||
completionSource.SetCanceled();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
completionSource.SetResult(task.Result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace Phantom.Web.Services;
|
|||||||
public static class PhantomWebServices {
|
public static class PhantomWebServices {
|
||||||
public static void AddPhantomServices(this IServiceCollection services) {
|
public static void AddPhantomServices(this IServiceCollection services) {
|
||||||
services.AddSingleton<ControllerConnection>();
|
services.AddSingleton<ControllerConnection>();
|
||||||
services.AddSingleton<MessageListener>();
|
services.AddSingleton<ControllerMessageHandlerFactory>();
|
||||||
|
|
||||||
services.AddSingleton<AgentManager>();
|
services.AddSingleton<AgentManager>();
|
||||||
services.AddSingleton<InstanceManager>();
|
services.AddSingleton<InstanceManager>();
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
using Phantom.Common.Messages.Web;
|
using Phantom.Common.Messages.Web;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
using Phantom.Utils.Rpc.Runtime;
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
|
||||||
namespace Phantom.Web.Services.Rpc;
|
namespace Phantom.Web.Services.Rpc;
|
||||||
|
|
||||||
public sealed class ControllerConnection {
|
public sealed class ControllerConnection {
|
||||||
private readonly RpcConnectionToServer<IMessageToControllerListener> connection;
|
private readonly RpcConnectionToServer<IMessageToController> connection;
|
||||||
|
|
||||||
public ControllerConnection(RpcConnectionToServer<IMessageToControllerListener> connection) {
|
public ControllerConnection(RpcConnectionToServer<IMessageToController> connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,11 +15,11 @@ public sealed class ControllerConnection {
|
|||||||
return connection.Send(message);
|
return connection.Send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TReply> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken = default) where TMessage : IMessageToController<TReply> {
|
public Task<TReply> Send<TMessage, TReply>(TMessage message, TimeSpan waitForReplyTime, CancellationToken waitForReplyCancellationToken = default) where TMessage : IMessageToController, ICanReply<TReply> {
|
||||||
return connection.Send<TMessage, TReply>(message, waitForReplyTime, waitForReplyCancellationToken);
|
return connection.Send<TMessage, TReply>(message, waitForReplyTime, waitForReplyCancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TReply> Send<TMessage, TReply>(TMessage message, CancellationToken waitForReplyCancellationToken) where TMessage : IMessageToController<TReply> {
|
public Task<TReply> Send<TMessage, TReply>(TMessage message, CancellationToken waitForReplyCancellationToken) where TMessage : IMessageToController, ICanReply<TReply> {
|
||||||
return connection.Send<TMessage, TReply>(message, Timeout.InfiniteTimeSpan, waitForReplyCancellationToken);
|
return connection.Send<TMessage, TReply>(message, Timeout.InfiniteTimeSpan, waitForReplyCancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
using Phantom.Common.Messages.Web;
|
||||||
|
using Phantom.Common.Messages.Web.BiDirectional;
|
||||||
|
using Phantom.Common.Messages.Web.ToWeb;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
using Phantom.Web.Services.Agents;
|
||||||
|
using Phantom.Web.Services.Instances;
|
||||||
|
|
||||||
|
namespace Phantom.Web.Services.Rpc;
|
||||||
|
|
||||||
|
sealed class ControllerMessageHandlerActor : ReceiveActor<IMessageToWeb> {
|
||||||
|
public readonly record struct Init(RpcConnectionToServer<IMessageToController> Connection, AgentManager AgentManager, InstanceManager InstanceManager, InstanceLogManager InstanceLogManager, TaskCompletionSource<bool> RegisterSuccessWaiter);
|
||||||
|
|
||||||
|
public static Props<IMessageToWeb> Factory(Init init) {
|
||||||
|
return Props<IMessageToWeb>.Create(() => new ControllerMessageHandlerActor(init), new ActorConfiguration { SupervisorStrategy = SupervisorStrategies.Resume });
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly RpcConnectionToServer<IMessageToController> connection;
|
||||||
|
private readonly AgentManager agentManager;
|
||||||
|
private readonly InstanceManager instanceManager;
|
||||||
|
private readonly InstanceLogManager instanceLogManager;
|
||||||
|
private readonly TaskCompletionSource<bool> registerSuccessWaiter;
|
||||||
|
|
||||||
|
private ControllerMessageHandlerActor(Init init) {
|
||||||
|
this.connection = init.Connection;
|
||||||
|
this.agentManager = init.AgentManager;
|
||||||
|
this.instanceManager = init.InstanceManager;
|
||||||
|
this.instanceLogManager = init.InstanceLogManager;
|
||||||
|
this.registerSuccessWaiter = init.RegisterSuccessWaiter;
|
||||||
|
|
||||||
|
Receive<RegisterWebResultMessage>(HandleRegisterWebResult);
|
||||||
|
Receive<RefreshAgentsMessage>(HandleRefreshAgents);
|
||||||
|
Receive<RefreshInstancesMessage>(HandleRefreshInstances);
|
||||||
|
Receive<InstanceOutputMessage>(HandleInstanceOutput);
|
||||||
|
Receive<ReplyMessage>(HandleReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleRegisterWebResult(RegisterWebResultMessage message) {
|
||||||
|
registerSuccessWaiter.TrySetResult(message.Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleRefreshAgents(RefreshAgentsMessage message) {
|
||||||
|
agentManager.RefreshAgents(message.Agents);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleRefreshInstances(RefreshInstancesMessage message) {
|
||||||
|
instanceManager.RefreshInstances(message.Instances);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleInstanceOutput(InstanceOutputMessage message) {
|
||||||
|
instanceLogManager.AddLines(message.InstanceGuid, message.Lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleReply(ReplyMessage message) {
|
||||||
|
connection.Receive(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
using Akka.Actor;
|
||||||
|
using Phantom.Common.Messages.Web;
|
||||||
|
using Phantom.Utils.Actor;
|
||||||
|
using Phantom.Utils.Rpc.Runtime;
|
||||||
|
using Phantom.Utils.Tasks;
|
||||||
|
using Phantom.Web.Services.Agents;
|
||||||
|
using Phantom.Web.Services.Instances;
|
||||||
|
|
||||||
|
namespace Phantom.Web.Services.Rpc;
|
||||||
|
|
||||||
|
public sealed class ControllerMessageHandlerFactory {
|
||||||
|
private readonly RpcConnectionToServer<IMessageToController> connection;
|
||||||
|
private readonly AgentManager agentManager;
|
||||||
|
private readonly InstanceManager instanceManager;
|
||||||
|
private readonly InstanceLogManager instanceLogManager;
|
||||||
|
|
||||||
|
private readonly TaskCompletionSource<bool> registerSuccessWaiter = AsyncTasks.CreateCompletionSource<bool>();
|
||||||
|
|
||||||
|
public Task<bool> RegisterSuccessWaiter => registerSuccessWaiter.Task;
|
||||||
|
|
||||||
|
private int messageHandlerId = 0;
|
||||||
|
|
||||||
|
public ControllerMessageHandlerFactory(RpcConnectionToServer<IMessageToController> connection, AgentManager agentManager, InstanceManager instanceManager, InstanceLogManager instanceLogManager) {
|
||||||
|
this.connection = connection;
|
||||||
|
this.agentManager = agentManager;
|
||||||
|
this.instanceManager = instanceManager;
|
||||||
|
this.instanceLogManager = instanceLogManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActorRef<IMessageToWeb> Create(IActorRefFactory actorSystem) {
|
||||||
|
int id = Interlocked.Increment(ref messageHandlerId);
|
||||||
|
return actorSystem.ActorOf(ControllerMessageHandlerActor.Factory(new ControllerMessageHandlerActor.Init(connection, agentManager, instanceManager, instanceLogManager, registerSuccessWaiter)), "ControllerMessageHandler-" + id);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user