UNPKG

@hashgraph/solo

Version:

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

159 lines (141 loc) 5.75 kB
/** * SPDX-License-Identifier: Apache-2.0 */ import * as constants from './constants.js'; import {type Helm} from './helm.js'; import chalk from 'chalk'; import {SoloError} from './errors.js'; import {type SoloLogger} from './logging.js'; import {inject, injectable} from 'tsyringe-neo'; import {patchInject} from './dependency_injection/container_helper.js'; import {type NamespaceName} from './kube/resources/namespace/namespace_name.js'; import {InjectTokens} from './dependency_injection/inject_tokens.js'; @injectable() export class ChartManager { constructor( @inject(InjectTokens.Helm) private readonly helm?: Helm, @inject(InjectTokens.SoloLogger) private readonly logger?: SoloLogger, ) { this.helm = patchInject(helm, InjectTokens.Helm, this.constructor.name); this.logger = patchInject(logger, InjectTokens.SoloLogger, this.constructor.name); } /** * Setup chart repositories * * This must be invoked before calling other methods * * @param repoURLs - a map of name and chart repository URLs * @param force - whether or not to update the repo * @returns the urls */ async setup(repoURLs: Map<string, string> = constants.DEFAULT_CHART_REPO, force = true) { try { const forceUpdateArg = force ? '--force-update' : ''; const promises: Promise<string>[] = []; for (const [name, url] of repoURLs.entries()) { promises.push(this.addRepo(name, url, forceUpdateArg)); } return await Promise.all(promises); // urls } catch (e: Error | any) { throw new SoloError(`failed to setup chart repositories: ${e.message}`, e); } } async addRepo(name: string, url: string, forceUpdateArg: string) { this.logger.debug(`Adding repo ${name} -> ${url}`, {repoName: name, repoURL: url}); await this.helm.repo('add', name, url, forceUpdateArg); return url; } /** List available clusters * * @param namespaceName - the namespace name * @param kubeContext - the kube context */ async getInstalledCharts(namespaceName: NamespaceName, kubeContext?: string) { const namespaceArg = namespaceName ? `-n ${namespaceName.name}` : '--all-namespaces'; const contextArg = kubeContext ? `--kube-context ${kubeContext}` : ''; try { return await this.helm.list(` ${contextArg} ${namespaceArg} --no-headers | awk '{print $1 " [" $9"]"}'`); } catch (e: Error | any) { this.logger.showUserError(e); } return []; } async install( namespaceName: NamespaceName, chartReleaseName: string, chartPath: string, version: string, valuesArg = '', kubeContext: string, ) { try { const isInstalled = await this.isChartInstalled(namespaceName, chartReleaseName, kubeContext); if (!isInstalled) { const versionArg = version ? `--version ${version}` : ''; const namespaceArg = namespaceName ? `-n ${namespaceName} --create-namespace` : ''; let contextArg = ''; if (kubeContext) { contextArg = `--kube-context ${kubeContext}`; } this.logger.debug(`> installing chart:${chartPath}`); await this.helm.install( `${chartReleaseName} ${chartPath} ${versionArg} ${namespaceArg} ${valuesArg} ${contextArg}`, ); this.logger.debug(`OK: chart is installed: ${chartReleaseName} (${chartPath})`); } else { this.logger.debug(`OK: chart is already installed:${chartReleaseName} (${chartPath})`); } } catch (e: Error | any) { throw new SoloError(`failed to install chart ${chartReleaseName}: ${e.message}`, e); } return true; } async isChartInstalled(namespaceName: NamespaceName, chartReleaseName: string, kubeContext?: string) { this.logger.debug(`> checking if chart is installed [ chart: ${chartReleaseName}, namespace: ${namespaceName} ]`); const charts = await this.getInstalledCharts(namespaceName, kubeContext); return charts.some(item => item.startsWith(chartReleaseName)); } async uninstall(namespaceName: NamespaceName, chartReleaseName: string, kubeContext?: string) { try { const isInstalled = await this.isChartInstalled(namespaceName, chartReleaseName, kubeContext); if (isInstalled) { let contextArg = ''; if (kubeContext) { contextArg = `--kube-context ${kubeContext}`; } this.logger.debug(`uninstalling chart release: ${chartReleaseName}`); await this.helm.uninstall(`-n ${namespaceName} ${chartReleaseName} ${contextArg}`); this.logger.debug(`OK: chart release is uninstalled: ${chartReleaseName}`); } else { this.logger.debug(`OK: chart release is already uninstalled: ${chartReleaseName}`); } } catch (e: Error | any) { throw new SoloError(`failed to uninstall chart ${chartReleaseName}: ${e.message}`, e); } return true; } async upgrade( namespaceName: NamespaceName, chartReleaseName: string, chartPath: string, version = '', valuesArg = '', kubeContext?: string, ) { const versionArg = version ? `--version ${version}` : ''; try { this.logger.debug(chalk.cyan('> upgrading chart:'), chalk.yellow(`${chartReleaseName}`)); let contextArg = ''; if (kubeContext) { contextArg = `--kube-context ${kubeContext}`; } await this.helm.upgrade( `-n ${namespaceName.name} ${chartReleaseName} ${chartPath} ${versionArg} --reuse-values ${valuesArg} ${contextArg}`, ); this.logger.debug(chalk.green('OK'), `chart '${chartReleaseName}' is upgraded`); } catch (e: Error | any) { throw new SoloError(`failed to upgrade chart ${chartReleaseName}: ${e.message}`, e); } return true; } }