UNPKG

@chimoney.io/iaas-k8s-deployment

Version:

Kubernetes Infrastructure as a Service deployment package for streamlined multi-cloud environments

247 lines (244 loc) 10 kB
export class DeploymentMonitor { constructor(deploymentId, action, stackName, companyName, logger) { this.healthChecks = []; this.metrics = { deploymentId, startTime: new Date(), action, stackName, companyName, success: false, progressEvents: [], }; this.logger = logger; } recordProgress(progress) { this.metrics.progressEvents.push(progress); this.logger.info(`Progress: ${progress.status} - ${progress.message}`); } recordCompletion(result) { this.metrics.endTime = new Date(); this.metrics.duration = this.metrics.endTime.getTime() - this.metrics.startTime.getTime(); this.metrics.success = result.success; this.metrics.rollbackPerformed = result.rollbackPerformed; if (!result.success && result.error) { this.metrics.errorType = this.categorizeError(result.error); } if (result.summary) { this.extractResourceChanges(result.summary); } this.logger.info("Deployment metrics recorded", this.getMetricsSummary()); } categorizeError(error) { if (error.includes("timeout") || error.includes("TIMEOUT")) { return "TIMEOUT"; } if (error.includes("permission") || error.includes("unauthorized")) { return "PERMISSION"; } if (error.includes("validation") || error.includes("invalid")) { return "VALIDATION"; } if (error.includes("network") || error.includes("connection")) { return "NETWORK"; } if (error.includes("resource") && error.includes("conflict")) { return "RESOURCE_CONFLICT"; } return "UNKNOWN"; } extractResourceChanges(summary) { if (summary.resourceChanges) { this.metrics.resourcesCreated = summary.resourceChanges.create || 0; this.metrics.resourcesUpdated = summary.resourceChanges.update || 0; this.metrics.resourcesDeleted = summary.resourceChanges.delete || 0; } } async performHealthChecks() { this.healthChecks = []; // Check Pulumi CLI availability await this.checkPulumiCli(); // Check cloud provider credentials await this.checkCloudCredentials(); // Check Helm chart accessibility await this.checkHelmChart(); // Check network connectivity await this.checkNetworkConnectivity(); return this.healthChecks; } async checkPulumiCli() { try { const { exec } = await import("child_process"); const { promisify } = await import("util"); const execAsync = promisify(exec); const { stdout } = await execAsync("pulumi version"); this.addHealthCheck("pulumi-cli", "healthy", `Pulumi CLI available: ${stdout.trim()}`); } catch (error) { this.addHealthCheck("pulumi-cli", "unhealthy", `Pulumi CLI not available: ${error instanceof Error ? error.message : String(error)}`); } } async checkCloudCredentials() { const cloudProvider = process.env.CLOUD_PROVIDER || "gcp"; try { if (cloudProvider === "aws") { await this.checkAwsCredentials(); } else if (cloudProvider === "gcp") { await this.checkGcpCredentials(); } } catch (error) { this.addHealthCheck("cloud-credentials", "unhealthy", `Cloud credentials check failed: ${error instanceof Error ? error.message : String(error)}`); } } async checkAwsCredentials() { try { const { exec } = await import("child_process"); const { promisify } = await import("util"); const execAsync = promisify(exec); const { stdout } = await execAsync("aws sts get-caller-identity"); const identity = JSON.parse(stdout); this.addHealthCheck("aws-credentials", "healthy", `AWS credentials valid for user: ${identity.UserId}`, { account: identity.Account, arn: identity.Arn }); } catch (error) { this.addHealthCheck("aws-credentials", "unhealthy", "AWS credentials not configured or invalid"); } } async checkGcpCredentials() { try { const { exec } = await import("child_process"); const { promisify } = await import("util"); const execAsync = promisify(exec); const { stdout } = await execAsync('gcloud auth list --format="value(account)" --filter="status:ACTIVE"'); const activeAccount = stdout.trim(); if (activeAccount) { this.addHealthCheck("gcp-credentials", "healthy", `GCP credentials valid for account: ${activeAccount}`); } else { this.addHealthCheck("gcp-credentials", "unhealthy", "No active GCP account found"); } } catch (error) { this.addHealthCheck("gcp-credentials", "unhealthy", "GCP credentials not configured or invalid"); } } async checkHelmChart() { const helmChartPath = process.env.HELM_CHART_PATH || "../helm-chart"; try { const fs = await import("fs"); const path = await import("path"); const chartYamlPath = path.join(helmChartPath, "Chart.yaml"); if (fs.existsSync(chartYamlPath)) { const chartContent = fs.readFileSync(chartYamlPath, "utf8"); this.addHealthCheck("helm-chart", "healthy", `Helm chart accessible at: ${helmChartPath}`, { path: chartYamlPath }); } else { this.addHealthCheck("helm-chart", "unhealthy", `Helm chart not found at: ${chartYamlPath}`); } } catch (error) { this.addHealthCheck("helm-chart", "unhealthy", `Error checking Helm chart: ${error instanceof Error ? error.message : String(error)}`); } } async checkNetworkConnectivity() { const cloudProvider = process.env.CLOUD_PROVIDER || "gcp"; try { const testUrls = cloudProvider === "aws" ? ["https://aws.amazon.com", "https://s3.amazonaws.com"] : ["https://cloud.google.com", "https://storage.googleapis.com"]; // Simple connectivity check (in a real implementation, you might use actual SDK calls) this.addHealthCheck("network-connectivity", "healthy", `Network connectivity assumed healthy for ${cloudProvider}`); } catch (error) { this.addHealthCheck("network-connectivity", "unhealthy", "Network connectivity issues detected"); } } addHealthCheck(name, status, message, details) { this.healthChecks.push({ name, status, message, timestamp: new Date(), details, }); } getMetrics() { return { ...this.metrics }; } getMetricsSummary() { return { deploymentId: this.metrics.deploymentId, duration: this.metrics.duration, success: this.metrics.success, resourceChanges: { created: this.metrics.resourcesCreated || 0, updated: this.metrics.resourcesUpdated || 0, deleted: this.metrics.resourcesDeleted || 0, }, progressEventCount: this.metrics.progressEvents.length, rollbackPerformed: this.metrics.rollbackPerformed, }; } getHealthStatus() { const hasUnhealthy = this.healthChecks.some((check) => check.status === "unhealthy"); return { overall: hasUnhealthy ? "unhealthy" : "healthy", checks: this.healthChecks, }; } exportMetrics() { return JSON.stringify({ ...this.getMetrics(), healthChecks: this.healthChecks, }, null, 2); } } // ============================================================================= // Deployment Reporter // ============================================================================= export class DeploymentReporter { static generateReport(metrics, healthChecks) { const duration = metrics.duration ? `${(metrics.duration / 1000).toFixed(2)}s` : "N/A"; const successIcon = metrics.success ? "✅" : "❌"; let report = ` # Deployment Report ${successIcon} ## Summary - **Deployment ID**: ${metrics.deploymentId} - **Action**: ${metrics.action} - **Stack**: ${metrics.stackName} - **Company**: ${metrics.companyName} - **Status**: ${metrics.success ? "SUCCESS" : "FAILED"} - **Duration**: ${duration} - **Start Time**: ${metrics.startTime.toISOString()} - **End Time**: ${metrics.endTime?.toISOString() || "N/A"} ## Resource Changes - **Created**: ${metrics.resourcesCreated || 0} - **Updated**: ${metrics.resourcesUpdated || 0} - **Deleted**: ${metrics.resourcesDeleted || 0} ## Progress Timeline `; metrics.progressEvents.forEach((event, index) => { report += `${index + 1}. **${event.status}** - ${event.message} (${event.timestamp.toISOString()})\n`; }); if (metrics.rollbackPerformed) { report += `\n⚠️ **Rollback Performed**: Automatic rollback was executed due to deployment failure.\n`; } if (healthChecks.length > 0) { report += `\n## Health Checks\n`; healthChecks.forEach((check) => { const icon = check.status === "healthy" ? "✅" : check.status === "unhealthy" ? "❌" : "⚠️"; report += `- ${icon} **${check.name}**: ${check.message}\n`; }); } return report; } } //# sourceMappingURL=monitoring.js.map