Files
Servers/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/ContainerConfigMapper.cs

253 lines
7.4 KiB
C#

using Docker.DotNet.Models;
using MoonlightServers.Daemon.Models;
namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker;
public class ContainerConfigMapper
{
public CreateContainerParameters GetRuntimeConfig(
string uuid,
string name,
RuntimeConfiguration configuration,
string runtimeStoragePath
)
{
var parameters = new CreateContainerParameters()
{
HostConfig = new()
};
ApplySharedOptions(parameters, configuration);
// Limits
if (configuration.Limits.CpuPercent.HasValue)
{
parameters.HostConfig.CPUQuota = configuration.Limits.CpuPercent.Value * 1000;
parameters.HostConfig.CPUPeriod = 100000;
parameters.HostConfig.CPUShares = 1024;
}
if (configuration.Limits.MemoryMb.HasValue)
{
var memoryLimit = configuration.Limits.MemoryMb.Value;
// The overhead multiplier gives the container a little bit more memory to prevent crashes
var memoryOverhead = memoryLimit + memoryLimit * 0.05f;
parameters.HostConfig.Memory = (long)memoryOverhead * 1024L * 1024L;
parameters.HostConfig.MemoryReservation = (long)memoryLimit * 1024L * 1024L;
if (configuration.Limits.SwapMb.HasValue)
{
var rawSwap = configuration.Limits.SwapMb.Value * 1024L * 1024L;
parameters.HostConfig.MemorySwap = rawSwap + (long)memoryOverhead;
}
}
parameters.HostConfig.BlkioWeight = 100;
parameters.HostConfig.OomKillDisable = true;
// Storage
parameters.HostConfig.Tmpfs = new Dictionary<string, string>()
{
{ "/tmp", "rw,exec,nosuid,size=100M" } // TODO: Config
};
parameters.WorkingDir = "/home/container";
parameters.HostConfig.Mounts = new List<Mount>();
parameters.HostConfig.Mounts.Add(new Mount()
{
Source = runtimeStoragePath,
Target = "/home/container",
Type = "bind",
ReadOnly = false
});
// Labels
parameters.Labels = new Dictionary<string, string>()
{
{ "dev.moonlightpanel", "true" },
{ "dev.moonlightpanel.id", uuid }
};
foreach (var label in configuration.Environment.Labels)
parameters.Labels.Add(label.Key, label.Value);
// Security
parameters.HostConfig.CapDrop = new List<string>()
{
"setpcap", "mknod", "audit_write", "net_raw", "dac_override",
"fowner", "fsetid", "net_bind_service", "sys_chroot", "setfcap"
};
parameters.HostConfig.ReadonlyRootfs = true;
parameters.HostConfig.SecurityOpt = new List<string>()
{
"no-new-privileges"
};
// Name
parameters.Name = name;
// Docker Image
parameters.Image = configuration.Template.DockerImage;
// Networking
if (configuration.Network.Ports.Length > 0 && !string.IsNullOrWhiteSpace(configuration.Network.FriendlyName))
parameters.Hostname = configuration.Network.FriendlyName;
parameters.ExposedPorts = new Dictionary<string, EmptyStruct>();
parameters.HostConfig.PortBindings = new Dictionary<string, IList<PortBinding>>();
foreach (var port in configuration.Network.Ports)
{
parameters.ExposedPorts.Add($"{port.Port}/tcp", new());
parameters.ExposedPorts.Add($"{port.Port}/udp", new());
parameters.HostConfig.PortBindings.Add($"{port.Port}/tcp", new List<PortBinding>
{
new()
{
HostPort = port.Port.ToString(),
HostIP = port.IpAddress
}
});
parameters.HostConfig.PortBindings.Add($"{port.Port}/udp", new List<PortBinding>
{
new()
{
HostPort = port.Port.ToString(),
HostIP = port.IpAddress
}
});
}
// TODO: Force outgoing ip stuff
// User
parameters.User = "1000:1000";
return parameters;
}
public CreateContainerParameters GetInstallConfig(
string uuid,
string name,
RuntimeConfiguration runtimeConfiguration,
InstallConfiguration installConfiguration,
string runtimeStoragePath,
string installStoragePath
)
{
var parameters = new CreateContainerParameters()
{
HostConfig = new()
};
ApplySharedOptions(parameters, runtimeConfiguration);
// Labels
parameters.Labels = new Dictionary<string, string>()
{
{ "dev.moonlightpanel", "true" },
{ "dev.moonlightpanel.id", uuid }
};
foreach (var label in runtimeConfiguration.Environment.Labels)
parameters.Labels.Add(label.Key, label.Value);
// Name
parameters.Name = name;
// Docker Image
parameters.Image = installConfiguration.DockerImage;
// User
parameters.User = "1000:1000";
// Storage
parameters.WorkingDir = "/mnt/server";
parameters.HostConfig.Mounts = new List<Mount>();
parameters.HostConfig.Mounts.Add(new Mount()
{
Source = runtimeStoragePath,
Target = "/mnt/server",
ReadOnly = false,
Type = "bind"
});
parameters.HostConfig.Mounts.Add(new Mount()
{
Source = installStoragePath,
Target = "/mnt/install",
ReadOnly = false,
Type = "bind"
});
// Command
parameters.Cmd = [installConfiguration.Shell, "/mnt/install/install.sh"];
return parameters;
}
private void ApplySharedOptions(
CreateContainerParameters parameters,
RuntimeConfiguration configuration
)
{
// Input, output & error streams and TTY
parameters.Tty = true;
parameters.AttachStderr = true;
parameters.AttachStdin = true;
parameters.AttachStdout = true;
parameters.OpenStdin = true;
// Logging
parameters.HostConfig.LogConfig = new()
{
Type = "json-file", // We need to use this provider, as the GetLogs endpoint needs it
Config = new Dictionary<string, string>()
};
// Environment variables
parameters.Env = new List<string>()
{
$"STARTUP={configuration.Template.StartupCommand}",
//TODO: Add timezone, add server ip
};
if (configuration.Limits.MemoryMb.HasValue)
parameters.Env.Add($"SERVER_MEMORY={configuration.Limits.MemoryMb.Value}");
if (configuration.Network.MainPort != null)
{
parameters.Env.Add($"SERVER_IP={configuration.Network.MainPort.IpAddress}");
parameters.Env.Add($"SERVER_PORT={configuration.Network.MainPort.Port}");
}
// Handle port variables
var i = 1;
foreach (var port in configuration.Network.Ports)
{
parameters.Env.Add($"ML_PORT_{i}={port.Port}");
i++;
}
// Copy variables as env vars
foreach (var variable in configuration.Environment.Variables)
parameters.Env.Add($"{variable.Key}={variable.Value}");
}
}