UNPKG

@topgroup/diginext

Version:

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

204 lines (203 loc) 10.1 kB
"use strict"; 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;