import k8s from "@kubernetes/client-node"; import { getDeployment, getDeployments, getServerAssets, scaleDeployment, } from "./k8s-server-control.js"; import { ERR } from "../util/logging.js"; import ExpressClientError from "../util/ExpressClientError.js"; const kc = new k8s.KubeConfig(); kc.loadFromDefault(); const k8sMetrics = new k8s.Metrics(kc); const namespace = process.env.MCL_SERVER_NAMESPACE; export async function startServerContainer(serverSpec) { const { name } = serverSpec; try { await scaleDeployment(name, true); } catch (e) { ERR("SERVER CONTROL", e); throw new ExpressClientError({ c: 500, m: `Error updating server '${name}'!\n`, }); } } export async function stopServerContainer(serverSpec) { const { name } = serverSpec; try { await scaleDeployment(name, false); } catch (e) { ERR("SERVER CONTROL", e); throw new ExpressClientError({ c: 500, m: `Error updating server '${name}'!`, }); } } export async function getInstances() { const serverDeployments = await getDeployments(); const podMetricsResponse = await k8sMetrics.getPodMetrics(namespace); var name, metrics, services, serverAvailable, ftpAvailable; const serverInstances = serverDeployments.map((s) => { name = s.metadata.annotations["minecluster.dunemask.net/server-name"]; metrics = null; const { containers } = s.spec.template.spec; services = containers.map(({ name }) => name.split("-").pop()); const serverStatusList = s.status.conditions.map( ({ type: statusType, status: sts }) => ({ statusType, sts }), ); const deploymentAvailable = serverStatusList.find( (ss) => ss.statusType === "Available" && ss.sts === "True", ) !== undefined; serverAvailable = services.includes(`server`) && deploymentAvailable; ftpAvailable = services.includes("ftp") && deploymentAvailable; const pod = podMetricsResponse.items.find(({ metadata: md }) => { return md.labels && md.labels.app && md.labels.app === `mcl-${name}-app`; }); if (serverAvailable && 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, services, serverAvailable, ftpAvailable }; }); return serverInstances; } export async function getNamespaceMetrics() { const serverInstances = await getInstances(); var clusterMetrics = { cpu: 0, memory: 0 }; if (servers.length > 1) { const clusterCpu = serverInstances .map(({ metrics }) => (metrics ? metrics.cpu : 0)) .reduce((a, b) => a + b); const clusterMem = serverInstances .map(({ metrics }) => (metrics ? metrics.memory : 0)) .reduce((a, b) => a + b); clusterMetrics = { cpu: clusterCpu, memory: clusterMem }; } return clusterMetrics; }