UNPKG

@topgroup/diginext

Version:

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

249 lines (248 loc) 11.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.execDigitalOcean = exports.connectDockerToRegistry = exports.createImagePullingSecret = exports.createRecordInDomain = exports.authenticate = void 0; const log_1 = require("diginext-utils/dist/xconsole/log"); const inquirer_1 = __importDefault(require("inquirer")); const js_yaml_1 = __importDefault(require("js-yaml")); const lodash_1 = require("lodash"); const yargs_1 = __importDefault(require("yargs")); const app_config_1 = require("../../app.config"); const plugins_1 = require("../../plugins"); const ask_for_cluster_1 = require("../cluster/ask-for-cluster"); const k8s_1 = __importDefault(require("../k8s")); const kube_config_1 = require("../k8s/kube-config"); /** * * @param {InputOptions} options */ const authenticate = async (options) => { const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); const { execaCommand } = await Promise.resolve().then(() => __importStar(require("execa"))); let API_ACCESS_TOKEN; if (!options.key && !options.input) (0, log_1.logError)(`[DIGITAL_OCEAN] API ACCESS TOKEN not found. Learn more: https://docs.digitalocean.com/reference/api/create-personal-access-token/`); API_ACCESS_TOKEN = options.key ? options.key : options.input; // authenticate Docker with this container registry try { await execaCommand(`doctl auth init --access-token ${API_ACCESS_TOKEN}`); return true; } catch (e) { (0, log_1.logWarn)(`[DIGITAL_OCEAN]`, e); } // wait 5s and retry 1 more time (sometime the API on DO is unreachable) await (0, plugins_1.wait)(5 * 1000); try { await execaCommand(`doctl auth init --access-token ${API_ACCESS_TOKEN}`); return true; } catch (e) { (0, log_1.logError)(`[DIGITAL_OCEAN]`, e); return false; } }; exports.authenticate = authenticate; /** * @deprecated */ const createRecordInDomain = async (input) => { (0, log_1.logError)(`[DIGITAL_OCEAN] createRecordInDomain() > This function is deprecated.`); return; }; exports.createRecordInDomain = createRecordInDomain; /** * Create DigitalOcean Container Registry image's pull secret */ const createImagePullingSecret = async (options) => { const { execa, execaCommand, execaSync } = await Promise.resolve().then(() => __importStar(require("execa"))); const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); // Implement create "imagePullSecret" of Digital Ocean const { registrySlug, clusterSlug, namespace = "default" } = options; if (!registrySlug) { (0, log_1.logError)(`[DIGITAL_OCEAN] Container Registry's slug is required.`); return; } if (!clusterSlug) { (0, log_1.logError)(`[DIGITAL_OCEAN] Cluster's short name is required.`); return; } // get Container Registry data: const registry = await DB.findOne("registry", { slug: registrySlug }, { subpath: "/all", ignorable: true }); if (!registry) { (0, log_1.logError)(`[DIGITAL_OCEAN] Container Registry (${registrySlug}) not found. Please contact your admin or create a new one.`); return; } // Get SERVICE ACCOUNT from CONTAINER REGISTRY -> to authenticate & generate "imagePullSecrets" const { host, serviceAccount, provider: providerShortName } = registry; // Get "context" by "cluster" -> to create "imagePullSecrets" of "registry" in cluster's namespace const cluster = await DB.findOne("cluster", { slug: clusterSlug }); if (!cluster) { (0, log_1.logError)(`[DIGITAL_OCEAN] Cluster "${clusterSlug}" not found.`); return; } const { name: context } = await (0, kube_config_1.getKubeContextByCluster)(cluster); const secretName = `${providerShortName}-docker-registry-key`; const { apiAccessToken: API_ACCESS_TOKEN } = registry; // check namespace is existed const isNsExisted = await k8s_1.default.isNamespaceExisted(namespace, { context }); if (!isNsExisted) { // create new namespace? const ns = await k8s_1.default.createNamespace(namespace, { context }); // still can't create namespace -> throw error! if (!ns) throw new Error(`Namespace "${namespace}" is not existed on this cluster ("${clusterSlug}").`); return; } // create secret in the namespace (if needed) const applyCommand = `| kubectl apply -f -`; try { // command: "doctl registry kubernetes-manifest" const { stdout: registryYaml } = await execaCommand(`doctl registry kubernetes-manifest --context ${context} --namespace ${namespace} --name ${secretName} --access-token ${API_ACCESS_TOKEN} ${applyCommand}`); const registrySecretData = js_yaml_1.default.load(registryYaml); if (!registrySecretData) { (0, log_1.logError)(`[DIGITAL_OCEAN] Failed to create "imagePullSecrets" in "${namespace}" namespace of "${context}" cluster.`); return; } // Save to database const imagePullSecret = { name: secretName, value: registrySecretData.data[".dockerconfigjson"], }; const [updatedRegistry] = await DB.update("registry", { slug: registrySlug }, { imagePullSecret }); if (!updatedRegistry) (0, log_1.logError)(`[DIGITAL_OCEAN] Can't update container registry of Digital Ocean.`); // log(`DigitalOcean.createImagePullingSecret() :>>`, { updatedRegistry }); return imagePullSecret; } catch (e) { throw new Error(`[DIGITAL_OCEAN] Error creating "imagePullSecret": ${e}`.replace(API_ACCESS_TOKEN, "***")); } }; exports.createImagePullingSecret = createImagePullingSecret; /** * Connect Docker to Digital Ocean Container Registry * @param {InputOptions} options */ const connectDockerToRegistry = async (options) => { var _a; const { execa, execaCommand, execaSync } = await Promise.resolve().then(() => __importStar(require("execa"))); const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); const { host, key: API_ACCESS_TOKEN, userId, workspaceId, registry: registrySlug } = options; try { let connectRes; // connect DOCKER to CONTAINER REGISTRY if (API_ACCESS_TOKEN) { connectRes = await execaCommand(`doctl registry login --access-token ${API_ACCESS_TOKEN}`); } else { connectRes = await execaCommand(`doctl registry login`); } if (app_config_1.Config.BUILDER === "podman") { // connect PODMAN to CONTAINER REGISTRY connectRes = await execaCommand(`podman login`); } if (options.isDebugging) (0, log_1.log)(`[DIGITAL OCEAN] connectDockerRegistry >`, { authRes: connectRes }); } catch (e) { (0, log_1.logError)(e); return; } const existingRegistry = await DB.findOne("registry", { slug: registrySlug }, { subpath: "/all", ignorable: true }); if (options.isDebugging) (0, log_1.log)(`[DIGITAL OCEAN] connectDockerRegistry >`, { existingRegistry }); if (existingRegistry) return existingRegistry; // IF NOT EXISTED -> Save this container registry to database! const registryHost = host || "registry.digitalocean.com"; const imageBaseURL = `${registryHost}/${((_a = options.workspace) === null || _a === void 0 ? void 0 : _a.slug) || "diginext"}`; let newRegistry = await DB.create("registry", { name: "Digital Ocean Container Registry", provider: "digitalocean", host: registryHost, imageBaseURL, apiAccessToken: API_ACCESS_TOKEN, owner: userId, workspace: workspaceId, }); return newRegistry; }; exports.connectDockerToRegistry = connectDockerToRegistry; const execDigitalOcean = async (options) => { const { secondAction } = options; const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); switch (secondAction) { case "auth": try { await (0, exports.authenticate)(options); } catch (e) { (0, log_1.logError)(e); } break; case "connect-registry": try { await (0, exports.connectDockerToRegistry)(options); } catch (e) { (0, log_1.logError)(e); } break; case "create-image-pull-secret": const registries = await DB.find("registry", {}, { subpath: "/all" }); if ((0, lodash_1.isEmpty)(registries)) { (0, log_1.logError)(`[DIGITAL_OCEAN] This workspace doesn't have any registered Container Registries.`); return; } const { selectedRegistry } = await inquirer_1.default.prompt({ message: `Select the container registry:`, type: "list", choices: registries.map((reg, i) => { return { name: `[${i + 1}] ${reg.name} (${reg.provider})`, value: reg }; }), }); const clusterSlug = typeof options.cluster === "boolean" ? (await (0, ask_for_cluster_1.askForCluster)()).slug : options.cluster; try { await (0, exports.createImagePullingSecret)({ clusterSlug, registrySlug: selectedRegistry.slug, namespace: options.namespace, }); } catch (e) { (0, log_1.logError)(e); } break; default: yargs_1.default.showHelp(); break; } }; exports.execDigitalOcean = execDigitalOcean; exports.default = { authenticate: exports.authenticate, connectDockerRegistry: exports.connectDockerToRegistry, createImagePullingSecret: exports.createImagePullingSecret };