@chimoney.io/iaas-k8s-deployment
Version:
Kubernetes Infrastructure as a Service deployment package for streamlined multi-cloud environments
247 lines (244 loc) • 10 kB
JavaScript
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