UNPKG

@controlplane/cli

Version:

Control Plane Corporation CLI

207 lines 9.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.K8sMapper = void 0; const types_1 = require("../types"); const hpa_1 = require("./handlers/hpa"); class K8sMapper { constructor(overrideProtocol) { this.overrideProtocol = overrideProtocol; this.k8sWorkloadNameToHpa = {}; this.workloadToSecretNames = new Map(); this.pvcToWorkload = new Map(); this.pvNameToObject = new Map(); this.storageClassNameToObject = new Map(); this.serviceAccountNameToObject = new Map(); this.configMapNameToObject = new Map(); this.imagePullSecrets = []; this.warnings = []; this.kindToK8sObject = {}; this.kindToEquivalent = new Map(); // Fill kind to equivalent this.kindToEquivalent.set('ConfigMap', 'Secret'); this.kindToEquivalent.set('ReplicationController', 'Deployment'); this.kindToEquivalent.set('ReplicaSet', 'Deployment'); this.kindToEquivalent.set('StatefulSet', 'Deployment'); this.kindToEquivalent.set('Job', 'Deployment'); this.kindToEquivalent.set('CronJob', 'Deployment'); this.kindToEquivalent.set('DaemonSet', 'Deployment'); } /*** Public Methods ***/ initialize(objects) { // Map objects to their respective data structures for (const object of objects) { // Ensure that no object of the same kind has the same name this.ensureUniqueNames(object); // Assume this object is being mapped until proven otherwise let isMapped = true; // Start mapping objects to their respective data structures switch (object.resource.kind) { case 'StatefulSet': this.handleK8sStatefulWorkload(object.resource); break; case 'StatefulSetList': const k8sWorkloadList = object.resource; for (const k8sWorkload of k8sWorkloadList.items) { this.handleK8sStatefulWorkload(k8sWorkload); } break; case 'ConfigMap': const configMap = object.resource; this.configMapNameToObject.set(configMap.metadata.name, configMap); break; case 'ConfigMapList': const configMapList = object.resource; for (const configMap of configMapList.items) { this.configMapNameToObject.set(configMap.metadata.name, configMap); } break; case 'PersistentVolumeClaim': this.pvcToWorkload.set(object.resource.metadata.name, ''); break; case 'PersistentVolumeClaimList': const pvcList = object.resource; for (const pvc of pvcList.items) { this.pvcToWorkload.set(pvc.metadata.name, ''); } break; case 'PersistentVolume': const pv = object.resource; this.pvNameToObject.set(pv.metadata.name, pv); break; case 'PersistentVolumeList': const pvList = object.resource; for (const pv of pvList.items) { this.pvNameToObject.set(pv.metadata.name, pv); } break; case 'StorageClass': const storageClass = object.resource; this.storageClassNameToObject.set(storageClass.metadata.name, storageClass); break; case 'StorageClassList': const storageClassList = object.resource; for (const storageClass of storageClassList.items) { this.storageClassNameToObject.set(storageClass.metadata.name, storageClass); } break; case 'HorizontalPodAutoscaler': this.handleHpa(object.filePath, object.resource); break; case 'HorizontalPodAutoscalerList': const hpaList = object.resource; for (const hpa of hpaList.items) { this.handleHpa(object.filePath, hpa); } break; case 'ServiceAccount': const serviceAccount = object.resource; this.serviceAccountNameToObject.set(serviceAccount.metadata.name, serviceAccount); break; case 'ServiceAccountList': const serviceAccountList = object.resource; for (const serviceAccount of serviceAccountList.items) { this.serviceAccountNameToObject.set(serviceAccount.metadata.name, serviceAccount); } break; default: isMapped = false; break; } // Don't worry about this, this is put in place so we can avoid developer error; // It helps sync between the kinds being mapped and the mappable kinds list if (isMapped && !types_1.K8sMappableKinds.includes(object.resource.kind)) { // As I said, don't worry about it; This exception will never fire on production because we will catch it in test throw new Error(`DEVELOPER ERROR: K8s Kind: '${object.resource.kind}' needs to be added to the K8sMappableKinds array.`); } } } mapSecretToWorkload(secretName, workload) { if (!this.workloadToSecretNames.has(workload)) { this.workloadToSecretNames.set(workload, new Set([secretName])); return; } // Get secret names that this workload is referring to const secretNames = this.workloadToSecretNames.get(workload); // Don't add the same secret twice to the map if (secretNames.has(secretName)) { return; } // Map secret to workload secretNames.add(secretName); } /*** Private Methods ***/ ensureUniqueNames(object) { let name = object.resource.metadata.name; let kind = object.resource.kind; let kindToReplace; let replacedWithKind; // Handle lists if (object.resource.hasOwnProperty('items')) { const list = object.resource; for (const item of list.items) { this.ensureUniqueNames({ filePath: object.filePath, resource: item }); } return; } // Process persistent volume claims of a K8s stateful workload if (object.resource.kind == 'StatefulSet') { const k8sWorkload = object.resource; // Ensure that each persistent volume claim name is not creating a conflict if (k8sWorkload.hasOwnProperty('spec') && k8sWorkload.spec.volumeClaimTemplates) { for (const pvc of k8sWorkload.spec.volumeClaimTemplates) { this.ensureUniqueNames({ resource: pvc, filePath: object.filePath }); } } } // Replace kinds with their equivalent if (this.kindToEquivalent.has(kind)) { kindToReplace = kind; replacedWithKind = this.kindToEquivalent.get(kind); kind = replacedWithKind; } // Register kind as a key if (!this.kindToK8sObject[kind]) { this.kindToK8sObject[kind] = {}; } // Check if the name already exists for this kind, if so, throw an error accordingly if (this.kindToK8sObject[kind][name]) { const existingObject = this.kindToK8sObject[kind][name]; let fileMessage = object.filePath == existingObject.filePath ? `File path: ${object.filePath}` : `File paths: '${object.filePath}' and '${existingObject.filePath}'`; let message = `ERROR: Duplicate name '${name}' found for kind '${kind}'. ${fileMessage}.`; // The following cases might occur: // - ConfigMap == ConfigMap || Secret != Secret => A Secret already registered this name, so a ConfigMap cannot // - Secret == Undefined || ConfigMap != Secret => A ConfigMap already registered this name, so a Secret cannot if (existingObject.resource.kind == kindToReplace || existingObject.resource.kind != kind) { message = `Error: A '${existingObject.resource.kind}' cannot share the same name as a '${kind}'. Name '${name}', ${fileMessage}.`; } throw new Error(message); } // Reserve this name for this kind this.kindToK8sObject[kind][name] = object; } handleK8sStatefulWorkload(object) { const k8sWorkload = object; if (!k8sWorkload.hasOwnProperty('spec') || !k8sWorkload.spec.volumeClaimTemplates) { return; } // Map persistent volume claim name to workload name for (const pvc of k8sWorkload.spec.volumeClaimTemplates) { this.pvcToWorkload.set(pvc.metadata.name, k8sWorkload.metadata.name); } } handleHpa(filePath, hpa) { // Handle Horizontal Pod Autoscaler const hpaHandler = new hpa_1.K8sHorizontalPodAutoscalerHandler(filePath, hpa); hpaHandler.handle(); // Only handle auto-scalable kinds if (!types_1.K8sAutoscalableKinds.includes(hpa.spec.scaleTargetRef.kind)) { return; } // Map the Horizontal Pod Autoscaler to workload this.k8sWorkloadNameToHpa[hpa.spec.scaleTargetRef.name] = hpa; } } exports.K8sMapper = K8sMapper; //# sourceMappingURL=mapper.js.map