@topgroup/diginext
Version:
A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.
1,120 lines • 75.2 kB
JavaScript
"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