@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
141 lines (121 loc) • 5.19 kB
text/typescript
/**
* SPDX-License-Identifier: Apache-2.0
*/
import fs from 'fs';
import path from 'path';
import * as util from 'util';
import {MissingArgumentError} from '../errors.js';
import * as helpers from '../helpers.js';
import * as constants from '../constants.js';
import {type PackageDownloader} from '../package_downloader.js';
import {type Zippy} from '../zippy.js';
import {Templates} from '../templates.js';
import * as version from '../../../version.js';
import {ShellRunner} from '../shell_runner.js';
import * as semver from 'semver';
import {OS_WIN32, OS_WINDOWS} from '../constants.js';
import {inject, injectable} from 'tsyringe-neo';
import {patchInject} from '../dependency_injection/container_helper.js';
import {InjectTokens} from '../dependency_injection/inject_tokens.js';
// constants required by HelmDependencyManager
const HELM_RELEASE_BASE_URL = 'https://get.helm.sh';
const HELM_ARTIFACT_TEMPLATE = 'helm-%s-%s-%s.%s';
const HELM_ARTIFACT_EXT: Map<string, string> = new Map()
.set(constants.OS_DARWIN, 'tar.gz')
.set(constants.OS_LINUX, 'tar.gz')
.set(constants.OS_WINDOWS, 'zip');
/**
* Helm dependency manager installs or uninstalls helm client at SOLO_HOME_DIR/bin directory
*/
export class HelmDependencyManager extends ShellRunner {
private readonly osPlatform: string;
private readonly osArch: string;
private helmPath: string;
private readonly artifactName: string;
private readonly helmURL: string;
private readonly checksumURL: string;
constructor(
private readonly downloader?: PackageDownloader,
private readonly zippy?: Zippy,
private readonly installationDir?: string,
osPlatform?: NodeJS.Platform,
osArch?: string,
private readonly helmVersion?: string,
) {
super();
this.installationDir = patchInject(installationDir, InjectTokens.HelmInstallationDir, this.constructor.name);
this.osPlatform = patchInject(osPlatform, InjectTokens.OsPlatform, this.constructor.name);
this.osArch = patchInject(osArch, InjectTokens.OsArch, this.constructor.name);
this.helmVersion = patchInject(helmVersion, InjectTokens.HelmVersion, this.constructor.name);
if (!installationDir) throw new MissingArgumentError('installation directory is required');
this.downloader = patchInject(downloader, InjectTokens.PackageDownloader, this.constructor.name);
this.zippy = patchInject(zippy, InjectTokens.Zippy, this.constructor.name);
this.installationDir = installationDir;
// Node.js uses 'win32' for windows in package.json os field, but helm uses 'windows'
if (osPlatform === OS_WIN32) {
this.osPlatform = OS_WINDOWS;
} else {
this.osPlatform = osPlatform;
}
this.osArch = ['x64', 'x86-64'].includes(osArch) ? 'amd64' : osArch;
this.helmPath = Templates.installationPath(constants.HELM, this.osPlatform, this.installationDir);
const fileExt = HELM_ARTIFACT_EXT.get(this.osPlatform);
this.artifactName = util.format(HELM_ARTIFACT_TEMPLATE, this.helmVersion, this.osPlatform, this.osArch, fileExt);
this.helmURL = `${HELM_RELEASE_BASE_URL}/${this.artifactName}`;
this.checksumURL = `${HELM_RELEASE_BASE_URL}/${this.artifactName}.sha256sum`;
}
getHelmPath() {
return this.helmPath;
}
isInstalled() {
return fs.existsSync(this.helmPath);
}
/**
* Uninstall helm from solo bin folder
*/
uninstall() {
if (this.isInstalled()) {
fs.rmSync(this.helmPath);
}
}
async install(tmpDir: string = helpers.getTmpDir()) {
const extractedDir = path.join(tmpDir, 'extracted-helm');
let helmSrc = path.join(extractedDir, `${this.osPlatform}-${this.osArch}`, constants.HELM);
const packageFile = await this.downloader.fetchPackage(this.helmURL, this.checksumURL, tmpDir);
if (this.osPlatform === constants.OS_WINDOWS) {
this.zippy.unzip(packageFile, extractedDir);
// append .exe for windows
helmSrc = path.join(extractedDir, `${this.osPlatform}-${this.osArch}`, `${constants.HELM}.exe`);
} else {
this.zippy.untar(packageFile, extractedDir);
}
if (!fs.existsSync(this.installationDir)) {
fs.mkdirSync(this.installationDir);
}
// install new helm
this.uninstall();
this.helmPath = Templates.installationPath(constants.HELM, this.osPlatform, this.installationDir);
fs.cpSync(helmSrc, this.helmPath);
if (fs.existsSync(extractedDir)) {
fs.rmSync(extractedDir, {recursive: true});
}
return this.isInstalled();
}
async checkVersion(shouldInstall = true) {
if (!this.isInstalled()) {
if (shouldInstall) {
await this.install();
} else {
return false;
}
}
const output = await this.run(`${this.helmPath} version --short`);
const parts = output[0].split('+');
this.logger.debug(`Found ${constants.HELM}:${parts[0]}`);
return semver.gte(parts[0], version.HELM_VERSION);
}
getHelmVersion() {
return version.HELM_VERSION;
}
}