import k8s from "@kubernetes/client-node"; import { ERR } from "../util/logging.js"; const kc = new k8s.KubeConfig(); kc.loadFromDefault(); const k8sDeps = kc.makeApiClient(k8s.AppsV1Api); const k8sCore = kc.makeApiClient(k8s.CoreV1Api); const k8sMetrics = new k8s.Metrics(kc); const namespace = process.env.MCL_SERVER_NAMESPACE; export async function startServer(req, res) { const serverSpec = req.body; if (!serverSpec) return res.sendStatus(400); if (!serverSpec.name) return res.status(400).send("Server name required!"); const { name } = serverSpec; const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); const dep = deploymentRes.body.items.find( (i) => i.metadata.name === `mcl-${name}` ); if (!dep) return res.status(409).send("Server does not exist!"); if (dep.spec.replicas === 1) return res.status(409).send("Server already started!"); dep.spec.replicas = 1; k8sDeps.replaceNamespacedDeployment(`mcl-${name}`, namespace, dep); res.sendStatus(200); } export async function stopServer(req, res) { const serverSpec = req.body; if (!serverSpec) return res.sendStatus(400); if (!serverSpec.name) return res.status(400).send("Server name required!"); const { name } = serverSpec; const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); const dep = deploymentRes.body.items.find( (i) => i.metadata.name === `mcl-${name}` ); if (!dep) return res.status(409).send("Server does not exist!"); if (dep.spec.replicas === 0) return res.status(409).send("Server already stopped!"); dep.spec.replicas = 0; k8sDeps.replaceNamespacedDeployment(`mcl-${name}`, namespace, dep); res.sendStatus(200); } export async function serverList(req, res) { const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); const deployments = deploymentRes.body.items.map((i) => i.metadata.name); // TODO Add an annotation and manage using that const serverDeployments = deployments.filter((d) => d.startsWith("mcl-")); res.json(serverDeployments.map((sd) => sd.substring(4))); } export async function getServers(req, res) { const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); const deployments = deploymentRes.body.items; const podMetricsResponse = await k8sMetrics.getPodMetrics(namespace); // TODO Add an annotation and manage using that const serverDeployments = deployments.filter((d) => d.metadata.name.startsWith("mcl-") ); var name, metrics, started; const servers = serverDeployments.map((s) => { name = s.metadata.name.substring(4); metrics = null; started = !!s.spec.replicas; const pod = podMetricsResponse.items.find(({ metadata: md }) => { return md.labels && md.labels.app && md.labels.app === `mcl-${name}-app`; }); if (pod) { const podCpus = pod.containers.map( ({ usage }) => parseInt(usage.cpu) / 1_000_000 ); const podMems = pod.containers.map( ({ usage }) => parseInt(usage.memory) / 1024 ); metrics = { cpu: Math.ceil(podCpus.reduce((a, b) => a + b)), memory: Math.ceil(podMems.reduce((a, b) => a + b)), }; } return { name, metrics, started }; }); var clusterMetrics = { cpu: 0, memory: 0 }; if (servers.length > 1) { const clusterCpu = servers .map(({ metrics }) => (metrics ? metrics.cpu : 0)) .reduce((a, b) => a + b); const clusterMem = servers .map(({ metrics }) => (metrics ? metrics.memory : 0)) .reduce((a, b) => a + b); clusterMetrics = { cpu: clusterCpu, memory: clusterMem }; } res.json({ servers, clusterMetrics }); }