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