UNPKG

@topgroup/diginext

Version:

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

194 lines (193 loc) 10.6 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.authClusterBySlug = exports.authCluster = exports.switchContextToCluster = exports.switchContext = void 0; const log_1 = require("diginext-utils/dist/xconsole/log"); const fs_1 = require("fs"); const plugins_1 = require("../../plugins"); const custom_1 = __importDefault(require("../providers/custom")); const digitalocean_1 = __importDefault(require("../providers/digitalocean")); const gcloud_1 = __importDefault(require("../providers/gcloud")); const kube_config_1 = require("./kube-config"); const switchContext = async (context) => { const result = await (0, plugins_1.execCmd)(`kubectl config use-context ${context}`, `[CLUSTER AUTH] Cannot switch current kube context to "${context}"`); return typeof result !== "undefined"; }; exports.switchContext = switchContext; const switchContextToCluster = async (clusterSlug, providerShortName) => { const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); let context; if (providerShortName) { const ctx = await (0, kube_config_1.getKubeContextByClusterSlug)(clusterSlug, providerShortName); context = ctx.name; } else { const cluster = await DB.findOne("cluster", { slug: clusterSlug }); if (!cluster) { (0, log_1.logError)(`Can't switch to cluster "${clusterSlug}".`); return; } context = cluster.contextName; if (!context && cluster.providerShortName) { const ctx = await (0, kube_config_1.getKubeContextByClusterSlug)(clusterSlug, cluster.providerShortName); context = ctx.name; } } if (!context) { (0, log_1.logError)(`[CLUSTER AUTH] Cannot switch current kube context to "${clusterSlug}"`); return; } const result = await (0, plugins_1.execCmd)(`kubectl config use-context ${context}`, `[CLUSTER AUTH] Cannot switch current kube context to "${clusterSlug}"`); return typeof result !== "undefined"; }; exports.switchContextToCluster = switchContextToCluster; /** * Authenticate current machine with the K8S cluster by its short name (context-name). */ const authCluster = async (cluster, options) => { const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); const { shouldSwitchContextToThisCluster = true, isDebugging } = options; const { providerShortName, slug: clusterSlug } = cluster; if (isDebugging) console.log("[AUTH CLUSTER] cluster :>> ", cluster); if (!clusterSlug) throw new Error(`Param "slug" (cluster's slug) is required.`); if (!providerShortName) throw new Error(`Param "provider" (Cloud Provider's short name) is required.`); let filePath; let context; switch (providerShortName) { case "gcloud": // Only support Google Service Account authentication const { serviceAccount } = cluster; if (cluster.isVerified && !serviceAccount) throw new Error(`Permissions denied, please contact your administrator.`); if (!serviceAccount) throw new Error(`This cluster doesn't have any Google Service Account to authenticate.`); // validate service account file: const saConfig = JSON.parse(serviceAccount); if (!saConfig.project_id) throw new Error(`Invalid Google service account.`); const projectID = saConfig.project_id; // start authenticating... filePath = (0, plugins_1.createTmpFile)(`gsa.json`, serviceAccount); const gcloudAuth = await gcloud_1.default.authenticate({ filePath }); if (!gcloudAuth) throw new Error(`[UNKNOWN] Cannot authenticate the Google Cloud provider.`); // delete temporary service account (0, fs_1.unlink)(filePath, (err) => err && (0, log_1.logError)(`[CLUSTER AUTH] Remove tmp file:`, err)); // save this cluster to KUBE_CONFIG if (!cluster.zone) throw new Error(`ZONE is required for Google Cloud Cluster authentication.`); await (0, plugins_1.execCmd)(`gcloud container clusters get-credentials ${cluster.shortName} --zone=${cluster.zone} --project=${projectID}`); // get K8S context context = await (0, kube_config_1.getKubeContextByCluster)(cluster); if (context) { cluster = await DB.updateOne("cluster", { slug: clusterSlug }, { contextName: context.name }, { ownership: options.ownership }); } else { throw new Error(`Context of "${clusterSlug}" cluster not found.`); } // switch context (if needed) // if (shouldSwitchContextToThisCluster) await switchContext(context.name); // mark this cluster verified cluster = await DB.updateOne("cluster", { slug: clusterSlug }, { isVerified: true }, { ownership: options.ownership }); (0, log_1.logSuccess)(`[CLUSTER MANAGER] ✓ Connected to "${clusterSlug}" cluster.`); return cluster; case "digitalocean": // Only support Digital Ocean API access token authentication const { apiAccessToken } = cluster; if (cluster.isVerified && !apiAccessToken) throw new Error(`Permissions denied, please contact your administrator.`); if (!apiAccessToken) { throw new Error(`This cluster doesn't have any Digital Ocean API access token to authenticate.`); } const doAuth = await digitalocean_1.default.authenticate({ key: apiAccessToken }); if (!doAuth) throw new Error(`[UNKNOWN] Cannot authenticate the Digital Ocean provider.`); // save cluster access info to "kubeconfig" await (0, plugins_1.execCmd)(`doctl kubernetes cluster kubeconfig save ${cluster.shortName}`); // get K8S context context = await (0, kube_config_1.getKubeContextByCluster)(cluster); if (context) { cluster = await DB.updateOne("cluster", { slug: clusterSlug }, { contextName: context.name }, { ownership: options.ownership }); } else { throw new Error(`Context of "${clusterSlug}" cluster not found.`); } // switch context (if needed) // if (shouldSwitchContextToThisCluster) await switchContext(context.name); // mark this cluster verified cluster = await DB.updateOne("cluster", { slug: clusterSlug }, { isVerified: true }); (0, log_1.logSuccess)(`[CLUSTER MANAGER] ✓ Connected to "${clusterSlug}" cluster.`); return cluster; case "custom": // Only support "kube-config" authentication const { kubeConfig } = cluster; if (cluster.isVerified && !kubeConfig) throw new Error(`Permissions denied, please contact your administrator.`); if (!kubeConfig) throw new Error(`This cluster doesn't have any "kube-config" data to authenticate.`); filePath = (0, plugins_1.createTmpFile)(`${clusterSlug}-kube-config.yaml`, kubeConfig); // start authenticating & save cluster access info to "kubeconfig"... cluster = await custom_1.default.authenticate(cluster, { filePath, isDebugging, ownership: options.ownership }); if (!cluster) throw new Error(`Unable to authenticate this cluster: ${cluster.name}`); const { contextName, isVerified } = cluster; if (!contextName) throw new Error(`Context of "${clusterSlug}" cluster not found.`); if (!isVerified) throw new Error(`Unable to connect to "${clusterSlug}" cluster.`); // delete temporary file (0, fs_1.unlink)(filePath, (err) => err && (0, log_1.logError)(`[CLUSTER AUTH] Remove tmp file:`, err)); // switch context (if needed) // if (shouldSwitchContextToThisCluster) await switchContext(contextName); (0, log_1.logSuccess)(`[CLUSTER MANAGER] ✓ Connected to "${clusterSlug}" cluster.`); return cluster; default: throw new Error(`❌ This provider (${providerShortName}) is not supported yet.`); } }; exports.authCluster = authCluster; /** * Authenticate current machine with the K8S cluster by its short name (context-name). * @param clusterSlug - A cluster name on the cloud provider (**NOT** a cluster in `kubeconfig`) */ const authClusterBySlug = async (clusterSlug, options) => { if (!clusterSlug) throw new Error(`Param "clusterSlug" is required.`); // find the cluster in the database: const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); let cluster = await DB.findOne("cluster", { slug: clusterSlug }); if (!cluster) { throw new Error(`This cluster (${clusterSlug}) is not existed, please contact your administrator or register a new one with the CLI command.`); } return (0, exports.authCluster)(cluster, options); }; exports.authClusterBySlug = authClusterBySlug; exports.default = exports.authCluster;