@topgroup/diginext
Version:
A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.
204 lines (203 loc) • 10.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateProjectAndAppMetadata = exports.checkDomainConflict = exports.checkContainerLogsForErrors = exports.retrieveContainerLogs = exports.finalizeReleaseAndBuild = exports.handleRolloutFailure = exports.checkDeploymentReadiness = exports.authenticateCluster = exports.setupWebhookService = exports.prepareReleaseData = void 0;
const log_1 = require("diginext-utils/dist/xconsole/log");
const k8s_1 = __importDefault(require("../../../modules/k8s"));
const kubectl_1 = require("../../../modules/k8s/kubectl");
const mongodb_1 = require("../../../plugins/mongodb");
const services_1 = require("../../../services");
const mark_release_as_active_1 = require("../mark-release-as-active");
// Helper functions would be implemented similarly, extracting logic from the original function
async function prepareReleaseData(releaseId, onUpdate) {
const releaseSvc = new services_1.ReleaseService();
// Step 1: Validate input
if (!releaseId) {
const error = "Release ID is required";
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(error);
return null;
}
// Step 2: Update release status to in_progress
const releaseData = await releaseSvc.updateOne({ _id: releaseId }, { status: "in_progress" }, { populate: ["owner", "workspace", "build"] });
// Step 3: Validate release data
if (!releaseData) {
const error = `Unable to roll out: Release "${releaseId}" not found.`;
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(error);
return null;
}
// Step 4: Log update
if (onUpdate) {
onUpdate(`Rolling out the release: "${releaseData.slug}" (ID: ${releaseId})`);
}
// Step 5: Return prepared release data
return releaseData;
}
exports.prepareReleaseData = prepareReleaseData;
async function setupWebhookService(owner, workspace, releaseId) {
// Step 1: Initialize webhook service
const webhookSvc = new services_1.WebhookService();
webhookSvc.ownership = { owner, workspace };
// Step 2: Find existing webhook for this release
const webhook = await webhookSvc.findOne({ release: releaseId });
// Step 3: Return webhook service with optional webhook
return { webhookSvc, webhook };
}
exports.setupWebhookService = setupWebhookService;
async function authenticateCluster(clusterSlug, owner, workspace, onUpdate) {
const clusterSvc = new services_1.ClusterService();
// Step 1: Find cluster
const cluster = await clusterSvc.findOne({ slug: clusterSlug }, { subpath: "/all" });
// Step 2: Validate cluster existence
if (!cluster) {
const error = `Cluster "${clusterSlug}" not found.`;
(0, log_1.logError)(error);
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(error);
return null;
}
// Step 3: Authenticate cluster
try {
await k8s_1.default.authCluster(cluster, { ownership: { owner, workspace } });
if (onUpdate) {
onUpdate(`Connected to "${clusterSlug}" cluster successfully.`);
}
return cluster;
}
catch (e) {
const error = `Unable to authenticate the cluster: ${e.message}`;
(0, log_1.logError)(`[AUTHENTICATE_CLUSTER] ${error}`);
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(error);
return null;
}
}
exports.authenticateCluster = authenticateCluster;
async function checkDeploymentReadiness(readinessChecker, requiredReplicas) {
try {
// Step 1: Check deployment readiness
const pods = await readinessChecker.getPods();
const health = await readinessChecker.checkPodHealth(pods);
console.log("checkDeploymentReadiness() :>>", health);
// Step 2: Log readiness status
(0, log_1.log)(`Deployment readiness check: ${health.isHealthy ? "READY" : "NOT READY"}`);
return health.isHealthy;
}
catch (error) {
// Step 3: Handle errors
(0, log_1.logError)(`Deployment readiness check failed: ${error.message}`);
return false;
}
}
exports.checkDeploymentReadiness = checkDeploymentReadiness;
async function handleRolloutFailure(releaseId, buildId, webhookSvc, errorMessage) {
const releaseSvc = new services_1.ReleaseService();
const buildSvc = new services_1.BuildService();
// Step 1: Trigger webhook if available
const webhook = await webhookSvc.findOne({ release: releaseId });
if (webhook) {
webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed");
}
// Step 2: Update release status
await releaseSvc.updateOne({ _id: releaseId }, { status: "failed" }).catch(console.error);
// Step 3: Update build deployment status
await buildSvc.updateOne({ _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error);
// Step 4: Log error
(0, log_1.logError)(`Rollout failure: ${errorMessage}`);
// Step 5: Return error object
return {
error: errorMessage,
data: { releaseId, buildId },
};
}
exports.handleRolloutFailure = handleRolloutFailure;
async function finalizeReleaseAndBuild(releaseId, buildId, projectSlug, appSlug, env, owner) {
const buildSvc = new services_1.BuildService();
const projectSvc = new services_1.ProjectService();
const appSvc = new services_1.AppService();
// 1. Mark this release as active
try {
const latestRelease = await (0, mark_release_as_active_1.markReleaseAsActive)({ id: releaseId, appSlug, env });
if (!latestRelease)
throw new Error(`Release "${releaseId}" not found.`);
}
catch (e) {
const error = `[ERROR] Unable to mark the latest release (${releaseId}) status as "active": ${e.message}`;
(0, log_1.logError)(error);
throw new Error(error);
}
// 2. Update build deployment status to success
const build = await buildSvc.updateOne({ _id: buildId }, { deployStatus: "success" });
// 3. Update project with latest build and updater
await projectSvc.updateOne({ slug: projectSlug }, { lastUpdatedBy: owner.username, latestBuild: build === null || build === void 0 ? void 0 : build._id }, { select: ["_id", "updatedAt"] });
// 4. Update app's deployment environment with latest release details
const appUpdateData = {
[`deployEnvironment.${env}.latestRelease`]: releaseId,
[`deployEnvironment.${env}.appVersion`]: build === null || build === void 0 ? void 0 : build.tag,
[`deployEnvironment.${env}.buildId`]: buildId,
};
await appSvc.updateOne({ slug: appSlug }, appUpdateData, { select: ["_id"] });
// Log success
(0, log_1.logSuccess)(`✅ Successfully finalized release and build for "${appSlug}"`);
}
exports.finalizeReleaseAndBuild = finalizeReleaseAndBuild;
async function retrieveContainerLogs(namespace, appVersion, context, isNewDeploymentReady) {
try {
const logOptions = {
filterLabel: `app-version=${appVersion}`,
context,
...(isNewDeploymentReady ? {} : { previous: true }),
};
const containerLogs = await (0, kubectl_1.logPodByFilter)(namespace, logOptions);
return containerLogs;
}
catch (error) {
(0, log_1.logError)(`Failed to retrieve container logs: ${error}`);
return "";
}
}
exports.retrieveContainerLogs = retrieveContainerLogs;
function checkContainerLogsForErrors(containerLogs) {
const errorPatterns = ["Error from server", "An error occurred", "Command failed", "Unexpected Server Error"];
return errorPatterns.some((pattern) => containerLogs.includes(pattern));
}
exports.checkContainerLogsForErrors = checkContainerLogsForErrors;
async function checkDomainConflict(ingress, namespace, context, onUpdate) {
var _a;
if (!ingress || !((_a = ingress.spec) === null || _a === void 0 ? void 0 : _a.rules))
return false;
const domains = ingress.spec.rules.map((rule) => rule.host).filter(Boolean);
if (domains.length === 0)
return false;
const allIngresses = await k8s_1.default.getAllIngresses({ context });
for (const domain of domains) {
const conflictingIngress = allIngresses.find((ing) => ing.spec.rules.some((rule) => rule.host === domain) && ing.metadata.namespace !== namespace);
if (conflictingIngress) {
const error = `Domain "${domain}" is already in use in namespace "${conflictingIngress.metadata.namespace}"`;
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(error);
return true;
}
}
return false;
}
exports.checkDomainConflict = checkDomainConflict;
async function updateProjectAndAppMetadata(releaseData, buildId, owner) {
const { projectSlug, appSlug, env, _id: releaseId } = releaseData;
const buildSvc = new services_1.BuildService();
const projectSvc = new services_1.ProjectService();
const appSvc = new services_1.AppService();
// Update "deployStatus" of a build to success
const build = await buildSvc.updateOne({ _id: buildId }, { deployStatus: "success" }, { select: ["_id", "deployStatus"] });
// Update project to sort by latest release
const project = await projectSvc.updateOne({ slug: projectSlug }, {
lastUpdatedBy: owner.username,
latestBuild: build === null || build === void 0 ? void 0 : build._id,
}, { select: ["_id", "updatedAt"] });
// Assign this release as "latestRelease" of this app's deploy environment
const app = await appSvc.updateOne({ slug: appSlug }, {
[`deployEnvironment.${env}.latestRelease`]: releaseId,
[`deployEnvironment.${env}.appVersion`]: releaseData.appVersion,
[`deployEnvironment.${env}.buildId`]: buildId,
}, { select: ["_id"] });
return { project, app, build };
}
exports.updateProjectAndAppMetadata = updateProjectAndAppMetadata;