@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
128 lines (111 loc) • 4.68 kB
text/typescript
// SPDX-License-Identifier: Apache-2.0
import * as constants from '../constants.js';
import * as version from '../../../version.js';
import {inject, injectable} from 'tsyringe-neo';
import {patchInject} from '../dependency-injection/container-helper.js';
import {InjectTokens} from '../dependency-injection/inject-tokens.js';
import {BaseDependencyManager} from './base-dependency-manager.js';
import {Zippy} from '../zippy.js';
import {PathEx} from '../../business/utils/path-ex.js';
import {PackageDownloader} from '../package-downloader.js';
import util from 'node:util';
import fs from 'node:fs';
import {SoloError} from '../errors/solo-error.js';
import {OperatingSystem} from '../../business/utils/operating-system.js';
const HELM_RELEASE_BASE_URL: string = 'https://get.helm.sh';
const HELM_ARTIFACT_TEMPLATE: string = 'helm-%s-%s-%s.%s';
/**
* Helm dependency manager installs or uninstalls helm client at SOLO_HOME_DIR/bin directory
*/
()
export class HelmDependencyManager extends BaseDependencyManager {
public constructor(
(InjectTokens.PackageDownloader) downloader: PackageDownloader,
(InjectTokens.Zippy) private readonly zippy: Zippy,
(InjectTokens.HelmInstallationDirectory) installationDirectory: string,
(InjectTokens.OsArch) osArch: string,
(InjectTokens.HelmVersion) helmVersion: string,
) {
super(
patchInject(downloader, InjectTokens.PackageDownloader, HelmDependencyManager.name),
patchInject(installationDirectory, InjectTokens.HelmInstallationDirectory, HelmDependencyManager.name),
patchInject(osArch, InjectTokens.OsArch, HelmDependencyManager.name),
patchInject(helmVersion, InjectTokens.HelmVersion, HelmDependencyManager.name) || version.HELM_VERSION,
constants.HELM,
HELM_RELEASE_BASE_URL,
);
// Patch injected values to handle undefined values
this.zippy = patchInject(this.zippy, InjectTokens.Zippy, HelmDependencyManager.name);
}
/**
* Get the Helm artifact name based on version, OS, and architecture
*/
protected getArtifactName(): string {
const fileExtension: string = OperatingSystem.isWin32() ? 'zip' : 'tar.gz';
return util.format(
HELM_ARTIFACT_TEMPLATE,
this.getRequiredVersion(),
OperatingSystem.getFormattedPlatform(),
this.osArch,
fileExtension,
);
}
/**
* Process the downloaded Helm package by extracting it and finding the executable
*/
protected async processDownloadedPackage(packageFilePath: string, temporaryDirectory: string): Promise<string[]> {
// Extract the archive
if (OperatingSystem.isWin32()) {
this.zippy!.unzip(packageFilePath, temporaryDirectory);
} else {
this.zippy!.untar(packageFilePath, temporaryDirectory);
}
// Find the Helm executable inside the extracted directory
const helmExecutablePath: string = PathEx.join(
temporaryDirectory,
`${OperatingSystem.getFormattedPlatform()}-${this.osArch}`,
this.executableName,
);
// Ensure the extracted file exists
if (!fs.existsSync(helmExecutablePath)) {
const executablePath: string = PathEx.join(temporaryDirectory, this.executableName);
if (fs.existsSync(executablePath)) {
fs.rmSync(executablePath);
}
fs.cpSync(helmExecutablePath, executablePath);
if (!fs.existsSync(executablePath)) {
throw new Error(`Helm executable not found in extracted archive: ${executablePath}`);
}
return [executablePath];
}
return [helmExecutablePath];
}
public async getVersion(executableWithPath: string): Promise<string> {
try {
// Override KUBECONFIG to prevent loading kubeconfig and triggering authentication
// plugins (e.g., Teleport exec credentials) which can hang in non-interactive environments.
const nullDevice: string = OperatingSystem.isWin32() ? 'nul' : '/dev/null';
const output: string[] = await this.run(
`"${executableWithPath}" version --short`,
[],
false,
false,
{KUBECONFIG: nullDevice},
30_000,
);
const parts: string[] = output[0].split('+');
const versionOnly: string = parts[0];
this.logger.info(`Helm version: ${versionOnly}`);
this.logger.debug(`Found ${constants.HELM}:${versionOnly}`);
return versionOnly;
} catch (error) {
throw new SoloError('Failed to check helm version', error);
}
}
protected getDownloadURL(): string {
return `${this.downloadBaseUrl}/${this.artifactName}`;
}
protected getChecksumURL(): string {
return `${this.downloadURL}.sha256sum`;
}
}