[FEATURE] Initial Anti-Storage
This commit is contained in:
parent
b84fba6153
commit
9f0689f6a4
6 changed files with 95 additions and 57 deletions
|
@ -15,8 +15,14 @@ const dnsRegex = new RegExp(
|
||||||
|
|
||||||
function backupPayloadFilter(req, res) {
|
function backupPayloadFilter(req, res) {
|
||||||
const serverSpec = req.body;
|
const serverSpec = req.body;
|
||||||
const { backupHost, backupBucket, backupId, backupKey, backupInterval } =
|
const {
|
||||||
serverSpec;
|
storage,
|
||||||
|
backupHost,
|
||||||
|
backupBucket,
|
||||||
|
backupId,
|
||||||
|
backupKey,
|
||||||
|
backupInterval,
|
||||||
|
} = serverSpec;
|
||||||
// TODO: Impliment non creation time backups
|
// TODO: Impliment non creation time backups
|
||||||
if (
|
if (
|
||||||
!!backupHost ||
|
!!backupHost ||
|
||||||
|
@ -25,6 +31,8 @@ function backupPayloadFilter(req, res) {
|
||||||
!!backupKey ||
|
!!backupKey ||
|
||||||
!!backupInterval
|
!!backupInterval
|
||||||
) {
|
) {
|
||||||
|
if (storage === 0)
|
||||||
|
return res.status(400).send("Backups cannot be used if storage is zero!");
|
||||||
// If any keys are required, all are required
|
// If any keys are required, all are required
|
||||||
if (
|
if (
|
||||||
!(
|
!(
|
||||||
|
|
|
@ -22,7 +22,7 @@ export async function createServerEntry(serverSpec) {
|
||||||
serverType: server_type,
|
serverType: server_type,
|
||||||
cpu, // TODO: Ignored for now by the K8S manifests
|
cpu, // TODO: Ignored for now by the K8S manifests
|
||||||
memory,
|
memory,
|
||||||
storage,
|
storage: storage_val,
|
||||||
extraPorts: extra_ports,
|
extraPorts: extra_ports,
|
||||||
backupHost: backup_host,
|
backupHost: backup_host,
|
||||||
backupBucket: backup_bucket_path,
|
backupBucket: backup_bucket_path,
|
||||||
|
@ -30,6 +30,7 @@ export async function createServerEntry(serverSpec) {
|
||||||
backupKey: backup_key,
|
backupKey: backup_key,
|
||||||
backupInterval: backup_interval,
|
backupInterval: backup_interval,
|
||||||
} = serverSpec;
|
} = serverSpec;
|
||||||
|
|
||||||
var q = insertQuery(table, {
|
var q = insertQuery(table, {
|
||||||
name,
|
name,
|
||||||
host,
|
host,
|
||||||
|
@ -37,7 +38,7 @@ export async function createServerEntry(serverSpec) {
|
||||||
server_type,
|
server_type,
|
||||||
cpu, // TODO: Ignored for now by the K8S manifests
|
cpu, // TODO: Ignored for now by the K8S manifests
|
||||||
memory,
|
memory,
|
||||||
storage,
|
storage: !storage_val || storage_val === "0" ? null : storage_val, // 0, undefined, null, or "0" becomes null
|
||||||
extra_ports,
|
extra_ports,
|
||||||
backup_enabled: !!backup_interval ? true : null, // We already verified the payload, so any backup key will work
|
backup_enabled: !!backup_interval ? true : null, // We already verified the payload, so any backup key will work
|
||||||
backup_host,
|
backup_host,
|
||||||
|
|
|
@ -30,11 +30,13 @@ spec:
|
||||||
# runAsUser: 1000
|
# runAsUser: 1000
|
||||||
terminationGracePeriodSeconds: 30
|
terminationGracePeriodSeconds: 30
|
||||||
volumes:
|
volumes:
|
||||||
- name: datadir
|
- emptyDir: {}
|
||||||
persistentVolumeClaim:
|
name: datadir
|
||||||
claimName: changeme-pvc-name
|
|
||||||
- emptyDir: {}
|
- emptyDir: {}
|
||||||
name: backupdir
|
name: backupdir
|
||||||
|
# - name: datadir
|
||||||
|
# persistentVolumeClaim:
|
||||||
|
# claimName: changeme-pvc-name
|
||||||
# - name: rclone-config
|
# - name: rclone-config
|
||||||
# secret:
|
# secret:
|
||||||
# defaultMode: 420
|
# defaultMode: 420
|
||||||
|
|
|
@ -4,7 +4,7 @@ import yaml from "js-yaml";
|
||||||
const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8"));
|
const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8"));
|
||||||
|
|
||||||
export function getFtpContainer(serverSpec) {
|
export function getFtpContainer(serverSpec) {
|
||||||
const { mclName } = serverSpec;
|
const { mclName, storage } = serverSpec;
|
||||||
const ftpContainer = loadYaml("lib/k8s/configs/containers/ftp-server.yml");
|
const ftpContainer = loadYaml("lib/k8s/configs/containers/ftp-server.yml");
|
||||||
ftpContainer.name = `mcl-${mclName}-ftp`;
|
ftpContainer.name = `mcl-${mclName}-ftp`;
|
||||||
const ftpPortList = [
|
const ftpPortList = [
|
||||||
|
@ -18,11 +18,12 @@ export function getFtpContainer(serverSpec) {
|
||||||
name,
|
name,
|
||||||
protocol: "TCP",
|
protocol: "TCP",
|
||||||
}));
|
}));
|
||||||
|
if (!storage) delete ftpContainer.volumeMounts;
|
||||||
return ftpContainer;
|
return ftpContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCoreServerContainer(serverSpec) {
|
export function getCoreServerContainer(serverSpec) {
|
||||||
const { mclName, version, serverType, memory } = serverSpec;
|
const { mclName, version, serverType, memory, storage } = serverSpec;
|
||||||
const container = loadYaml("lib/k8s/configs/containers/minecraft-server.yml");
|
const container = loadYaml("lib/k8s/configs/containers/minecraft-server.yml");
|
||||||
// Container Updates
|
// Container Updates
|
||||||
container.name = `mcl-${mclName}-server`;
|
container.name = `mcl-${mclName}-server`;
|
||||||
|
@ -38,12 +39,21 @@ export function getCoreServerContainer(serverSpec) {
|
||||||
// RCON
|
// RCON
|
||||||
const rs = `mcl-${mclName}-rcon-secret`;
|
const rs = `mcl-${mclName}-rcon-secret`;
|
||||||
findEnv("RCON_PASSWORD").valueFrom.secretKeyRef.name = rs;
|
findEnv("RCON_PASSWORD").valueFrom.secretKeyRef.name = rs;
|
||||||
|
if (!storage) delete container.volumeMounts;
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getServerContainer(serverSpec) {
|
export function getServerContainer(serverSpec) {
|
||||||
const { difficulty, gamemode, motd, maxPlayers, seed, ops, whitelist } =
|
const {
|
||||||
serverSpec;
|
difficulty,
|
||||||
|
gamemode,
|
||||||
|
motd,
|
||||||
|
maxPlayers,
|
||||||
|
seed,
|
||||||
|
ops,
|
||||||
|
whitelist,
|
||||||
|
storage,
|
||||||
|
} = serverSpec;
|
||||||
const container = getCoreServerContainer(serverSpec);
|
const container = getCoreServerContainer(serverSpec);
|
||||||
|
|
||||||
const findEnv = (k) => container.env.find(({ name: n }) => n === k);
|
const findEnv = (k) => container.env.find(({ name: n }) => n === k);
|
||||||
|
@ -57,12 +67,13 @@ export function getServerContainer(serverSpec) {
|
||||||
updateEnv("SEED", seed);
|
updateEnv("SEED", seed);
|
||||||
updateEnv("OPS", ops);
|
updateEnv("OPS", ops);
|
||||||
updateEnv("WHITELIST", whitelist); */
|
updateEnv("WHITELIST", whitelist); */
|
||||||
|
if (!storage) delete container.volumeMounts;
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBackupContainer(serverSpec) {
|
export function getBackupContainer(serverSpec) {
|
||||||
const { mclName, backupEnabled, backupPath } = serverSpec;
|
const { mclName, backupEnabled, backupPath, storage } = serverSpec;
|
||||||
const container = loadYaml("lib/k8s/configs/containers/minecraft-backup.yml");
|
const container = loadYaml("lib/k8s/configs/containers/minecraft-backup.yml");
|
||||||
if (!backupEnabled) return;
|
if (!backupEnabled) return;
|
||||||
const findEnv = (k) => container.env.find(({ name: n }) => n === k);
|
const findEnv = (k) => container.env.find(({ name: n }) => n === k);
|
||||||
|
@ -73,6 +84,7 @@ export function getBackupContainer(serverSpec) {
|
||||||
// RCON
|
// RCON
|
||||||
const rs = `mcl-${mclName}-rcon-secret`;
|
const rs = `mcl-${mclName}-rcon-secret`;
|
||||||
findEnv("RCON_PASSWORD").valueFrom.secretKeyRef.name = rs;
|
findEnv("RCON_PASSWORD").valueFrom.secretKeyRef.name = rs;
|
||||||
|
if (!storage) delete container.volumeMounts;
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,18 +86,19 @@ function createRconSecret(serverSpec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createServerVolume(serverSpec) {
|
function createServerVolume(serverSpec) {
|
||||||
const { mclName, id } = serverSpec;
|
const { mclName, id, storage } = serverSpec;
|
||||||
|
if (!storage) return;
|
||||||
const volumeYaml = loadYaml("lib/k8s/configs/server-pvc.yml");
|
const volumeYaml = loadYaml("lib/k8s/configs/server-pvc.yml");
|
||||||
volumeYaml.metadata.labels.service = `mcl-${mclName}-server`;
|
volumeYaml.metadata.labels.service = `mcl-${mclName}-server`;
|
||||||
volumeYaml.metadata.name = `mcl-${mclName}-volume`;
|
volumeYaml.metadata.name = `mcl-${mclName}-volume`;
|
||||||
volumeYaml.metadata.namespace = namespace;
|
volumeYaml.metadata.namespace = namespace;
|
||||||
volumeYaml.metadata.annotations["minecluster.dunemask.net/id"] = id;
|
volumeYaml.metadata.annotations["minecluster.dunemask.net/id"] = id;
|
||||||
volumeYaml.spec.resources.requests.storage = "5Gi"; // TODO: Changeme
|
volumeYaml.spec.resources.requests.storage = `${storage}Gi`;
|
||||||
return volumeYaml;
|
return volumeYaml;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createServerDeploy(serverSpec) {
|
function createServerDeploy(serverSpec) {
|
||||||
const { mclName, id, backupEnabled } = serverSpec;
|
const { mclName, id, backupEnabled, storage } = serverSpec;
|
||||||
const deployYaml = loadYaml("lib/k8s/configs/server-deployment.yml");
|
const deployYaml = loadYaml("lib/k8s/configs/server-deployment.yml");
|
||||||
const { metadata } = deployYaml;
|
const { metadata } = deployYaml;
|
||||||
const serverContainer = getServerContainer(serverSpec);
|
const serverContainer = getServerContainer(serverSpec);
|
||||||
|
@ -119,9 +120,18 @@ function createServerDeploy(serverSpec) {
|
||||||
id;
|
id;
|
||||||
|
|
||||||
// Volumes
|
// Volumes
|
||||||
deployYaml.spec.template.spec.volumes.find(
|
if (!!storage) {
|
||||||
({ name }) => name === "datadir",
|
const dvi = deployYaml.spec.template.spec.volumes.findIndex(
|
||||||
).persistentVolumeClaim.claimName = `mcl-${mclName}-volume`;
|
({ name }) => name === "datadir",
|
||||||
|
);
|
||||||
|
delete deployYaml.spec.template.spec.volumes[dvi].emptyDir;
|
||||||
|
deployYaml.spec.template.spec.volumes[dvi] = {
|
||||||
|
...deployYaml.spec.template.spec.volumes[dvi],
|
||||||
|
persistentVolumeClaim: {
|
||||||
|
claimName: `mcl-${mclName}-volume`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Backups
|
// Backups
|
||||||
if (backupEnabled) {
|
if (backupEnabled) {
|
||||||
|
@ -194,9 +204,10 @@ export default async function createServerResources(createSpec) {
|
||||||
const rconService = createRconService(createSpec);
|
const rconService = createRconService(createSpec);
|
||||||
const extraService = createExtraService(createSpec);
|
const extraService = createExtraService(createSpec);
|
||||||
const serverResources = [];
|
const serverResources = [];
|
||||||
serverResources.push(
|
if (!!serverVolume)
|
||||||
k8sCore.createNamespacedPersistentVolumeClaim(namespace, serverVolume),
|
serverResources.push(
|
||||||
);
|
k8sCore.createNamespacedPersistentVolumeClaim(namespace, serverVolume),
|
||||||
|
);
|
||||||
if (!!extraService)
|
if (!!extraService)
|
||||||
serverResources.push(
|
serverResources.push(
|
||||||
k8sCore.createNamespacedService(namespace, extraService),
|
k8sCore.createNamespacedService(namespace, extraService),
|
||||||
|
|
|
@ -103,43 +103,47 @@ export default function CreateCoreOptions() {
|
||||||
<MemoryOption value={spec.memory} onChange={coreUpdate("memory")} />
|
<MemoryOption value={spec.memory} onChange={coreUpdate("memory")} />
|
||||||
<StorageOption value={spec.storage} onChange={coreUpdate("storage")} />
|
<StorageOption value={spec.storage} onChange={coreUpdate("storage")} />
|
||||||
<ExtraPortsOption onChange={updateSpec} />
|
<ExtraPortsOption onChange={updateSpec} />
|
||||||
<FormControlLabel
|
{spec.storage !== 0 && (
|
||||||
control={
|
<FormControlLabel
|
||||||
<Switch
|
control={
|
||||||
checked={backupEnabled}
|
<Switch
|
||||||
onChange={toggleBackupEnabled}
|
checked={backupEnabled}
|
||||||
inputProps={{ "aria-label": "controlled" }}
|
onChange={toggleBackupEnabled}
|
||||||
/>
|
inputProps={{ "aria-label": "controlled" }}
|
||||||
}
|
/>
|
||||||
label="Enable Backups?"
|
}
|
||||||
labelPlacement="start"
|
label="Enable Backups?"
|
||||||
sx={{ mr: "auto" }}
|
labelPlacement="start"
|
||||||
/>
|
sx={{ mr: "auto" }}
|
||||||
{backupEnabled && (
|
/>
|
||||||
<FormControl
|
|
||||||
fullWidth
|
|
||||||
sx={{ mt: "2rem", display: "flex", gap: ".5rem" }}
|
|
||||||
>
|
|
||||||
<Typography variant="h6">Backups</Typography>
|
|
||||||
<BackupHostOption
|
|
||||||
value={spec.backupHost}
|
|
||||||
onChange={coreUpdate("backupHost")}
|
|
||||||
/>
|
|
||||||
<BackupBucketOption
|
|
||||||
value={spec.backupBucket}
|
|
||||||
onChange={coreUpdate("backupBucket")}
|
|
||||||
/>
|
|
||||||
<BackupIdOption
|
|
||||||
value={spec.backupId}
|
|
||||||
onChange={coreUpdate("backupId")}
|
|
||||||
/>
|
|
||||||
<BackupKeyOption
|
|
||||||
value={spec.backupKey}
|
|
||||||
onChange={coreUpdate("backupKey")}
|
|
||||||
/>
|
|
||||||
<BackupIntervalOption onChange={coreUpdate("backupInterval")} />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
)}
|
||||||
|
{backupEnabled &&
|
||||||
|
spec.storage !==
|
||||||
|
0(
|
||||||
|
<FormControl
|
||||||
|
fullWidth
|
||||||
|
sx={{ mt: "2rem", display: "flex", gap: ".5rem" }}
|
||||||
|
>
|
||||||
|
<Typography variant="h6">Backups</Typography>
|
||||||
|
<BackupHostOption
|
||||||
|
value={spec.backupHost}
|
||||||
|
onChange={coreUpdate("backupHost")}
|
||||||
|
/>
|
||||||
|
<BackupBucketOption
|
||||||
|
value={spec.backupBucket}
|
||||||
|
onChange={coreUpdate("backupBucket")}
|
||||||
|
/>
|
||||||
|
<BackupIdOption
|
||||||
|
value={spec.backupId}
|
||||||
|
onChange={coreUpdate("backupId")}
|
||||||
|
/>
|
||||||
|
<BackupKeyOption
|
||||||
|
value={spec.backupKey}
|
||||||
|
onChange={coreUpdate("backupKey")}
|
||||||
|
/>
|
||||||
|
<BackupIntervalOption onChange={coreUpdate("backupInterval")} />
|
||||||
|
</FormControl>,
|
||||||
|
)}
|
||||||
|
|
||||||
<Button onClick={upsertSpec} variant="contained">
|
<Button onClick={upsertSpec} variant="contained">
|
||||||
Create
|
Create
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue