Re-implemented server state machine. Cleaned up code
TODO: Handle trigger errors
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
using Docker.DotNet.Models;
|
||||
using Mono.Unix.Native;
|
||||
using MoonCore.Helpers;
|
||||
using MoonlightServers.Daemon.Models;
|
||||
using MoonlightServers.Daemon.Models.Cache;
|
||||
|
||||
namespace MoonlightServers.Daemon.Extensions.ServerExtensions;
|
||||
namespace MoonlightServers.Daemon.Extensions;
|
||||
|
||||
public static class ServerConfigExtensions
|
||||
public static class ServerConfigurationExtensions
|
||||
{
|
||||
public static CreateContainerParameters GetRuntimeContainerParameters(this Server server)
|
||||
public static CreateContainerParameters ToRuntimeCreateParameters(this ServerConfiguration configuration, string hostPath, string containerName)
|
||||
{
|
||||
var parameters = server.GetSharedContainerParameters();
|
||||
var parameters = configuration.ToSharedCreateParameters();
|
||||
|
||||
#region Security
|
||||
|
||||
@@ -29,20 +29,20 @@ public static class ServerConfigExtensions
|
||||
|
||||
#region Name
|
||||
|
||||
parameters.Name = server.RuntimeContainerName;
|
||||
parameters.Hostname = server.RuntimeContainerName;
|
||||
parameters.Name = containerName;
|
||||
parameters.Hostname = containerName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Docker Image
|
||||
|
||||
parameters.Image = server.Configuration.DockerImage;
|
||||
parameters.Image = configuration.DockerImage;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Environment
|
||||
|
||||
parameters.Env = server.ConstructEnv()
|
||||
parameters.Env = configuration.ToEnvironmentVariables()
|
||||
.Select(x => $"{x.Key}={x.Value}")
|
||||
.ToList();
|
||||
|
||||
@@ -56,7 +56,7 @@ public static class ServerConfigExtensions
|
||||
|
||||
#region User
|
||||
|
||||
var userId = Syscall.getuid();
|
||||
var userId = Syscall.getuid(); // TODO: Extract to external service?
|
||||
|
||||
if (userId == 0)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ public static class ServerConfigExtensions
|
||||
|
||||
parameters.HostConfig.Mounts.Add(new()
|
||||
{
|
||||
Source = server.RuntimeVolumePath,
|
||||
Source = hostPath,
|
||||
Target = "/home/container",
|
||||
ReadOnly = false,
|
||||
Type = "bind"
|
||||
@@ -93,7 +93,7 @@ public static class ServerConfigExtensions
|
||||
parameters.ExposedPorts = new Dictionary<string, EmptyStruct>();
|
||||
parameters.HostConfig.PortBindings = new Dictionary<string, IList<PortBinding>>();
|
||||
|
||||
foreach (var allocation in server.Configuration.Allocations)
|
||||
foreach (var allocation in configuration.Allocations)
|
||||
{
|
||||
parameters.ExposedPorts.Add($"{allocation.Port}/tcp", new());
|
||||
parameters.ExposedPorts.Add($"{allocation.Port}/udp", new());
|
||||
@@ -122,8 +122,8 @@ public static class ServerConfigExtensions
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public static CreateContainerParameters GetSharedContainerParameters(this Server server)
|
||||
|
||||
private static CreateContainerParameters ToSharedCreateParameters(this ServerConfiguration configuration)
|
||||
{
|
||||
var parameters = new CreateContainerParameters()
|
||||
{
|
||||
@@ -142,7 +142,7 @@ public static class ServerConfigExtensions
|
||||
|
||||
#region CPU
|
||||
|
||||
parameters.HostConfig.CPUQuota = server.Configuration.Cpu * 1000;
|
||||
parameters.HostConfig.CPUQuota = configuration.Cpu * 1000;
|
||||
parameters.HostConfig.CPUPeriod = 100000;
|
||||
parameters.HostConfig.CPUShares = 1024;
|
||||
|
||||
@@ -150,7 +150,7 @@ public static class ServerConfigExtensions
|
||||
|
||||
#region Memory & Swap
|
||||
|
||||
var memoryLimit = server.Configuration.Memory;
|
||||
var memoryLimit = configuration.Memory;
|
||||
|
||||
// The overhead multiplier gives the container a little bit more memory to prevent crashes
|
||||
var memoryOverhead = memoryLimit + (memoryLimit * 0.05f); // TODO: Config
|
||||
@@ -183,6 +183,8 @@ public static class ServerConfigExtensions
|
||||
|
||||
#region DNS
|
||||
|
||||
// TODO: Read hosts dns settings?
|
||||
|
||||
parameters.HostConfig.DNS = /*config.Docker.DnsServers.Any() ? config.Docker.DnsServers :*/ new List<string>()
|
||||
{
|
||||
"1.1.1.1",
|
||||
@@ -215,27 +217,25 @@ public static class ServerConfigExtensions
|
||||
parameters.Labels = new Dictionary<string, string>();
|
||||
|
||||
parameters.Labels.Add("Software", "Moonlight-Panel");
|
||||
parameters.Labels.Add("ServerId", server.Configuration.Id.ToString());
|
||||
parameters.Labels.Add("ServerId", configuration.Id.ToString());
|
||||
|
||||
#endregion
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> ConstructEnv(this Server server)
|
||||
public static Dictionary<string, string> ToEnvironmentVariables(this ServerConfiguration configuration)
|
||||
{
|
||||
var config = server.Configuration;
|
||||
|
||||
var result = new Dictionary<string, string>
|
||||
{
|
||||
//TODO: Add timezone, add server ip
|
||||
{ "STARTUP", config.StartupCommand },
|
||||
{ "SERVER_MEMORY", config.Memory.ToString() }
|
||||
{ "STARTUP", configuration.StartupCommand },
|
||||
{ "SERVER_MEMORY", configuration.Memory.ToString() }
|
||||
};
|
||||
|
||||
if (config.Allocations.Length > 0)
|
||||
if (configuration.Allocations.Length > 0)
|
||||
{
|
||||
var mainAllocation = config.Allocations.First();
|
||||
var mainAllocation = configuration.Allocations.First();
|
||||
|
||||
result.Add("SERVER_IP", mainAllocation.IpAddress);
|
||||
result.Add("SERVER_PORT", mainAllocation.Port.ToString());
|
||||
@@ -243,14 +243,14 @@ public static class ServerConfigExtensions
|
||||
|
||||
// Handle allocation variables
|
||||
var i = 1;
|
||||
foreach (var allocation in config.Allocations)
|
||||
foreach (var allocation in configuration.Allocations)
|
||||
{
|
||||
result.Add($"ML_PORT_{i}", allocation.Port.ToString());
|
||||
i++;
|
||||
}
|
||||
|
||||
// Copy variables as env vars
|
||||
foreach (var variable in config.Variables)
|
||||
foreach (var variable in configuration.Variables)
|
||||
result.Add(variable.Key, variable.Value);
|
||||
|
||||
return result;
|
||||
@@ -1,65 +0,0 @@
|
||||
using System.Text;
|
||||
using Docker.DotNet;
|
||||
using Docker.DotNet.Models;
|
||||
using MoonlightServers.Daemon.Models;
|
||||
|
||||
namespace MoonlightServers.Daemon.Extensions.ServerExtensions;
|
||||
|
||||
public static class ServerConsoleExtensions
|
||||
{
|
||||
public static async Task Attach(this Server server)
|
||||
{
|
||||
var dockerClient = server.ServiceProvider.GetRequiredService<DockerClient>();
|
||||
|
||||
var stream = await dockerClient.Containers.AttachContainerAsync(server.ContainerId, true,
|
||||
new ContainerAttachParameters()
|
||||
{
|
||||
Stderr = true,
|
||||
Stdin = true,
|
||||
Stdout = true,
|
||||
Stream = true
|
||||
},
|
||||
server.Cancellation.Token
|
||||
);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (!server.Cancellation.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var buffer = new byte[1024];
|
||||
|
||||
var readResult = await stream.ReadOutputAsync(
|
||||
buffer,
|
||||
0,
|
||||
buffer.Length,
|
||||
server.Cancellation.Token
|
||||
);
|
||||
|
||||
if(readResult.EOF)
|
||||
break;
|
||||
|
||||
var resizedBuffer = new byte[readResult.Count];
|
||||
Array.Copy(buffer, resizedBuffer, readResult.Count);
|
||||
buffer = new byte[buffer.Length];
|
||||
|
||||
var decodedText = Encoding.UTF8.GetString(resizedBuffer);
|
||||
await server.Console.WriteToOutput(decodedText);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
server.Logger.LogWarning("An unhandled error occured while reading from container stream: {e}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using Docker.DotNet;
|
||||
using MoonlightServers.Daemon.Models;
|
||||
using MoonlightServers.DaemonShared.Enums;
|
||||
|
||||
namespace MoonlightServers.Daemon.Extensions.ServerExtensions;
|
||||
|
||||
public static class ServerCreateExtensions
|
||||
{
|
||||
public static async Task Create(this Server server)
|
||||
{
|
||||
var dockerClient = server.ServiceProvider.GetRequiredService<DockerClient>();
|
||||
|
||||
// Ensure image is pulled
|
||||
await server.EnsureDockerImage();
|
||||
|
||||
// Ensure runtime storage is created
|
||||
await server.EnsureRuntimeStorage();
|
||||
|
||||
// Creating container
|
||||
await server.NotifyTask(ServerTask.CreatingContainer);
|
||||
|
||||
var parameters = server.GetRuntimeContainerParameters();
|
||||
var container = await dockerClient.Containers.CreateContainerAsync(parameters);
|
||||
|
||||
server.ContainerId = container.ID;
|
||||
|
||||
// Attach console
|
||||
await server.Attach();
|
||||
}
|
||||
|
||||
public static async Task ReCreate(this Server server)
|
||||
{
|
||||
await server.Destroy();
|
||||
await server.Create();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using Docker.DotNet;
|
||||
using MoonlightServers.Daemon.Models;
|
||||
using MoonlightServers.DaemonShared.Enums;
|
||||
|
||||
namespace MoonlightServers.Daemon.Extensions.ServerExtensions;
|
||||
|
||||
public static class ServerDestroyExtensions
|
||||
{
|
||||
public static async Task Destroy(this Server server)
|
||||
{
|
||||
// Note: This only destroys the container, it doesn't delete any data
|
||||
|
||||
var dockerClient = server.ServiceProvider.GetRequiredService<DockerClient>();
|
||||
|
||||
try
|
||||
{
|
||||
var container = await dockerClient.Containers.InspectContainerAsync(
|
||||
server.RuntimeContainerName
|
||||
);
|
||||
|
||||
if (container.State.Running)
|
||||
{
|
||||
// Stop container when running
|
||||
|
||||
await server.NotifyTask(ServerTask.StoppingContainer);
|
||||
|
||||
await dockerClient.Containers.StopContainerAsync(container.ID, new()
|
||||
{
|
||||
WaitBeforeKillSeconds = 30 // TODO: Config
|
||||
});
|
||||
}
|
||||
|
||||
await server.NotifyTask(ServerTask.RemovingContainer);
|
||||
await dockerClient.Containers.RemoveContainerAsync(container.ID, new());
|
||||
}
|
||||
catch (DockerContainerNotFoundException){}
|
||||
|
||||
// Canceling server sub-tasks and recreating cancellation token
|
||||
if (!server.Cancellation.IsCancellationRequested)
|
||||
await server.Cancellation.CancelAsync();
|
||||
|
||||
server.Cancellation = new();
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using Docker.DotNet;
|
||||
using Docker.DotNet.Models;
|
||||
using MoonlightServers.Daemon.Models;
|
||||
using MoonlightServers.DaemonShared.Enums;
|
||||
|
||||
namespace MoonlightServers.Daemon.Extensions.ServerExtensions;
|
||||
|
||||
public static class ServerImageExtensions
|
||||
{
|
||||
public static async Task EnsureDockerImage(this Server server)
|
||||
{
|
||||
await server.NotifyTask(ServerTask.PullingDockerImage);
|
||||
|
||||
var dockerClient = server.ServiceProvider.GetRequiredService<DockerClient>();
|
||||
|
||||
await dockerClient.Images.CreateImageAsync(new()
|
||||
{
|
||||
FromImage = server.Configuration.DockerImage
|
||||
},
|
||||
new AuthConfig(),
|
||||
new Progress<JSONMessage>(async message =>
|
||||
{
|
||||
if (message.Progress == null)
|
||||
return;
|
||||
|
||||
var percentage = message.Progress.Total > 0
|
||||
? Math.Round((float)message.Progress.Current / message.Progress.Total * 100f, 2)
|
||||
: 0d;
|
||||
|
||||
server.Logger.LogInformation(
|
||||
"Docker Image: [{id}] {status} - {percent}",
|
||||
message.ID,
|
||||
message.Status,
|
||||
percentage
|
||||
);
|
||||
|
||||
//await UpdateProgress(server, serviceProvider, percentage);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using MoonlightServers.Daemon.Models;
|
||||
using MoonlightServers.DaemonShared.Enums;
|
||||
|
||||
namespace MoonlightServers.Daemon.Extensions.ServerExtensions;
|
||||
|
||||
public static class ServerMetaExtensions
|
||||
{
|
||||
public static async Task NotifyTask(this Server server, ServerTask task)
|
||||
{
|
||||
server.Logger.LogInformation("Task: {task}", task);
|
||||
await server.InvokeTaskAdded(task.ToString());
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Docker.DotNet;
|
||||
using MoonlightServers.Daemon.Models;
|
||||
using MoonlightServers.DaemonShared.Enums;
|
||||
|
||||
namespace MoonlightServers.Daemon.Extensions.ServerExtensions;
|
||||
|
||||
public static class ServerStartExtensions
|
||||
{
|
||||
public static async Task StateMachineHandler_Start(this Server server)
|
||||
{
|
||||
await server.ReCreate();
|
||||
|
||||
await server.NotifyTask(ServerTask.StartingContainer);
|
||||
var dockerClient = server.ServiceProvider.GetRequiredService<DockerClient>();
|
||||
|
||||
await dockerClient.Containers.StartContainerAsync(server.ContainerId, new());
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using MoonlightServers.Daemon.Models;
|
||||
using MoonlightServers.DaemonShared.Enums;
|
||||
|
||||
namespace MoonlightServers.Daemon.Extensions.ServerExtensions;
|
||||
|
||||
public static class ServerStorageExtensions
|
||||
{
|
||||
public static async Task EnsureRuntimeStorage(this Server server)
|
||||
{
|
||||
// TODO: Add virtual disk
|
||||
await server.NotifyTask(ServerTask.CreatingStorage);
|
||||
|
||||
// Create volume if missing
|
||||
if (!Directory.Exists(server.RuntimeVolumePath))
|
||||
Directory.CreateDirectory(server.RuntimeVolumePath);
|
||||
|
||||
// TODO: Chown
|
||||
//Syscall.chown()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user