@topgroup/diginext
Version:
A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.
249 lines (248 loc) • 11.2 kB
JavaScript
;
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 };