UNPKG

@topgroup/diginext

Version:

A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.

1,120 lines 75.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getPodsByFilter = exports.getAllPods = exports.getPods = exports.getPod = exports.deleteServiceByFilter = exports.deleteService = exports.getAllServices = exports.getServices = exports.getService = exports.createService = exports.deleteStatefulSetsByFilter = exports.deleteStatefulSet = exports.scaleStatefulSet = exports.getStatefulSetsByFilter = exports.getStatefulSet = exports.getAllStatefulSets = exports.getStatefulSets = exports.deleteDeploymentsByFilter = exports.deleteDeploy = exports.setDeployImagePullSecretByFilter = exports.setDeployPortAll = exports.setDeployImageAll = exports.setDeployImage = exports.scaleDeployByFilter = exports.scaleDeploy = exports.getDeploysByFilter = exports.getDeploy = exports.getAllDeploys = exports.getDeploys = exports.deleteIngressByFilter = exports.deleteIngress = exports.getIngresses = exports.getIngress = exports.getIngressClasses = exports.getAllIngresses = exports.deleteSecretsByFilter = exports.deleteSecret = exports.isSecretExisted = exports.getAllSecrets = exports.getSecrets = exports.isNamespaceExisted = exports.deleteNamespaceByCluster = exports.deleteNamespace = exports.createNamespace = exports.getNamespace = exports.getAllNamespaces = exports.getAllNodes = exports.kubectlApplyContent = exports.kubectlApply = exports.objectToFilterLabels = void 0; exports.kubectlAnnotateDeployment = exports.deleteStorageClassesByFilter = exports.deleteStorageClass = exports.getStorageClassesByFilter = exports.getAllStorageClasses = exports.getStorageClasses = exports.getStorageClass = exports.deletePersistentVolumeClaimsByFilter = exports.deletePersistentVolumeClaim = exports.getPersistentVolumeClaimsByFilter = exports.getAllPersistentVolumeClaims = exports.getPersistentVolumeClaims = exports.getPersistentVolumeClaim = exports.deletePersistentVolumesByFilter = exports.deletePersistentVolume = exports.getPersistentVolumesByFilter = exports.getAllPersistentVolumes = exports.getPersistentVolumes = exports.getPersistentVolume = exports.rollbackDeployRevision = exports.rollbackDeploy = exports.deleteEnvVarByFilter = exports.deleteEnvVar = exports.setEnvVarByFilter = exports.setEnvVar = exports.logPodByFilter = exports.logPod = exports.deletePodsByFilter = exports.deletePod = void 0; const makeDaySlug_1 = require("diginext-utils/dist/string/makeDaySlug"); const log_1 = require("diginext-utils/dist/xconsole/log"); const execa_1 = require("execa"); const fs_1 = require("fs"); const lodash_1 = require("lodash"); const path_1 = __importDefault(require("path")); const const_1 = require("../../config/const"); const plugins_1 = require("../../plugins"); const index_1 = __importDefault(require("./index")); /** * Convert filter object to filter labels string * - Use ! for different than value * @example { phase: "!prerelease", app: "abc-xyz" } -> "phase!=prerelease,app=abc-xyz" */ function objectToFilterLabels(obj) { if (!obj) throw new Error(`Input object is required.`); return Object.entries(obj) .map(([key, val]) => { if ((0, lodash_1.startsWith)("!", val)) return `${key}!=${val.substring(1)}`; return `${key}=${val}`; }) .join(","); } exports.objectToFilterLabels = objectToFilterLabels; /** * Similar to `kubectl apply -f deployment.yaml` * @param filePath - Path to Kubernetes YAML file or URL of Kubernetes YAML file * @param namespace - Target namespace of the cluster * @param options - kubectl command options * @returns */ async function kubectlApply(filePath, options = {}) { const { context } = options; const stdout = await (0, plugins_1.execCmd)(`kubectl ${context ? `--context=${context} ` : ""}apply -f ${filePath}`, `[KUBE_CTL] kubectlApply > Failed to apply "${filePath}" of "${context}" cluster.`); if (stdout) (0, log_1.logSuccess)(stdout); return stdout; } exports.kubectlApply = kubectlApply; async function kubectlApplyContent(yamlContent, options = {}) { const { context, filterLabel } = options; if (!yamlContent) throw new Error(`[KUBE_CTL] kubectlApplyContent > YAML content is empty.`); // create temporary YAML file const tmpDir = path_1.default.resolve(const_1.CLI_DIR, `storage/kubectl_tmp`); if (!(0, fs_1.existsSync)(tmpDir)) (0, fs_1.mkdirSync)(tmpDir, { recursive: true }); const filePath = path_1.default.resolve(tmpDir, `deployment.${(0, makeDaySlug_1.makeDaySlug)({ divider: "" })}.yaml`); if ((0, fs_1.existsSync)(filePath)) (0, fs_1.unlinkSync)(filePath); (0, fs_1.writeFileSync)(filePath, yamlContent, "utf8"); // process kubectl apply command which point to that temporary YAML file: const { stdout } = await (0, execa_1.execaCommandSync)(`kubectl ${context ? `--context=${context} ` : ""}apply -f ${filePath} ${filterLabel ? `-l ${filterLabel} ` : ""}`); if (stdout) (0, log_1.logSuccess)(`Applied deployment YAML in "${context}" context:\n${stdout}`); return stdout; } exports.kubectlApplyContent = kubectlApplyContent; /** * Get all nodes of a cluster */ async function getAllNodes(options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("get", "node"); if (filterLabel) args.push("-l", filterLabel); args.push("-o", "json"); // get metrics let usage; try { // get resource usage const { stdout: usageStr } = (0, execa_1.execaCommandSync)(`kubectl --context=${context} top node --no-headers=true`); usage = usageStr.split("\n").map((line) => { const [name, cpu, cpuPercent, memory, memoryPercent] = line.trim().split(/\s+/); const memoryCapacity = Math.round(((0, lodash_1.toInteger)(memory.replace(/Mi/, "")) / (0, lodash_1.toInteger)(memoryPercent.replace("%", ""))) * 100) + "Mi"; const cpuCapacity = Math.round(((0, lodash_1.toInteger)(cpu.replace(/m/, "")) / (0, lodash_1.toInteger)(cpuPercent.replace("%", ""))) * 100) + "m"; return { name, cpu, cpuPercent, cpuCapacity, memory, memoryPercent, memoryCapacity, }; }); } catch (e2) { (0, log_1.logWarn)(`[KUBE_CTL] getAllNodes > ${context} > Unable to get metrics :>> ${e2}`); usage = []; } const { stdout } = await (0, execa_1.execa)("kubectl", args); const nodes = JSON.parse(stdout).items.map((node) => { // get pod count try { const { stdout: podRes } = (0, execa_1.execaCommandSync)(`kubectl --context=${context} get pods --field-selector spec.nodeName=${node.metadata.name} -A -o json`); const pods = JSON.parse(podRes).items; node.podCount = pods.length; } catch (e) { node.podCount = 0; } // usage const nodeUsage = usage.find((n) => n.name === node.metadata.name); node.cpu = (nodeUsage === null || nodeUsage === void 0 ? void 0 : nodeUsage.cpu) || "0"; node.cpuPercent = (nodeUsage === null || nodeUsage === void 0 ? void 0 : nodeUsage.cpuPercent) || "0"; node.cpuCapacity = (nodeUsage === null || nodeUsage === void 0 ? void 0 : nodeUsage.cpuCapacity) || "0"; node.memory = (nodeUsage === null || nodeUsage === void 0 ? void 0 : nodeUsage.memory) || "0"; node.memoryPercent = (nodeUsage === null || nodeUsage === void 0 ? void 0 : nodeUsage.memoryPercent) || "0"; node.memoryCapacity = (nodeUsage === null || nodeUsage === void 0 ? void 0 : nodeUsage.memoryCapacity) || "0"; return node; }); return nodes; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllNodes > ${context} > Unable to get the list:`, e); return []; } } exports.getAllNodes = getAllNodes; /** * Get all namepsaces of a cluster */ async function getAllNamespaces(options = {}) { const { context, filterLabel, skipOnError } = options; const stdout = await (0, plugins_1.execCmd)(`kubectl ${context ? `--context=${context} ` : ""}get namespace ${filterLabel ? `-l ${filterLabel} ` : ""}-o json`, `Can't get namespace list.`); try { return JSON.parse(stdout).items; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllNamespaces > ${context} > Can't get namespace list.`); return []; } } exports.getAllNamespaces = getAllNamespaces; /** * Get a namepsace of a cluster */ async function getNamespace(name, options = {}) { const { context, skipOnError, output = "json" } = options; const stdout = await (0, plugins_1.execCmd)(`kubectl ${context ? `--context=${context} ` : ""}get namespace ${name} -o ${output}`, `Can't get a namespace.`); try { return !(options === null || options === void 0 ? void 0 : options.output) || (options === null || options === void 0 ? void 0 : options.output) === "json" ? JSON.parse(stdout).items : stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllNamespaces > ${context} > Can't get namespace list.`); return; } } exports.getNamespace = getNamespace; /** * Create new namespace of a cluster */ async function createNamespace(namespace, options = {}) { const { context, skipOnError } = options; try { await (0, plugins_1.execCmd)(`kubectl ${context ? `--context=${context} ` : ""}create namespace ${namespace}`); return { name: namespace }; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] createNamespace > ${context} >`, e); return; } } exports.createNamespace = createNamespace; /** * Delete a namespace of a cluster */ async function deleteNamespace(namespace, options = {}) { const { context, filterLabel, skipOnError } = options; try { await (0, plugins_1.execCmd)(`kubectl ${context ? `--context=${context} ` : ""}delete namespace ${namespace} ${filterLabel ? `-l ${filterLabel} ` : ""}`); return { namespace }; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteNamespace > ${context} >`, e); return; } } exports.deleteNamespace = deleteNamespace; /** * Delete a namespace of a cluster */ async function deleteNamespaceByCluster(namespace, clusterSlug) { const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); const cluster = await DB.findOne("cluster", { slug: clusterSlug }); if (!cluster) { (0, log_1.logError)(`[KUBECTL] Can't delete namespace "${namespace}" due to cluster "${clusterSlug}" not found.`); return; } const { name: context } = await index_1.default.getKubeContextByCluster(cluster); try { await (0, plugins_1.execCmd)(`kubectl ${context ? `--context=${context} ` : ""}delete namespace ${namespace}`); return { namespace }; } catch (e) { (0, log_1.logError)(`[KUBE_CTL] deleteNamespaceByCluster > ${context} >`, e); return; } } exports.deleteNamespaceByCluster = deleteNamespaceByCluster; /** * Check whether this namespace was existed */ async function isNamespaceExisted(namespace, options = {}) { const allNamespaces = await getAllNamespaces(options); if ((0, lodash_1.isUndefined)(allNamespaces)) return; if ((0, lodash_1.isEmpty)(allNamespaces)) return false; return typeof allNamespaces.find((ns) => ns.metadata.name === namespace) !== "undefined"; } exports.isNamespaceExisted = isNamespaceExisted; /** * Get all secrets of a namespace */ async function getSecrets(namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const stdout = await (0, plugins_1.execCmd)(`kubectl ${context ? `--context=${context} ` : ""}get secret -n ${namespace} ${filterLabel ? `-l ${filterLabel} ` : ""}-o json`); return JSON.parse(stdout).items; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getSecrets > ${context} >`, e); return []; } } exports.getSecrets = getSecrets; /** * Get all secrets of a cluster */ async function getAllSecrets(options = {}) { const { context, filterLabel, skipOnError } = options; try { const stdout = await (0, plugins_1.execCmd)(`kubectl ${context ? `--context=${context} ` : ""}get secret ${filterLabel ? `-l ${filterLabel} ` : ""}-A -o json`); return JSON.parse(stdout).items; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllSecrets > ${context} >`, e); return []; } } exports.getAllSecrets = getAllSecrets; /** * Check whether this secret was existed in the namespace */ async function isSecretExisted(name, namespace = "default", options = {}) { const allSecrets = await getSecrets(namespace, options); if ((0, lodash_1.isEmpty)(allSecrets)) return false; return typeof allSecrets.find((ns) => ns.metadata.name === name) !== "undefined"; } exports.isSecretExisted = isSecretExisted; /** * Delete a secret in a namespace */ async function deleteSecret(name, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "delete", "secret", name); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteSecret > ${context} >`, e); return; } } exports.deleteSecret = deleteSecret; /** * Delete secrets in a namespace by filter */ async function deleteSecretsByFilter(namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "delete", "secret"); if (filterLabel) args.push(`-l`, filterLabel); const { stdout } = await (0, execa_1.execa)("kubectl", args); return JSON.parse(stdout); } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteSecretsByFilter > ${context} >`, e); return; } } exports.deleteSecretsByFilter = deleteSecretsByFilter; /** * Get all ingresses of a cluster */ async function getAllIngresses(options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("get", "ing", "-A"); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return JSON.parse(stdout).items; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllIngresses > ${context} >`, e); return; } } exports.getAllIngresses = getAllIngresses; async function getIngressClasses(options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("get", "ingressclass"); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return JSON.parse(stdout).items; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getIngressClasses > ${context} >`, e); return; } } exports.getIngressClasses = getIngressClasses; async function getIngress(name, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "ing", name); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return JSON.parse(stdout); } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getIngress > ${context} >`, e); return; } } exports.getIngress = getIngress; /** * Get ingress list of a namespace * @param namespace */ async function getIngresses(namespace = "default", options = {}) { const { context, skipOnError, filterLabel } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "ing"); if (filterLabel) args.push("-l", filterLabel); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return JSON.parse(stdout).items; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getIngress > ${context} >`, e); return []; } } exports.getIngresses = getIngresses; async function deleteIngress(name, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "delete", "ing", name); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteIngress > ${context} >`, e); return; } } exports.deleteIngress = deleteIngress; async function deleteIngressByFilter(namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "delete", "ing"); if (filterLabel) args.push("-l", filterLabel); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteIngressByFilter > ${context} >`, e); return; } } exports.deleteIngressByFilter = deleteIngressByFilter; /** * Get all deployments of a namespace * @param namespace @default "default" */ async function getDeploys(namespace = "default", options = {}) { const { context, filterLabel, skipOnError, metrics = true } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "deploy"); if (filterLabel) args.push("-l", filterLabel); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); const { items } = JSON.parse(stdout); const deploys = items; if (!metrics) return deploys; // get pods usage const usageStr = (0, execa_1.execaCommandSync)(`kubectl --context=${context} -n ${namespace} top pod --no-headers=true`).stdout; const podUsages = usageStr.split("\n").map((line) => { var _a, _b; const [name, cpu = "0m", memory = "0Mi"] = line.trim().split(/\s+/); const cpuInt = (_a = (0, lodash_1.toInteger)(cpu.replace("m", ""))) !== null && _a !== void 0 ? _a : 0; const memInt = (_b = (0, lodash_1.toInteger)(memory.replace("Mi", ""))) !== null && _b !== void 0 ? _b : 0; return { namespace, name, cpu, memory, cpuInt, memInt }; }); return deploys.map((deploy) => { var _a, _b, _c, _d, _e, _f; // resource usage average const cpuAvg = podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).reduce((total, obj) => total + obj.cpuInt, 0) / podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).length; deploy.cpuAvg = `${(0, lodash_1.round)(cpuAvg)}m`; const memAvg = podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).reduce((total, obj) => total + obj.memInt, 0) / podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).length; deploy.memoryAvg = `${(0, lodash_1.round)(memAvg)}Mi`; if (deploy.cpuAvg === "NaNm") deploy.cpuAvg = ""; if (deploy.memoryAvg === "NaNMi") deploy.memoryAvg = ""; // resource usage recommend deploy.cpuRecommend = (deploy.cpuAvg ? (0, lodash_1.toInteger)(deploy.cpuAvg.replace("m", "")) : 0) * 1.5 + "m"; deploy.memoryRecommend = (deploy.memoryAvg ? (0, lodash_1.toInteger)(deploy.memoryAvg.replace("Mi", "")) : 0) * 1.5 + "Mi"; // resource usage capacity deploy.cpuCapacity = ((_c = (_b = (_a = deploy.spec.template.spec.containers.find((cont) => { var _a, _b; return typeof ((_b = (_a = cont.resources) === null || _a === void 0 ? void 0 : _a.limits) === null || _b === void 0 ? void 0 : _b.cpu) !== "undefined"; })) === null || _a === void 0 ? void 0 : _a.resources) === null || _b === void 0 ? void 0 : _b.limits) === null || _c === void 0 ? void 0 : _c.cpu) || ""; deploy.memoryCapacity = ((_f = (_e = (_d = deploy.spec.template.spec.containers.find((cont) => { var _a, _b; return typeof ((_b = (_a = cont.resources) === null || _a === void 0 ? void 0 : _a.limits) === null || _b === void 0 ? void 0 : _b.memory) !== "undefined"; })) === null || _d === void 0 ? void 0 : _d.resources) === null || _e === void 0 ? void 0 : _e.limits) === null || _f === void 0 ? void 0 : _f.memory) || ""; return deploy; }); } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getDeploys > ${context} >`, e); return []; } } exports.getDeploys = getDeploys; /** * Get all deployments of a cluster */ async function getAllDeploys(options = {}) { const { context, filterLabel, skipOnError, metrics = true } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("get", "deploy", "-A"); if (filterLabel) args.push("-l", filterLabel); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); const { items } = JSON.parse(stdout); // get pods usage const usageStr = metrics ? (0, execa_1.execaCommandSync)(`kubectl --context=${context} top pod --no-headers=true -A`).stdout : ""; const podUsages = metrics ? usageStr.split("\n").map((line) => { var _a, _b; const [ns, name, cpu = "0m", memory = "0Mi"] = line.trim().split(/\s+/); const cpuInt = (_a = (0, lodash_1.toInteger)(cpu.replace("m", ""))) !== null && _a !== void 0 ? _a : 0; const memInt = (_b = (0, lodash_1.toInteger)(memory.replace("Mi", ""))) !== null && _b !== void 0 ? _b : 0; return { namespace: ns, name, cpu, memory, cpuInt, memInt }; }) : []; return items.map((deploy) => { var _a, _b, _c, _d, _e, _f; // resource usage average const cpuAvg = podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).reduce((total, obj) => total + obj.cpuInt, 0) / podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).length; deploy.cpuAvg = `${(0, lodash_1.round)(cpuAvg)}m`; const memAvg = podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).reduce((total, obj) => total + obj.memInt, 0) / podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).length; deploy.memoryAvg = `${(0, lodash_1.round)(memAvg)}Mi`; if (deploy.cpuAvg === "NaNm") deploy.cpuAvg = ""; if (deploy.memoryAvg === "NaNMi") deploy.memoryAvg = ""; // resource usage recommend deploy.cpuRecommend = (deploy.cpuAvg ? (0, lodash_1.toInteger)(deploy.cpuAvg.replace("m", "")) : 0) * 1.5 + "m"; deploy.memoryRecommend = (deploy.memoryAvg ? (0, lodash_1.toInteger)(deploy.memoryAvg.replace("Mi", "")) : 0) * 1.5 + "Mi"; // resource usage capacity deploy.cpuCapacity = ((_c = (_b = (_a = deploy.spec.template.spec.containers.find((cont) => { var _a, _b; return typeof ((_b = (_a = cont.resources) === null || _a === void 0 ? void 0 : _a.limits) === null || _b === void 0 ? void 0 : _b.cpu) !== "undefined"; })) === null || _a === void 0 ? void 0 : _a.resources) === null || _b === void 0 ? void 0 : _b.limits) === null || _c === void 0 ? void 0 : _c.cpu) || ""; deploy.memoryCapacity = ((_f = (_e = (_d = deploy.spec.template.spec.containers.find((cont) => { var _a, _b; return typeof ((_b = (_a = cont.resources) === null || _a === void 0 ? void 0 : _a.limits) === null || _b === void 0 ? void 0 : _b.memory) !== "undefined"; })) === null || _d === void 0 ? void 0 : _d.resources) === null || _e === void 0 ? void 0 : _e.limits) === null || _f === void 0 ? void 0 : _f.memory) || ""; return deploy; }); } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllDeploys > ${context} >`, e); return []; } } exports.getAllDeploys = getAllDeploys; /** * Get a deployment in a namespace */ async function getDeploy(name, namespace = "default", options = {}) { const { context, skipOnError, output = "json" } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "deploy", name); if (output === "json") args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return output === "json" ? JSON.parse(stdout) : stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getDeploy > ${context} >`, e); return; } } exports.getDeploy = getDeploy; /** * Get deployments in a namespace by filter labels */ async function getDeploysByFilter(namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "deploy"); if (filterLabel) args.push(`-l`, filterLabel); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return JSON.parse(stdout).items; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getDeploy > ${context} >`, e); return; } } exports.getDeploysByFilter = getDeploysByFilter; /** * Scale replicas of a deployment in a namespace */ async function scaleDeploy(name, replicas, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "scale", "deployment", name, `--replicas=${replicas}`); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] scaleDeploy > ${context} >`, e); return; } } exports.scaleDeploy = scaleDeploy; /** * Scale replicas of multiple deployments in a namespace by label filter * @param name - Deployment's name */ async function scaleDeployByFilter(replicas, namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "scale", "deployment", `--replicas=${replicas}`); if (filterLabel) args.push("-l", filterLabel); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] scaleDeployByFilter > ${context} >`, e); return; } } exports.scaleDeployByFilter = scaleDeployByFilter; /** * Set image to a container of a deployment in a namespace */ async function setDeployImage(name, container, imageURL, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "set", "image", `deployment/${name}`, `${container}=${imageURL}`); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] setDeployImage > ${context} >`, e); return; } } exports.setDeployImage = setDeployImage; /** * Set image to all containers of a deployment in a namespace * @param name - Deployment's name */ async function setDeployImageAll(name, imageURL, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "set", "image", `deployment/${name}`, `*=${imageURL}`); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] setDeployImageAll > ${context} >`, e); return; } } exports.setDeployImageAll = setDeployImageAll; /** * Set port to all containers of a deployment in a namespace * @param name - Deployment's name * @param port - New port */ async function setDeployPortAll(name, port, namespace = "default", options = {}) { const { context, skipOnError } = options; try { // get all container names const deployment = (await getDeploy(name, namespace, options)); const containers = deployment.spec.template.spec.containers; let res = ""; for (let i = 0; i < containers.length; i++) { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "patch", "deployment", name, "--type", "json", `--patch`, `[{"op": "replace", "path": "/spec/template/spec/containers/${i}/ports/0/containerPort", "value": ${port}}]`); const { stdout } = await (0, execa_1.execa)("kubectl", args); res += stdout + "\n"; } return res; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] setDeployPortAll > ${context} >`, e); return; } } exports.setDeployPortAll = setDeployPortAll; /** * Set "imagePullSecrets" name to deployments in a namespace by filter */ async function setDeployImagePullSecretByFilter(imagePullSecretName, namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); // kubectl patch deployment valid-deployment --type json -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]' args.push("-n", namespace, "patch", "deployment"); if (filterLabel) args.push(`-l`, filterLabel); args.push("--type", "json", `--patch`, `'[{"op": "replace", "path": "/spec/containers/0/imagePullSecrets/0/name", "value":"${imagePullSecretName}"}]'`); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] setDeployImagePullSecretByFilter > ${context} >`, e); return; } } exports.setDeployImagePullSecretByFilter = setDeployImagePullSecretByFilter; /** * Delete a deployment in a namespace */ async function deleteDeploy(name, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "delete", "deploy", name); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteDeploy > ${context} :>>`, e); return; } } exports.deleteDeploy = deleteDeploy; /** * Delete a deployments in a namespace by label filter */ async function deleteDeploymentsByFilter(namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "delete", "deploy"); if (filterLabel) args.push("-l", filterLabel); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteDeploymentsByFilter > ${context} >`, e); return; } } exports.deleteDeploymentsByFilter = deleteDeploymentsByFilter; /** * Get all StatefulSets of a namespace * @param namespace @default "default" */ async function getStatefulSets(namespace = "default", options = {}) { const { context, filterLabel, skipOnError, metrics = true } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "statefulset"); if (filterLabel) args.push("-l", filterLabel); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); const { items } = JSON.parse(stdout); const deploys = items; // get pods usage const usageStr = metrics ? (0, execa_1.execaCommandSync)(`kubectl --context=${context} -n ${namespace} top pod --no-headers=true`).stdout : ""; const podUsages = metrics ? usageStr.split("\n").map((line) => { var _a, _b; const [ns, name, cpu = "0m", memory = "0Mi"] = line.trim().split(/\s+/); const cpuInt = (_a = (0, lodash_1.toInteger)(cpu.replace("m", ""))) !== null && _a !== void 0 ? _a : 0; const memInt = (_b = (0, lodash_1.toInteger)(memory.replace("Mi", ""))) !== null && _b !== void 0 ? _b : 0; return { namespace: ns, name, cpu, memory, cpuInt, memInt }; }) : []; return deploys.map((deploy) => { var _a, _b, _c, _d, _e, _f; // resource usage average const cpuAvg = podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).reduce((total, obj) => total + obj.cpuInt, 0) / podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).length; deploy.cpuAvg = `${(0, lodash_1.round)(cpuAvg)}m`; const memAvg = podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).reduce((total, obj) => total + obj.memInt, 0) / podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).length; deploy.memoryAvg = `${(0, lodash_1.round)(memAvg)}Mi`; if (deploy.cpuAvg === "NaNm") deploy.cpuAvg = ""; if (deploy.memoryAvg === "NaNMi") deploy.memoryAvg = ""; // resource usage recommend deploy.cpuRecommend = (deploy.cpuAvg ? (0, lodash_1.toInteger)(deploy.cpuAvg.replace("m", "")) : 0) * 1.5 + "m"; deploy.memoryRecommend = (deploy.memoryAvg ? (0, lodash_1.toInteger)(deploy.memoryAvg.replace("Mi", "")) : 0) * 1.5 + "Mi"; // resource usage capacity deploy.cpuCapacity = ((_c = (_b = (_a = deploy.spec.template.spec.containers.find((cont) => { var _a, _b; return typeof ((_b = (_a = cont.resources) === null || _a === void 0 ? void 0 : _a.limits) === null || _b === void 0 ? void 0 : _b.cpu) !== "undefined"; })) === null || _a === void 0 ? void 0 : _a.resources) === null || _b === void 0 ? void 0 : _b.limits) === null || _c === void 0 ? void 0 : _c.cpu) || ""; deploy.memoryCapacity = ((_f = (_e = (_d = deploy.spec.template.spec.containers.find((cont) => { var _a, _b; return typeof ((_b = (_a = cont.resources) === null || _a === void 0 ? void 0 : _a.limits) === null || _b === void 0 ? void 0 : _b.memory) !== "undefined"; })) === null || _d === void 0 ? void 0 : _d.resources) === null || _e === void 0 ? void 0 : _e.limits) === null || _f === void 0 ? void 0 : _f.memory) || ""; return deploy; }); } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getStatefulSets > ${context} >`, e); return []; } } exports.getStatefulSets = getStatefulSets; /** * Get all statefulsets of a cluster */ async function getAllStatefulSets(options = {}) { const { context, filterLabel, skipOnError, metrics = true } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("get", "statefulset", "-A"); if (filterLabel) args.push("-l", filterLabel); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); const { items } = JSON.parse(stdout); // get pods usage const usageStr = metrics ? (0, execa_1.execaCommandSync)(`kubectl --context=${context} top pod --no-headers=true -A`).stdout : ""; const podUsages = metrics ? usageStr.split("\n").map((line) => { var _a, _b; const [ns, name, cpu = "0m", memory = "0Mi"] = line.trim().split(/\s+/); const cpuInt = (_a = (0, lodash_1.toInteger)(cpu.replace("m", ""))) !== null && _a !== void 0 ? _a : 0; const memInt = (_b = (0, lodash_1.toInteger)(memory.replace("Mi", ""))) !== null && _b !== void 0 ? _b : 0; return { namespace: ns, name, cpu, memory, cpuInt, memInt }; }) : []; return items.map((deploy) => { var _a, _b, _c, _d, _e, _f; // resource usage average const cpuAvg = podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).reduce((total, obj) => total + obj.cpuInt, 0) / podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).length; deploy.cpuAvg = `${(0, lodash_1.round)(cpuAvg)}m`; const memAvg = podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).reduce((total, obj) => total + obj.memInt, 0) / podUsages.filter((pod) => pod.name.indexOf(deploy.metadata.name) === 0).length; deploy.memoryAvg = `${(0, lodash_1.round)(memAvg)}Mi`; if (deploy.cpuAvg === "NaNm") deploy.cpuAvg = ""; if (deploy.memoryAvg === "NaNMi") deploy.memoryAvg = ""; // resource usage recommend deploy.cpuRecommend = (deploy.cpuAvg ? (0, lodash_1.toInteger)(deploy.cpuAvg.replace("m", "")) : 0) * 1.5 + "m"; deploy.memoryRecommend = (deploy.memoryAvg ? (0, lodash_1.toInteger)(deploy.memoryAvg.replace("Mi", "")) : 0) * 1.5 + "Mi"; // resource usage capacity deploy.cpuCapacity = ((_c = (_b = (_a = deploy.spec.template.spec.containers.find((cont) => { var _a, _b; return typeof ((_b = (_a = cont.resources) === null || _a === void 0 ? void 0 : _a.limits) === null || _b === void 0 ? void 0 : _b.cpu) !== "undefined"; })) === null || _a === void 0 ? void 0 : _a.resources) === null || _b === void 0 ? void 0 : _b.limits) === null || _c === void 0 ? void 0 : _c.cpu) || ""; deploy.memoryCapacity = ((_f = (_e = (_d = deploy.spec.template.spec.containers.find((cont) => { var _a, _b; return typeof ((_b = (_a = cont.resources) === null || _a === void 0 ? void 0 : _a.limits) === null || _b === void 0 ? void 0 : _b.memory) !== "undefined"; })) === null || _d === void 0 ? void 0 : _d.resources) === null || _e === void 0 ? void 0 : _e.limits) === null || _f === void 0 ? void 0 : _f.memory) || ""; return deploy; }); } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllStatefulSets > ${context} >`, e); return []; } } exports.getAllStatefulSets = getAllStatefulSets; /** * Get a StatefulSet in a namespace */ async function getStatefulSet(name, namespace = "default", options = {}) { const { context, skipOnError, output } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "statefulset", name); if (!output || output === "json") args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return !output || output === "json" ? JSON.parse(stdout) : stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getStatefulSet > ${context} >`, e); return; } } exports.getStatefulSet = getStatefulSet; /** * Get StatefulSets in a namespace by filter labels */ async function getStatefulSetsByFilter(namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "statefulset"); if (filterLabel) args.push(`-l`, filterLabel); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return JSON.parse(stdout); } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getStatefulSet > ${context} >`, e); return; } } exports.getStatefulSetsByFilter = getStatefulSetsByFilter; /** * Scale replicas of a StatefulSet in a namespace */ async function scaleStatefulSet(name, replicas, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "scale", "statefulset", name, `--replicas=${replicas}`); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] scaleStatefulSet >`, e); return; } } exports.scaleStatefulSet = scaleStatefulSet; /** * Delete a StatefulSet in a namespace */ async function deleteStatefulSet(name, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "delete", "statefulset", name); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteStatefulSet > ${context} >`, e); return; } } exports.deleteStatefulSet = deleteStatefulSet; /** * Delete StatefulSets in a namespace by label filter */ async function deleteStatefulSetsByFilter(namespace = "default", options = {}) { const { context, filterLabel, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "delete", "statefulset"); if (filterLabel) args.push("-l", filterLabel); const { stdout } = await (0, execa_1.execa)("kubectl", args); return stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] deleteStatefulSetsByFilter > ${context} >`, e); return; } } exports.deleteStatefulSetsByFilter = deleteStatefulSetsByFilter; /** * Create service by name * @param namespace @default "default" */ async function createService(name, namespace = "default", options = {}) { throw new Error(`This feature is under development.`); // const { execa, execaCommand, execaSync, execaCommandSync } = await import("execa"); // const { context, skipOnError } = options; // try { // const args = []; // if (context) args.push(`--context=${context}`); // args.push("-n", namespace, "get", "svc", name); // args.push("-o", "json"); // const { stdout } = await execa("kubectl", args); // return JSON.parse(stdout) as KubeService; // } catch (e) { // if (!skipOnError) logError(`[KUBE_CTL] getService >`, e); // return; // } } exports.createService = createService; /** * Get service by name * @param namespace @default "default" */ async function getService(name, namespace = "default", options = {}) { const { context, skipOnError } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "svc", name); if (!(options === null || options === void 0 ? void 0 : options.output) || (options === null || options === void 0 ? void 0 : options.output) === "json") args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); return !(options === null || options === void 0 ? void 0 : options.output) || (options === null || options === void 0 ? void 0 : options.output) === "json" ? JSON.parse(stdout) : stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getService > ${context} >`, e); return; } } exports.getService = getService; /** * Get services in a namespace * @param namespace @default "default" * @param labelFilter Filter by labels @example "phase!=prerelease,app=abc-xyz" */ async function getServices(namespace = "default", options = {}) { const { context, skipOnError, filterLabel } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("-n", namespace, "get", "svc"); if (filterLabel) args.push("-l", filterLabel); if (!(options === null || options === void 0 ? void 0 : options.output) || (options === null || options === void 0 ? void 0 : options.output) === "json") args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); const { items } = JSON.parse(stdout); return !(options === null || options === void 0 ? void 0 : options.output) || (options === null || options === void 0 ? void 0 : options.output) === "json" ? items : stdout; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllServices > ${context} >`, e); return []; } } exports.getServices = getServices; /** * Get all services in a cluster * @param labelFilter Filter by labels @example "phase!=prerelease,app=abc-xyz" */ async function getAllServices(options = {}) { const { context, skipOnError, filterLabel } = options; try { const args = []; if (context) args.push(`--context=${context}`); args.push("get", "svc"); if (filterLabel) args.push("-l", filterLabel); args.push("-A"); args.push("-o", "json"); const { stdout } = await (0, execa_1.execa)("kubectl", args); const { items } = JSON.parse(stdout); return items; } catch (e) { if (!skipOnError) (0, log_1.logError)(`[KUBE_CTL] getAllServices > ${context} >`, e); return []; } } exports.getAllServices = getAllServices; /** * Delete service by name * @param namespace @default "default" */ async function deleteService(name, namespace