@chimoney.io/iaas-k8s-deployment
Version:
Kubernetes Infrastructure as a Service deployment package for streamlined multi-cloud environments
144 lines • 7.12 kB
JavaScript
import { DeploymentError } from "../errors/deployment-errors.js";
import { storeDeploymentSnapshot } from "../rollback/rollback-manager.js";
// =============================================================================
// Error Handling Wrapper
// =============================================================================
export async function withErrorHandling(operation, logger, context) {
try {
logger.debug(`Starting operation: ${context}`);
const result = await operation();
logger.debug(`Completed operation: ${context}`);
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Error in ${context}:`, errorMessage);
if (error instanceof Error && error.stack) {
logger.debug("Stack trace:", error.stack);
}
// Re-throw as DeploymentError if not already
if (!(error instanceof DeploymentError)) {
throw new DeploymentError(`${context} failed: ${errorMessage}`, "OPERATION_FAILED", error);
}
throw error;
}
}
// =============================================================================
// Deployment Action Executor
// =============================================================================
export async function executeDeploymentAction(action, stack, logger, stackName) {
switch (action) {
case "up":
logger.info(`Running pulumi up for stack: ${stackName}...`);
let lastOutputTime = Date.now();
const progressInterval = setInterval(() => {
const timeSinceLastOutput = Date.now() - lastOutputTime;
if (timeSinceLastOutput > 30000) {
// 30 seconds without output
logger.info(`[PROGRESS] Deployment still running... (${Math.floor(timeSinceLastOutput / 1000)}s since last output)`);
}
}, 30000);
try {
const upRes = await stack.up({
onOutput: (output) => {
lastOutputTime = Date.now();
// Show Pulumi output at info level to ensure visibility
logger.info(`[PULUMI] ${output.trim()}`);
},
refresh: true, // Ensure we refresh state before updating
});
clearInterval(progressInterval);
logger.info("--- Update Summary ---");
if (upRes.summary) {
logger.info(`Status: ${upRes.summary.result}`);
logger.info(`Version: ${upRes.summary.version}`);
logger.debug("Resource changes:", JSON.stringify(upRes.summary.resourceChanges, null, 2));
}
logger.info("--- Outputs ---");
logger.debug("Stack outputs:", JSON.stringify(upRes.outputs, null, 2));
// Store configuration snapshot for successful deployments
if (upRes.summary?.result === "succeeded") {
try {
await storeDeploymentSnapshot(stack, logger, upRes);
logger.debug("Configuration snapshot stored for successful deployment");
}
catch (snapshotError) {
logger.warn("Failed to store configuration snapshot:", snapshotError);
// Don't fail the deployment for snapshot errors
}
}
let kubeconfig;
if (upRes.outputs.kubeconfig && upRes.outputs.kubeconfig.value) {
kubeconfig =
typeof upRes.outputs.kubeconfig.value === "string"
? upRes.outputs.kubeconfig.value
: JSON.stringify(upRes.outputs.kubeconfig.value);
logger.info("Kubeconfig is available in deployment outputs");
logger.debug("To configure kubectl:");
logger.debug(`1. Save kubeconfig to file: kubeconfig-${stackName}.yaml`);
logger.debug(`2. Set KUBECONFIG environment variable`);
logger.debug(`3. Test connection with: kubectl get nodes`);
}
return {
outputs: upRes.outputs,
summary: upRes.summary,
kubeconfig,
};
}
catch (error) {
clearInterval(progressInterval);
throw error;
}
case "preview":
logger.info(`Running pulumi preview for stack: ${stackName}...`);
await stack.preview({
onOutput: (output) => logger.info(`[PULUMI] ${output.trim()}`),
onError: (error) => logger.error(error),
});
logger.info("Preview finished successfully");
return {};
case "destroy":
logger.info(`Running pulumi destroy for stack: ${stackName}...`);
const destroyRes = await stack.destroy({
onOutput: (output) => {
// Show Pulumi output at info level to ensure visibility during destroy
logger.info(`[PULUMI] ${output.trim()}`);
},
});
logger.info("--- Destroy Summary ---");
if (destroyRes.summary) {
logger.info(`Status: ${destroyRes.summary.result}`);
logger.debug("Resource changes:", JSON.stringify(destroyRes.summary.resourceChanges, null, 2));
}
return {
summary: destroyRes.summary,
};
case "outputs":
logger.info(`Fetching outputs for stack: ${stackName}...`);
const outputs = await stack.outputs();
logger.info("--- Stack Outputs ---");
logger.info("Outputs:", JSON.stringify(outputs, null, 2));
return {
outputs: outputs,
};
case "refresh":
logger.info(`Running pulumi refresh for stack: ${stackName}...`);
const refreshRes = await stack.refresh({
onOutput: (output) => logger.info(`[PULUMI] ${output.trim()}`),
});
logger.info("--- Refresh Summary ---");
if (refreshRes.summary) {
logger.info(`Status: ${refreshRes.summary.result}`);
}
return {
summary: refreshRes.summary,
};
case "rollback":
logger.info(`Running rollback for stack: ${stackName}...`);
// Rollback is handled in the main function, this is just for completeness
throw new DeploymentError("Rollback should be handled in main deployment function", "INVALID_ACTION");
default:
throw new DeploymentError(`Unknown action: ${action}. Available actions: up, preview, destroy, outputs, refresh, rollback`, "INVALID_ACTION", { action });
}
}
//# sourceMappingURL=deployment-executor.js.map