UNPKG

@hashgraph/solo

Version:

An opinionated CLI tool to deploy and manage private Hedera Networks.

176 lines (159 loc) 6.8 kB
/** * SPDX-License-Identifier: Apache-2.0 */ import * as constants from '../../constants.js'; import {SoloError} from '../../errors.js'; import {ConsensusNodeStates} from './enumerations.js'; import {type K8Factory} from '../../kube/k8_factory.js'; import {type ComponentsDataWrapper} from './components_data_wrapper.js'; import {type BaseComponent} from './components/base_component.js'; import {type NamespaceName} from '../../kube/resources/namespace/namespace_name.js'; import {type V1Pod} from '@kubernetes/client-node'; import {type LocalConfig} from '../local_config.js'; /** * Static class is used to validate that components in the remote config * are present in the kubernetes cluster, and throw errors if there is mismatch. */ export class RemoteConfigValidator { /** * Gathers and handles validation of all components. * * @param namespace - namespace to validate the components in. * @param components - components to validate. * @param k8Factory - to validate the elements. * @param localConfig - to get the context from cluster */ public static async validateComponents( namespace: NamespaceName, components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, ): Promise<void> { await Promise.all([ ...RemoteConfigValidator.validateRelays(namespace, components, k8Factory, localConfig), ...RemoteConfigValidator.validateHaProxies(namespace, components, k8Factory, localConfig), ...RemoteConfigValidator.validateMirrorNodes(namespace, components, k8Factory, localConfig), ...RemoteConfigValidator.validateEnvoyProxies(namespace, components, k8Factory, localConfig), ...RemoteConfigValidator.validateConsensusNodes(namespace, components, k8Factory, localConfig), ...RemoteConfigValidator.validateMirrorNodeExplorers(namespace, components, k8Factory, localConfig), ]); } private static validateRelays( namespace: NamespaceName, components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, ): Promise<void>[] { return Object.values(components.relays).map(async component => { const context = localConfig.clusterRefs[component.cluster]; const labels = [constants.SOLO_RELAY_LABEL]; try { const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); if (!pods.length) throw new Error('Pod not found'); // to return the generic error message } catch (e) { RemoteConfigValidator.throwValidationError('Relay', component, e); } }); } private static validateHaProxies( namespace: NamespaceName, components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, ): Promise<void>[] { return Object.values(components.haProxies).map(async component => { const context = localConfig.clusterRefs[component.cluster]; const labels = [`app=${component.name}`]; try { const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); if (!pods.length) throw new Error('Pod not found'); // to return the generic error message } catch (e) { RemoteConfigValidator.throwValidationError('HaProxy', component, e); } }); } private static validateMirrorNodes( namespace: NamespaceName, components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, ): Promise<void>[] { return Object.values(components.mirrorNodes).map(async component => { const context = localConfig.clusterRefs[component.cluster]; const labels = constants.SOLO_HEDERA_MIRROR_IMPORTER; try { const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); if (!pods.length) throw new Error('Pod not found'); // to return the generic error message } catch (e) { RemoteConfigValidator.throwValidationError('Mirror node', component, e); } }); } private static validateEnvoyProxies( namespace: NamespaceName, components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, ): Promise<void>[] { return Object.values(components.envoyProxies).map(async component => { const context = localConfig.clusterRefs[component.cluster]; const labels = [`app=${component.name}`]; try { const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); if (!pods.length) throw new Error('Pod not found'); // to return the generic error message } catch (e) { RemoteConfigValidator.throwValidationError('Envoy proxy', component, e); } }); } private static validateConsensusNodes( namespace: NamespaceName, components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, ): Promise<void>[] { return Object.values(components.consensusNodes).map(async component => { if (component.state === ConsensusNodeStates.REQUESTED) return; const context = localConfig.clusterRefs[component.cluster]; const labels = [`app=network-${component.name}`]; try { const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); if (!pods.length) throw new Error('Pod not found'); // to return the generic error message } catch (e) { RemoteConfigValidator.throwValidationError('Consensus node', component, e); } }); } private static validateMirrorNodeExplorers( namespace: NamespaceName, components: ComponentsDataWrapper, k8Factory: K8Factory, localConfig: LocalConfig, ): Promise<void>[] { return Object.values(components.mirrorNodeExplorers).map(async component => { const context = localConfig.clusterRefs[component.cluster]; const labels = [constants.SOLO_HEDERA_EXPLORER_LABEL]; try { const pods: V1Pod[] = await k8Factory.getK8(context).pods().list(namespace, labels); if (!pods.length) throw new Error('Pod not found'); // to return the generic error message } catch (e) { RemoteConfigValidator.throwValidationError('Mirror node explorer', component, e); } }); } /** * Generic handler that throws errors. * * @param type - name to display in error message * @param component - component which is not found in the cluster * @param e - original error for the kube client */ private static throwValidationError(type: string, component: BaseComponent, e: Error | unknown): never { throw new SoloError( `${type} in remote config with name ${component.name} ` + `was not found in namespace: ${component.namespace}, cluster: ${component.cluster}`, e, {component: component.toObject()}, ); } }