UNPKG

@chimoney.io/iaas-k8s-deployment

Version:

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

562 lines 22.2 kB
// ============================================================================= // Tier-Based Resource Calculator // ============================================================================= import { PlanTier, PLAN_TIER_DEFINITIONS, getTierConfiguration, validateTierConfiguration, calculateTierCosts, } from "../types/plans.js"; export class TierCalculator { constructor(logger) { this.logger = logger; } /** * Calculate resource allocation for a given tier */ calculateTierAllocation(planTier, companyName, namespace, options = {}) { this.logger.info(`Calculating tier allocation for ${planTier} tier`); const result = { success: false, tierConfig: getTierConfiguration(planTier), helmValues: {}, resourceQuota: {}, limitRange: {}, estimatedCosts: { monthly: 0, yearly: 0, currency: "USD", }, warnings: [], errors: [], }; try { // Validate tier configuration const validationErrors = validateTierConfiguration(result.tierConfig); if (validationErrors.length > 0) { result.errors.push(...validationErrors); return result; } // Generate Helm values result.helmValues = this.generateHelmValues(result.tierConfig, companyName, namespace, options); // Generate resource quota result.resourceQuota = this.generateResourceQuota(result.tierConfig, namespace); // Generate limit range result.limitRange = this.generateLimitRange(result.tierConfig, namespace); // Calculate costs const costs = calculateTierCosts(planTier); result.estimatedCosts = { monthly: costs.totalEstimatedCost, yearly: costs.totalEstimatedCost * 12, currency: "USD", }; // Add any warnings this.addTierWarnings(result); result.success = true; this.logger.info(`Tier allocation calculated successfully for ${planTier}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); result.errors.push(`Failed to calculate tier allocation: ${errorMessage}`); this.logger.error(`Tier calculation failed: ${errorMessage}`); } return result; } /** * Validate if a tier can be allocated given cluster capacity */ validateTierAgainstCapacity(planTier, clusterCapacity, existingAllocations = []) { this.logger.info(`Validating ${planTier} against cluster capacity`); const tierConfig = getTierConfiguration(planTier); const tierDef = tierConfig.resourceAllocation; // Calculate total required resources const requiredResources = this.calculateTotalTierResources(tierDef); // Calculate existing allocations const existingResources = existingAllocations.reduce((total, config) => { const resources = this.calculateTotalTierResources(config.resourceAllocation); return { cpu: total.cpu + resources.cpu, memory: total.memory + resources.memory, storage: total.storage + resources.storage, }; }, { cpu: 0, memory: 0, storage: 0 }); // Convert cluster capacity to numbers const clusterCpu = this.parseResourceValue(clusterCapacity.totalCpu, "cpu"); const clusterMemory = this.parseResourceValue(clusterCapacity.totalMemory, "memory"); const clusterStorage = this.parseResourceValue(clusterCapacity.totalStorage, "storage"); // Calculate utilization const totalCpuAfter = existingResources.cpu + requiredResources.cpu; const totalMemoryAfter = existingResources.memory + requiredResources.memory; const totalStorageAfter = existingResources.storage + requiredResources.storage; const cpuUsagePercent = (totalCpuAfter / clusterCpu) * 100; const memoryUsagePercent = (totalMemoryAfter / clusterMemory) * 100; const storageUsagePercent = (totalStorageAfter / clusterStorage) * 100; // Check if allocation is possible (with 80% safety margin) const canAllocate = cpuUsagePercent <= 80 && memoryUsagePercent <= 80 && storageUsagePercent <= 80; const result = { isValid: true, canAllocate, resourceUtilization: { cpuUsagePercent: Math.round(cpuUsagePercent * 100) / 100, memoryUsagePercent: Math.round(memoryUsagePercent * 100) / 100, storageUsagePercent: Math.round(storageUsagePercent * 100) / 100, }, recommendations: [], errors: [], }; // Add recommendations if (cpuUsagePercent > 70) { result.recommendations.push("Consider adding more CPU capacity to the cluster"); } if (memoryUsagePercent > 70) { result.recommendations.push("Consider adding more memory capacity to the cluster"); } if (storageUsagePercent > 70) { result.recommendations.push("Consider adding more storage capacity to the cluster"); } if (!canAllocate) { result.errors.push(`Insufficient cluster capacity for ${planTier} tier allocation`); } return result; } /** * Calculate optimal cluster capacity for a set of tier allocations */ calculateOptimalClusterCapacity(tierAllocations) { let totalCpu = 0; let totalMemory = 0; let totalStorage = 0; tierAllocations.forEach(({ tier, count }) => { const tierDef = PLAN_TIER_DEFINITIONS[tier]; const tierResources = this.calculateTotalTierResources(tierDef); totalCpu += tierResources.cpu * count; totalMemory += tierResources.memory * count; totalStorage += tierResources.storage * count; }); // Add 30% buffer for system overhead and future growth const bufferMultiplier = 1.3; return { totalCpu: `${Math.ceil(totalCpu * bufferMultiplier)}m`, totalMemory: `${Math.ceil(totalMemory * bufferMultiplier)}Mi`, totalStorage: `${Math.ceil(totalStorage * bufferMultiplier)}Gi`, availableCpu: `${Math.ceil(totalCpu * bufferMultiplier)}m`, availableMemory: `${Math.ceil(totalMemory * bufferMultiplier)}Mi`, availableStorage: `${Math.ceil(totalStorage * bufferMultiplier)}Gi`, nodeCount: Math.max(3, Math.ceil((totalCpu * bufferMultiplier) / 4000)), // Assume 4 CPU per node }; } /** * Generate Helm values for tier-based deployment */ generateHelmValues(tierConfig, companyName, namespace, options) { const tierDef = tierConfig.resourceAllocation; const services = tierDef.services; const helmValues = { companyName, namespace, deploymentType: "shared", planTier: tierConfig.planTier, // Disable HPA for tier-based deployments autoscaling: { enabled: false, }, // Service configurations with tier-based resources rafikiAuth: { enabled: true, name: "rafiki-auth", replicaCount: services.rafikiAuth.replicas, resources: { requests: { cpu: services.rafikiAuth.cpu, memory: services.rafikiAuth.memory, }, limits: { cpu: this.calculateLimitFromRequest(services.rafikiAuth.cpu), memory: this.calculateLimitFromRequest(services.rafikiAuth.memory), }, }, hpa: { enabled: false, // Explicitly disable HPA }, storage: { size: services.rafikiAuth.storage, }, }, rafikiBackend: { enabled: true, name: "rafiki-backend", replicaCount: services.rafikiBackend.replicas, resources: { requests: { cpu: services.rafikiBackend.cpu, memory: services.rafikiBackend.memory, }, limits: { cpu: this.calculateLimitFromRequest(services.rafikiBackend.cpu), memory: this.calculateLimitFromRequest(services.rafikiBackend.memory), }, }, hpa: { enabled: false, // Explicitly disable HPA }, storage: { size: services.rafikiBackend.storage, }, }, nginx: { enabled: true, name: "nginx", replicaCount: services.nginx.replicas, resources: { requests: { cpu: services.nginx.cpu, memory: services.nginx.memory, }, limits: { cpu: this.calculateLimitFromRequest(services.nginx.cpu), memory: this.calculateLimitFromRequest(services.nginx.memory), }, }, hpa: { enabled: false, // Explicitly disable HPA }, storage: { size: services.nginx.storage, }, }, redis: { enabled: true, name: "redis", replicaCount: services.redis.replicas, resources: { requests: { cpu: services.redis.cpu, memory: services.redis.memory, }, limits: { cpu: this.calculateLimitFromRequest(services.redis.cpu), memory: this.calculateLimitFromRequest(services.redis.memory), }, }, storage: { size: services.redis.storage, }, }, // Tier-specific labels labels: { "iaas.deployment/tier": tierConfig.planTier, "iaas.deployment/company": companyName, "iaas.deployment/type": "shared", ...(options.customLabels || {}), }, // Kubecost configuration kubecost: { enabled: options.kubecostEnabled || false, labels: { tier: tierConfig.planTier, company: companyName, billingAccount: options.billingAccountId || "", }, }, // Network policies for tier isolation networkPolicy: { enabled: tierDef.networkPoliciesEnabled, policyTypes: ["Ingress", "Egress"], }, }; return helmValues; } /** * Generate Kubernetes ResourceQuota for tier */ generateResourceQuota(tierConfig, namespace) { const quota = tierConfig.resourceAllocation.quota; return { apiVersion: "v1", kind: "ResourceQuota", metadata: { name: `${namespace}-quota`, namespace: namespace, labels: { "iaas.deployment/tier": tierConfig.planTier, "iaas.deployment/managed-by": "pulumi", }, }, spec: { hard: { "requests.cpu": quota.requests.cpu, "requests.memory": quota.requests.memory, "requests.storage": quota.requests.storage, "limits.cpu": quota.limits.cpu, "limits.memory": quota.limits.memory, pods: quota.pods.toString(), services: quota.services.toString(), secrets: quota.secrets.toString(), configmaps: quota.configmaps.toString(), persistentvolumeclaims: quota.persistentvolumeclaims.toString(), }, }, }; } /** * Generate Kubernetes LimitRange for tier */ generateLimitRange(tierConfig, namespace) { return { apiVersion: "v1", kind: "LimitRange", metadata: { name: `${namespace}-limits`, namespace: namespace, labels: { "iaas.deployment/tier": tierConfig.planTier, "iaas.deployment/managed-by": "pulumi", }, }, spec: { limits: [ { type: "Container", default: { cpu: "500m", memory: "512Mi", }, defaultRequest: { cpu: "100m", memory: "128Mi", }, max: { cpu: "4000m", memory: "8Gi", }, min: { cpu: "50m", memory: "64Mi", }, }, { type: "PersistentVolumeClaim", max: { storage: "100Gi", }, min: { storage: "1Gi", }, }, ], }, }; } /** * Calculate total resources required for a tier */ calculateTotalTierResources(tierDef) { let totalCpu = 0; let totalMemory = 0; let totalStorage = 0; Object.values(tierDef.services).forEach((service) => { const cpuValue = this.parseResourceValue(service.cpu, "cpu"); const memoryValue = this.parseResourceValue(service.memory, "memory"); const storageValue = this.parseResourceValue(service.storage, "storage"); totalCpu += cpuValue * service.replicas; totalMemory += memoryValue * service.replicas; totalStorage += storageValue * service.replicas; }); return { cpu: totalCpu, memory: totalMemory, storage: totalStorage, }; } /** * Parse resource value string to number */ parseResourceValue(value, type) { if (type === "cpu") { // Convert CPU value to millicores if (value.endsWith("m")) { return parseInt(value.slice(0, -1)); } return parseInt(value) * 1000; } if (type === "memory" || type === "storage") { // Convert memory/storage to Mi (mebibytes) or Gi (gibibytes) if (value.endsWith("Mi")) { return parseInt(value.slice(0, -2)); } if (value.endsWith("Gi")) { return parseInt(value.slice(0, -2)) * 1024; } if (value.endsWith("Ti")) { return parseInt(value.slice(0, -2)) * 1024 * 1024; } // Assume bytes, convert to Mi return parseInt(value) / (1024 * 1024); } return parseInt(value); } /** * Calculate limit from request (typically 1.5-2x request) */ calculateLimitFromRequest(request) { if (request.endsWith("m")) { const value = parseInt(request.slice(0, -1)); return `${Math.ceil(value * 1.5)}m`; } if (request.endsWith("Mi")) { const value = parseInt(request.slice(0, -2)); return `${Math.ceil(value * 1.5)}Mi`; } if (request.endsWith("Gi")) { const value = parseInt(request.slice(0, -2)); return `${Math.ceil(value * 1.5)}Gi`; } return request; } /** * Add tier-specific warnings */ addTierWarnings(result) { const tierDef = result.tierConfig.resourceAllocation; if (tierDef.tier === PlanTier.BASIC) { result.warnings.push("Basic tier provides minimal resources. Consider upgrading for production workloads."); } if (!tierDef.networkPoliciesEnabled) { result.warnings.push("Network policies are disabled for this tier. Consider upgrading for enhanced security."); } if (tierDef.monthlyPriceUSD > 1000) { result.warnings.push("This is a high-cost tier. Please ensure resources are utilized efficiently."); } } /** * Calculate resources for a given tier */ calculateResources(planTier) { const tierConfig = getTierConfiguration(planTier); return tierConfig.resourceAllocation; } /** * Validate tier resources */ validateTierResources(planTier, resources) { const validationErrors = validateTierConfiguration({ planTier, resourceAllocation: resources, }); return { isValid: validationErrors.length === 0, errors: validationErrors, warnings: [], }; } /** * Calculate monthly cost for a tier */ calculateMonthlyCost(planTier) { const tierDef = PLAN_TIER_DEFINITIONS[planTier]; return tierDef.monthlyPriceUSD; } /** * Make generateHelmValues public for CLI usage */ generateHelmValuesForTier(planTier, companyName, namespace, options = {}) { const tierConfig = getTierConfiguration(planTier); return this.generateHelmValues(tierConfig, companyName, namespace, options); } /** * Get tier budget for monitoring dashboards */ getTierBudget(tier) { const tierDef = PLAN_TIER_DEFINITIONS[tier]; if (!tierDef) { this.logger.warn(`Unknown tier: ${tier}, defaulting to basic tier budget`); return PLAN_TIER_DEFINITIONS[PlanTier.BASIC].monthlyPriceUSD; } return tierDef.monthlyPriceUSD; } /** * Get tier CPU limits for monitoring dashboards */ getTierCpuLimits(tier) { const tierDef = PLAN_TIER_DEFINITIONS[tier]; if (!tierDef) { this.logger.warn(`Unknown tier: ${tier}, defaulting to basic tier CPU limits`); return PLAN_TIER_DEFINITIONS[PlanTier.BASIC].quota.limits.cpu; } return tierDef.quota.limits.cpu; } /** * Get tier memory limits for monitoring dashboards */ getTierMemoryLimits(tier) { const tierDef = PLAN_TIER_DEFINITIONS[tier]; if (!tierDef) { this.logger.warn(`Unknown tier: ${tier}, defaulting to basic tier memory limits`); return PLAN_TIER_DEFINITIONS[PlanTier.BASIC].quota.limits.memory; } return tierDef.quota.limits.memory; } /** * Get tier resource quota values */ getTierResourceQuota(tier, resource, type) { const tierDef = PLAN_TIER_DEFINITIONS[tier]; if (!tierDef) { this.logger.warn(`Unknown tier: ${tier}, defaulting to basic tier`); const basicTier = PLAN_TIER_DEFINITIONS[PlanTier.BASIC]; return this.getResourceQuotaValue(basicTier, resource, type); } return this.getResourceQuotaValue(tierDef, resource, type); } /** * Helper method to extract resource quota values */ getResourceQuotaValue(tierDef, resource, type) { switch (type) { case "requests": switch (resource) { case "cpu": return tierDef.quota.requests.cpu; case "memory": return tierDef.quota.requests.memory; case "storage": return tierDef.quota.requests.storage; default: this.logger.warn(`Unknown resource for requests: ${resource}`); return "0"; } case "limits": switch (resource) { case "cpu": return tierDef.quota.limits.cpu; case "memory": return tierDef.quota.limits.memory; case "storage": return tierDef.quota.limits.storage; default: this.logger.warn(`Unknown resource for limits: ${resource}`); return "0"; } case "claims": if (resource === "storage") { return tierDef.maxPersistentVolumeClaims.toString(); } this.logger.warn(`Claims type only supports storage resource, got: ${resource}`); return "0"; default: this.logger.warn(`Unknown quota type: ${type}`); return "0"; } } } /** * Helper function to create tier calculator instance */ export function createTierCalculator(logger) { return new TierCalculator(logger); } /** * Quick tier allocation for shared deployments */ export function quickTierAllocation(planTier, companyName, namespace, logger) { const calculator = createTierCalculator(logger); return calculator.calculateTierAllocation(planTier, companyName, namespace); } //# sourceMappingURL=tier-calculator.js.map