UNPKG

@topgroup/diginext

Version:

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

214 lines (213 loc) 9.98 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.execCustomProvider = exports.createImagePullingSecret = exports.connectDockerRegistry = exports.authenticate = void 0; const log_1 = require("diginext-utils/dist/xconsole/log"); const fs_1 = __importDefault(require("fs")); const js_yaml_1 = __importDefault(require("js-yaml")); const path_1 = __importDefault(require("path")); const yargs_1 = __importDefault(require("yargs")); const const_1 = require("../../config/const"); /** * Authenticate custom Kubernetes cluster access */ const authenticate = async (cluster, options) => { const { execaCommand } = await Promise.resolve().then(() => __importStar(require("execa"))); const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); const kubeConfigPath = options.filePath; if (!fs_1.default.existsSync(kubeConfigPath)) { throw new Error(`KUBECONFIG file not found. Try: "dx custom auth -f /path/to/your-kube-config.yaml"`); } // test cluster connection before writing config file const clusterConnect = await execaCommand(`kubectl --kubeconfig=${kubeConfigPath} cluster-info`).catch((e) => ({ stdout: undefined, stderr: "Unable to connect", })); const isConnectable = typeof clusterConnect.stdout !== "undefined" && clusterConnect.stderr.indexOf("Unable to connect") === -1; console.log(`[CUSTOM] clusterConnect > ${cluster.slug} :>> Connection status:`, isConnectable); if (!isConnectable) { await DB.updateOne("cluster", { _id: cluster._id }, { isVerified: false }); throw new Error(`Unable to connect to "${cluster.slug}" cluster, please double check your kubeconfig.`); } // load new kubeconfig yaml: let newKubeConfigContent = fs_1.default.readFileSync(kubeConfigPath, "utf8"); let newKubeConfig = js_yaml_1.default.load(newKubeConfigContent); // make sure it won't be duplicated in KUBE_CONFIG -> generate "context" from "cluster.slug" newKubeConfig.clusters = newKubeConfig.clusters.map((_cluster) => { _cluster.name = cluster.slug; return _cluster; }); newKubeConfig.users = newKubeConfig.users.map((_user) => { _user.name = `${cluster.slug}-user`; return _user; }); newKubeConfig.contexts = newKubeConfig.contexts.map((_context) => { _context.context.cluster = cluster.slug; _context.context.user = `${cluster.slug}-user`; _context.name = cluster.slug; return _context; }); newKubeConfig["current-context"] = cluster.slug; newKubeConfigContent = js_yaml_1.default.dump(newKubeConfig); // generate current kubeconfig file: let currentKubeConfigContent; try { /** FOR TEST */ const { stdout } = await execaCommand(`kubectl config view --flatten`); currentKubeConfigContent = stdout; } catch (e) { (0, log_1.logError)(`[CUSTOM_PROVIDER_AUTH]`, e); return; } // Only add new value if it's not existed let currentKubeConfig = js_yaml_1.default.load(currentKubeConfigContent); if (!currentKubeConfig.clusters) currentKubeConfig.clusters = []; if (!currentKubeConfig.contexts) currentKubeConfig.contexts = []; if (!currentKubeConfig.users) currentKubeConfig.users = []; // add cluster newKubeConfig.clusters.forEach((newItem) => { const existedItem = currentKubeConfig.clusters.find((item) => item.name === newItem.name); if (!existedItem) { currentKubeConfig.clusters.push(newItem); } else { // compare OLD & NEW values if (existedItem.cluster.server !== newItem.cluster.server) existedItem.cluster.server = newItem.cluster.server; if (existedItem.cluster["certificate-authority-data"] !== newItem.cluster["certificate-authority-data"]) existedItem.cluster["certificate-authority-data"] = newItem.cluster["certificate-authority-data"]; } }); // add user newKubeConfig.users.forEach((newItem) => { const existedItem = currentKubeConfig.users.find((item) => item.name == newItem.name); if (!existedItem) { currentKubeConfig.users.push(newItem); } else { let index = currentKubeConfig.users.findIndex((item) => item.name == newItem.name); // compare OLD & NEW values if (existedItem.user["client-certificate-data"] !== newItem.user["client-certificate-data"]) currentKubeConfig.users[index].user["client-certificate-data"] = newItem.user["client-certificate-data"]; if (existedItem.user["client-key-data"] !== newItem.user["client-key-data"]) currentKubeConfig.users[index].user["client-key-data"] = newItem.user["client-key-data"]; } }); // add context newKubeConfig.contexts.forEach((newItem) => { const existedItem = currentKubeConfig.contexts.find((item) => item.name == newItem.name); if (!existedItem) { currentKubeConfig.contexts.push(newItem); } else { if (existedItem.context.cluster !== newItem.context.cluster) existedItem.context.cluster = newItem.context.cluster; if (existedItem.context.user !== newItem.context.user) existedItem.context.user = newItem.context.user; } }); // console.dir(currentKubeConfig, { depth: 10 }); // [ONLY] for "custom" cluster -> context name == slug == short name const currentContext = newKubeConfig["current-context"]; if (options.isDebugging) console.log("[CUSTOM CLUSTER] Auth > currentContext :>> ", currentContext); // currentKubeConfig["current-context"] = newKubeConfig["current-context"]; // console.log(`[CLUSTER_AUTH] KUBE_CONFIG :>>`, currentKubeConfig); const finalKubeConfigContent = js_yaml_1.default.dump(currentKubeConfig); // log(finalKubeConfigContent); // console.log(`[CLUSTER_AUTH] KUBE_CONFIG :>>`, finalKubeConfigContent); const kubeConfigDir = path_1.default.resolve(const_1.HOME_DIR, ".kube"); const kubeConfigFile = path_1.default.resolve(kubeConfigDir, "config"); if (!fs_1.default.existsSync(kubeConfigDir)) fs_1.default.mkdirSync(kubeConfigDir, { recursive: true }); fs_1.default.writeFileSync(kubeConfigFile, finalKubeConfigContent, "utf8"); // console.log(`[CLUSTER_AUTH] finalKubeConfigContent :>>`, finalKubeConfigContent); // console.log(`[CLUSTER_AUTH] kubeConfigFile :>>`, kubeConfigFile); // if authentication is success -> update cluster as verified: // console.log("currentContext :>> ", currentContext); cluster = await DB.updateOne("cluster", { _id: cluster._id }, { isVerified: true, kubeConfig: newKubeConfigContent, contextName: currentContext, shortName: currentContext, }, { ownership: options.ownership, isDebugging: options.isDebugging }); // console.log("cluster :>> ", cluster); if (!cluster) throw new Error(`Unable to update context to cluster: "${cluster.slug}"`); return cluster; }; exports.authenticate = authenticate; /** * Connect Docker to custom Container Registry * @param {InputOptions} options */ const connectDockerRegistry = async (options) => { (0, log_1.logWarn)("This feature is under development."); return false; }; exports.connectDockerRegistry = connectDockerRegistry; /** * Create image pulling secret of custom Container Registry */ const createImagePullingSecret = async (options) => { (0, log_1.logWarn)(`This feature is under development.`); const { clusterSlug } = options; const secretName = `${clusterSlug}-docker-registry-key`; return { name: secretName, value: null }; }; exports.createImagePullingSecret = createImagePullingSecret; const execCustomProvider = async (options) => { const { secondAction } = options; switch (secondAction) { case "auth": try { await (0, exports.authenticate)(options); } catch (e) { (0, log_1.logError)(`[CUSTOM_PROVIDER_AUTH]`, e); } break; case "connect-registry": try { await (0, exports.connectDockerRegistry)(options); } catch (e) { (0, log_1.logError)(`[CONNECT_REGISTRY]`, e); } break; default: yargs_1.default.showHelp(); break; } }; exports.execCustomProvider = execCustomProvider; exports.default = { authenticate: exports.authenticate, connectDockerRegistry: exports.connectDockerRegistry, createImagePullingSecret: exports.createImagePullingSecret };