UNPKG

@chimoney.io/iaas-k8s-deployment

Version:

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

516 lines • 22.3 kB
import { TierCalculator } from "../utils/tier-calculator.js"; import { TierMigrationManager } from "../utils/tier-migration.js"; import { createKubecostClient } from "../utils/kubecost-client.js"; import { PLAN_TIER_DEFINITIONS, DEFAULT_KUBECOST_CONFIG, } from "../types/plans.js"; import chalk from "chalk"; /** * CLI commands for managing tier-based resource allocation */ export class TierCommands { constructor(options) { this.options = options; this.calculator = new TierCalculator(options.logger); // Initialize migration manager with Kubecost client const kubecostClient = createKubecostClient(options.kubecostUrl || "http://localhost:9090", DEFAULT_KUBECOST_CONFIG, options.logger); this.migrationManager = new TierMigrationManager(this.calculator, kubecostClient, options.logger); } /** * Show available tiers and their resource allocations */ async showTiers() { console.log(chalk.blue.bold("\nšŸ“Š Available Plan Tiers\n")); Object.entries(PLAN_TIER_DEFINITIONS).forEach(([tier, definition]) => { console.log(chalk.green.bold(`${tier.toUpperCase()} - $${definition.monthlyPriceUSD}/month`)); console.log(` Display Name: ${definition.displayName}`); console.log(` Description: ${definition.description}`); console.log(` Max Namespaces: ${definition.maxNamespaces}`); console.log(` Max PVCs: ${definition.maxPersistentVolumeClaims}`); console.log(` Network Policies: ${definition.networkPoliciesEnabled ? "Enabled" : "Disabled"}`); // Service details console.log(` Services:`); Object.entries(definition.services).forEach(([serviceName, serviceConfig]) => { console.log(` ${serviceName}:`); console.log(` CPU: ${serviceConfig.cpu}`); console.log(` Memory: ${serviceConfig.memory}`); console.log(` Storage: ${serviceConfig.storage}`); console.log(` Replicas: ${serviceConfig.replicas}`); }); console.log(""); }); } /** * Calculate resource allocation for a specific tier */ async calculateTier(tier) { try { const resources = this.calculator.calculateResources(tier); const validation = this.calculator.validateTierResources(tier, resources); const monthlyCost = this.calculator.calculateMonthlyCost(tier); console.log(chalk.blue.bold(`\nšŸ“‹ Resource Allocation for ${tier.toUpperCase()} Tier\n`)); console.log(chalk.yellow("Tier Information:")); console.log(` Display Name: ${resources.displayName}`); console.log(` Description: ${resources.description}`); console.log(` Monthly Cost: $${monthlyCost}`); console.log(` Max Namespaces: ${resources.maxNamespaces}`); console.log(` Max PVCs: ${resources.maxPersistentVolumeClaims}`); console.log(` Network Policies: ${resources.networkPoliciesEnabled ? "Enabled" : "Disabled"}\n`); console.log(chalk.yellow("Service Resource Allocation:")); Object.entries(resources.services).forEach(([serviceName, serviceConfig]) => { console.log(` ${serviceName}:`); console.log(` CPU: ${serviceConfig.cpu}`); console.log(` Memory: ${serviceConfig.memory}`); console.log(` Storage: ${serviceConfig.storage}`); console.log(` Replicas: ${serviceConfig.replicas}`); }); console.log(chalk.yellow("\nValidation:")); console.log(` Valid: ${validation.isValid ? chalk.green("āœ“") : chalk.red("āœ—")}`); if (!validation.isValid) { validation.errors.forEach((error) => { console.log(` Error: ${chalk.red(error)}`); }); } if (validation.warnings.length > 0) { validation.warnings.forEach((warning) => { console.log(` Warning: ${chalk.yellow(warning)}`); }); } } catch (error) { console.error(chalk.red(`Error calculating tier: ${error}`)); process.exit(1); } } /** * Validate current tier configuration */ async validateTier(tier) { try { console.log(chalk.blue.bold(`\nšŸ” Validating ${tier.toUpperCase()} Tier Configuration\n`)); const resources = this.calculator.calculateResources(tier); const validation = this.calculator.validateTierResources(tier, resources); console.log(chalk.yellow("Resource Validation:")); console.log(` Status: ${validation.isValid ? chalk.green("āœ… Valid") : chalk.red("āŒ Invalid")}`); if (validation.errors.length > 0) { console.log(chalk.red("\nErrors:")); validation.errors.forEach((error) => { console.log(` āŒ ${error}`); }); } if (validation.warnings.length > 0) { console.log(chalk.yellow("\nWarnings:")); validation.warnings.forEach((warning) => { console.log(` āš ļø ${warning}`); }); } } catch (error) { console.error(chalk.red(`Error validating tier: ${error}`)); process.exit(1); } } /** * Migrate resources to a new tier */ async migrateTier(currentTier, targetTier) { try { console.log(chalk.blue.bold(`\nšŸš€ Migrating from ${currentTier.toUpperCase()} to ${targetTier.toUpperCase()} Tier\n`)); // Perform migration const result = await this.migrationManager.executeMigration(this.options.companyName, currentTier, targetTier, this.options.namespace); if (result.success) { console.log(chalk.green("Migration successful!")); if (result.duration) { console.log(` Duration: ${result.duration}`); } if (result.details) { console.log(` Steps Executed: ${result.details.stepsExecuted || "N/A"}`); if (result.details.costImpact) { const impact = result.details.costImpact; console.log(` Cost Change: $${impact.difference}/month`); } } } else { console.error(chalk.red("Migration failed!")); if (result.error) { console.error(` Error: ${result.error}`); } process.exit(1); } } catch (error) { console.error(chalk.red(`Error during migration: ${error}`)); process.exit(1); } } /** * Plan a tier migration */ async planMigration(fromTier, toTier) { try { console.log(chalk.blue.bold(`\nšŸš€ Planning Migration: ${fromTier.toUpperCase()} → ${toTier.toUpperCase()}\n`)); const plan = await this.migrationManager.planMigration(this.options.companyName, fromTier, toTier, this.options.namespace); console.log(chalk.yellow("šŸ“‹ Migration Plan:")); console.log(` Company: ${plan.companyName}`); console.log(` From Tier: ${plan.fromTier.toUpperCase()}`); console.log(` To Tier: ${plan.toTier.toUpperCase()}`); console.log(` Namespace: ${plan.namespace}`); console.log(` Type: ${plan.migrationType.toUpperCase()}`); console.log(` Estimated Duration: ${plan.estimatedDuration}`); console.log(` Requires Downtime: ${plan.requiresDowntime ? "Yes" : "No"}`); if (plan.costImpact) { console.log(chalk.yellow("\nšŸ’° Cost Impact:")); console.log(` Current Monthly Cost: $${plan.costImpact.currentMonthlyCost}`); console.log(` New Monthly Cost: $${plan.costImpact.newMonthlyCost}`); const change = plan.costImpact.difference; const changeColor = change > 0 ? chalk.red : change < 0 ? chalk.green : chalk.gray; console.log(` Difference: ${changeColor(`${change > 0 ? "+" : ""}$${change}/month`)}`); } if (plan.preChecks.length > 0) { console.log(chalk.yellow("\nāœ… Pre-checks:")); plan.preChecks.forEach((check) => { console.log(` • ${check}`); }); } if (plan.steps.length > 0) { console.log(chalk.yellow("\nšŸ“ Migration Steps:")); plan.steps.forEach((step, index) => { console.log(` ${index + 1}. ${step}`); }); } if (plan.warnings.length > 0) { console.log(chalk.yellow("\nāš ļø Warnings:")); plan.warnings.forEach((warning) => { console.log(` • ${warning}`); }); } if (plan.rollbackSteps.length > 0) { console.log(chalk.yellow("\nšŸ”„ Rollback Steps:")); plan.rollbackSteps.forEach((step, index) => { console.log(` ${index + 1}. ${step}`); }); } } catch (error) { console.error(chalk.red(`Error planning migration: ${error}`)); process.exit(1); } } /** * Execute a tier migration */ async executeMigration(fromTier, toTier, options = {}) { try { console.log(chalk.blue.bold(`\nšŸš€ ${options.dryRun ? "Simulating" : "Executing"} Migration: ${fromTier.toUpperCase()} → ${toTier.toUpperCase()}\n`)); if (options.dryRun) { console.log(chalk.yellow("šŸ” DRY RUN MODE - No actual changes will be made")); } const result = await this.migrationManager.executeMigration(this.options.companyName, fromTier, toTier, this.options.namespace, options); if (result.success) { console.log(chalk.green.bold(`\nāœ… Migration completed successfully!`)); if (result.duration) { console.log(`Duration: ${result.duration}`); } if (result.details) { console.log(chalk.yellow("\nšŸ“Š Migration Details:")); console.log(` From Tier: ${result.details.fromTier?.toUpperCase()}`); console.log(` To Tier: ${result.details.toTier?.toUpperCase()}`); if (result.details.stepsExecuted) { console.log(` Steps Executed: ${result.details.stepsExecuted}`); } if (result.details.costImpact) { const impact = result.details.costImpact; console.log(` Cost Change: $${impact.difference}/month`); } } } else { console.error(chalk.red.bold(`\nāŒ Migration failed!`)); if (result.error) { console.error(`Error: ${result.error}`); } if (result.rollbackRequired) { console.error(chalk.yellow("šŸ”„ Rollback may be required")); } process.exit(1); } } catch (error) { console.error(chalk.red(`Error executing migration: ${error}`)); process.exit(1); } } /** * Validate a tier migration */ async validateMigration(fromTier, toTier, options = {}) { try { console.log(chalk.blue.bold(`\nšŸ” Validating Migration: ${fromTier.toUpperCase()} → ${toTier.toUpperCase()}\n`)); const validation = await this.migrationManager.validateMigration(this.options.companyName, fromTier, toTier, this.options.namespace, options); console.log(`Validation Status: ${validation.isValid ? chalk.green("āœ… Valid") : chalk.red("āŒ Invalid")}`); if (validation.errors.length > 0) { console.log(chalk.red("\nāŒ Errors:")); validation.errors.forEach((error) => { console.log(` • ${error}`); }); } if (validation.warnings.length > 0) { console.log(chalk.yellow("\nāš ļø Warnings:")); validation.warnings.forEach((warning) => { console.log(` • ${warning}`); }); } if (validation.recommendations.length > 0) { console.log(chalk.blue("\nšŸ’” Recommendations:")); validation.recommendations.forEach((recommendation) => { console.log(` • ${recommendation}`); }); } if (!validation.isValid) { process.exit(1); } } catch (error) { console.error(chalk.red(`Error validating migration: ${error}`)); process.exit(1); } } /** * Rollback a tier migration */ async rollbackMigration(fromTier, toTier) { try { console.log(chalk.blue.bold(`\nšŸ”„ Rolling back Migration: ${toTier.toUpperCase()} → ${fromTier.toUpperCase()}\n`)); const result = await this.migrationManager.rollbackMigration(this.options.companyName, fromTier, toTier, this.options.namespace); if (result.success) { console.log(chalk.green.bold(`\nāœ… Rollback completed successfully!`)); if (result.duration) { console.log(`Duration: ${result.duration}`); } } else { console.error(chalk.red.bold(`\nāŒ Rollback failed!`)); if (result.error) { console.error(`Error: ${result.error}`); } process.exit(1); } } catch (error) { console.error(chalk.red(`Error rolling back migration: ${error}`)); process.exit(1); } } /** * Show possible migration paths from a tier */ async showMigrationPaths(tier) { try { console.log(chalk.blue.bold(`\nšŸ›¤ļø Migration Paths from ${tier.toUpperCase()} Tier\n`)); const paths = this.migrationManager.getPossibleMigrations(tier); if (paths.length === 0) { console.log(chalk.yellow("No migration paths available from this tier.")); return; } paths.forEach((path) => { const arrow = path.migrationType === "upgrade" ? "ā¬†ļø" : "ā¬‡ļø"; const complexity = path.complexity === "low" ? chalk.green("Low") : path.complexity === "medium" ? chalk.yellow("Medium") : chalk.red("High"); console.log(`${arrow} ${tier.toUpperCase()} → ${path.targetTier.toUpperCase()}`); console.log(` Type: ${path.migrationType.toUpperCase()}`); console.log(` Complexity: ${complexity}`); console.log(` Estimated Duration: ${path.estimatedDuration}`); console.log(""); }); } catch (error) { console.error(chalk.red(`Error showing migration paths: ${error}`)); process.exit(1); } } } /** * Add tier management commands to yargs */ export function addTierCommands(yargs, logger) { const defaultLogger = { info: (message) => console.log(`[INFO] ${message}`), warn: (message) => console.warn(`[WARN] ${message}`), error: (message) => console.error(`[ERROR] ${message}`), debug: (message) => console.debug(`[DEBUG] ${message}`), }; const commandLogger = logger || defaultLogger; return yargs.command("tiers", "Manage tier-based resource allocation", (yargs) => yargs .command("list", "Show available tiers and their specifications", {}, async (args) => { const commands = new TierCommands({ ...args, logger: commandLogger, }); await commands.showTiers(); }) .command("calculate <tier>", "Calculate resource allocation for a specific tier", (yargs) => yargs.positional("tier", { describe: "Plan tier to calculate", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }), async (args) => { const commands = new TierCommands({ ...args, logger: commandLogger, }); await commands.calculateTier(args.tier); }) .command("validate <tier>", "Validate tier configuration", (yargs) => yargs.positional("tier", { describe: "Tier to validate", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }), async (args) => { const commands = new TierCommands({ ...args, logger: commandLogger, }); await commands.validateTier(args.tier); }) .command("migrate <currentTier> <targetTier>", "Migrate resources to a new tier", (yargs) => yargs .positional("currentTier", { describe: "Current plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }) .positional("targetTier", { describe: "Target plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }), async (args) => { const commands = new TierCommands({ ...args, logger: commandLogger, }); await commands.migrateTier(args.currentTier, args.targetTier); }) .command("plan-migration <fromTier> <toTier>", "Plan a migration from one tier to another", (yargs) => yargs .positional("fromTier", { describe: "Current plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }) .positional("toTier", { describe: "Target plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }), async (args) => { const commands = new TierCommands({ ...args, logger: commandLogger, }); await commands.planMigration(args.fromTier, args.toTier); }) .command("execute-migration <fromTier> <toTier>", "Execute a migration from one tier to another", (yargs) => yargs .positional("fromTier", { describe: "Current plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }) .positional("toTier", { describe: "Target plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }) .option("dry-run", { describe: "Simulate the migration without making changes", type: "boolean", default: false, }) .option("force", { describe: "Force the migration, skipping confirmation", type: "boolean", default: false, }) .option("skip-pre-checks", { describe: "Skip pre-migration checks", type: "boolean", default: false, }), async (args) => { const commands = new TierCommands({ ...args, logger: commandLogger, }); await commands.executeMigration(args.fromTier, args.toTier, { dryRun: args.dryRun, force: args.force, skipPreChecks: args.skipPreChecks, }); }) .command("validate-migration <fromTier> <toTier>", "Validate a migration from one tier to another", (yargs) => yargs .positional("fromTier", { describe: "Current plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }) .positional("toTier", { describe: "Target plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }) .option("check-resource-usage", { describe: "Check resource usage for the migration", type: "boolean", default: true, }) .option("check-cluster-capacity", { describe: "Check cluster capacity for the migration", type: "boolean", default: true, }), async (args) => { const commands = new TierCommands({ ...args, logger: commandLogger, }); await commands.validateMigration(args.fromTier, args.toTier, { checkResourceUsage: args.checkResourceUsage, checkClusterCapacity: args.checkClusterCapacity, }); }) .command("rollback-migration <fromTier> <toTier>", "Rollback a migration from one tier to another", (yargs) => yargs .positional("fromTier", { describe: "Current plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }) .positional("toTier", { describe: "Target plan tier", type: "string", choices: Object.keys(PLAN_TIER_DEFINITIONS), }), async (args) => { const commands = new TierCommands({ ...args, logger: commandLogger, }); await commands.rollbackMigration(args.fromTier, args.toTier); }) .option("company-name", { describe: "Company name for tier operations", type: "string", demandOption: true, }) .option("stack-name", { describe: "Pulumi stack name", type: "string", demandOption: true, }) .option("namespace", { describe: "Kubernetes namespace (for shared deployments)", type: "string", }) .option("kubeconfig", { describe: "Path to kubeconfig file", type: "string", }) .option("kubecost-url", { describe: "Kubecost server URL", type: "string", }) .demandCommand(1, "Please specify a tier command")); } //# sourceMappingURL=tier-commands.js.map