diff --git a/dist/app.js b/dist/app.js index 2f46e88..d87897e 100644 --- a/dist/app.js +++ b/dist/app.js @@ -8,4 +8,4 @@ const kc = new k8s.KubeConfig(); kc.loadFromDefault(); } -main().catch((e)=>{console.log(e)}); +main().catch((e)=>{console.error(e)}); diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index d5b9e3a..084e78f 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -9,6 +9,8 @@ import { import ExpressClientError, { sendError } from "../util/ExpressClientError.js"; import { toggleServer } from "../k8s/k8s-server-control.js"; import { checkAuthorization } from "../database/queries/server-queries.js"; +import { WARN } from "../util/logging.js"; +import modifyServerResources from "../k8s/server-modify.js"; const dnsRegex = new RegExp( `^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`, @@ -158,10 +160,15 @@ export async function getServer(req, res) { export async function modifyServer(req, res) { if (payloadFilter(req, res) !== "filtered") return; const serverSpec = req.body; + if (!!serverSpec.host) + WARN( + "MODIFY", + "Warning, hostname changing is not implimented yet! Please ask the developer if you'd like to see this added!", + ); try { await checkServerId(req.cairoId, serverSpec); const serverEntry = await modifyServerEntry(serverSpec); - // await createServerResources(serverEntry); + await modifyServerResources(serverEntry); res.sendStatus(200); } catch (e) { sendError(res)(e); diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index 326572b..a08d05b 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -165,7 +165,7 @@ export async function modifyServerEntry(serverSpec) { id, // ownerCairoId: owner_cairo_id, // DIsabled! If these becomes a reqest, please create a new function! name, - host, + // host, // TODO: Can only be updated if service name is generic and non descriptive version, serverType: server_type, cpu, // TODO: Ignored for now by the K8S manifests @@ -180,28 +180,66 @@ export async function modifyServerEntry(serverSpec) { backupInterval: backup_interval, } = serverSpec; - const q = updateWhereAllQuery( - table, - { + const q = + updateWhereAllQuery( + table, + { + name, + // host, // TODO: Can only be updated if service name is generic and non descriptive + version, + server_type, + cpu, // TODO: Ignored for now by the K8S manifests + memory, + // storage, // DO NOT INCLUDE THIS KEY, Not all storage providers in kubernetes allow for dynamically resizable PVCs + extra_ports, + backup_enabled, + backup_host, + backup_bucket_path, + backup_id, + backup_key, + backup_interval, + }, + { id }, + ) + ` RETURNING *;`; + try { + const entries = await pg.query(q); + const { name, - host, + host, // Should always read the database value + server_type: serverType, + storage, + extra_ports: extraPorts, + backup_enabled: backupEnabled, + backup_host: backupHost, + backup_bucket_path: backupPath, + backup_id: backupId, + backup_key: backupKey, + backup_interval: backupInterval, + } = entries[0]; + + const mclName = getMclName(host, id); + + return { + name, // Could change + mclName, // Shouldn't change + id, // Won't change + // host, // TODO: Can only be updated if service name is generic and non descriptive version, - server_type, + serverType, cpu, // TODO: Ignored for now by the K8S manifests memory, - // storage, // DO NOT INCLUDE THIS KEY, Not all storage providers in kubernetes allow for dynamically resizable PVCs - extra_ports, - backup_enabled, - backup_host, - backup_bucket_path, - backup_id, - backup_key, - backup_interval, - }, - { id }, - ); - - return pg.query(q); + storage, + extraPorts, + backupEnabled, + backupHost, + backupPath, + backupId, + backupKey, + backupInterval, + }; + } catch (e) { + asExpressClientError(e); + } } export async function getServerEntries() { diff --git a/lib/k8s/configs/server-svc.yml b/lib/k8s/configs/server-svc.yml index f21db9a..a6f520e 100644 --- a/lib/k8s/configs/server-svc.yml +++ b/lib/k8s/configs/server-svc.yml @@ -11,8 +11,6 @@ metadata: namespace: changeme-namespace spec: internalTrafficPolicy: Cluster - ipFamilies: - - IPv4 ipFamilyPolicy: SingleStack ports: # Programatically add all FTP ports. Port range includes 20, 21, 40000-40001 - name: minecraft diff --git a/lib/k8s/server-create.js b/lib/k8s/server-create.js index 30e21fa..7d09e61 100644 --- a/lib/k8s/server-create.js +++ b/lib/k8s/server-create.js @@ -18,7 +18,7 @@ const namespace = process.env.MCL_SERVER_NAMESPACE; const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); -function createExtraService(serverSpec) { +export function createExtraService(serverSpec) { const { mclName, id, extraPorts } = serverSpec; if (!extraPorts) return; const serviceYaml = loadYaml("lib/k8s/configs/extra-svc.yml"); @@ -49,7 +49,7 @@ function createExtraService(serverSpec) { return serviceYaml; } -function createBackupSecret(serverSpec) { +export function createBackupSecret(serverSpec) { if (!serverSpec.backupEnabled) return; // If backup not defined, don't create RCLONE secret const { mclName, id, backupId, backupKey, backupHost } = serverSpec; const backupYaml = loadYaml("lib/k8s/configs/backup-secret.yml"); @@ -153,7 +153,7 @@ function createServerDeploy(serverSpec) { return deployYaml; } -function createServerService(serverSpec) { +export function createServerService(serverSpec) { const { mclName, host, id } = serverSpec; const serviceYaml = loadYaml("lib/k8s/configs/server-svc.yml"); serviceYaml.metadata.annotations["ingress.qumine.io/hostname"] = host; diff --git a/lib/k8s/server-modify.js b/lib/k8s/server-modify.js new file mode 100644 index 0000000..74a1428 --- /dev/null +++ b/lib/k8s/server-modify.js @@ -0,0 +1,59 @@ +import k8s from "@kubernetes/client-node"; +import { + createExtraService, + createBackupSecret, + createServerService, +} from "./server-create.js"; +import kc from "./k8s-config.js"; +import { getServerAssets } from "./k8s-server-control.js"; +const k8sCore = kc.makeApiClient(k8s.CoreV1Api); +const namespace = process.env.MCL_SERVER_NAMESPACE; + +export default async function modifyServerResources(modifySpec) { + const { id: serverId } = modifySpec; + const serverAssets = await getServerAssets(serverId); + const serverService = createServerService(modifySpec); + const extraService = createExtraService(modifySpec); + const backupSecret = createBackupSecret(modifySpec); + const serverResources = []; + + if (!!serverService) + // Will Always Exist + serverResources.push( + k8sCore.replaceNamespacedService( + serverAssets.service.metadata.name, + namespace, + serverService, + ), + ); + + if (!!extraService && !!serverAssets.extraService) + // Might not exist + serverResources.push( + k8sCore.replaceNamespacedService( + serverAssets.extraService.metadata.name, + namespace, + extraService, + ), + ); + else if (!!extraService) + serverResources.push( + k8sCore.createNamespacedService(namespace, extraService), + ); + + if (!!backupSecret && !!serverAssets.backupSecret) + // Might not exist + serverResources.push( + k8sCore.replaceNamespacedSecret( + serverAssets.backupSecret.metadata.name, + namespace, + backupSecret, + ), + ); + else if (!!backupSecret) + serverResources.push( + k8sCore.createNamespacedSecret(namespace, backupSecret), + ); + + return await Promise.all(serverResources); +}