@hashgraph/solo
Version:
An opinionated CLI tool to deploy and manage private Hedera Networks.
101 lines (92 loc) • 4.08 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 {PackageDownloader} from '../package-downloader.js';
import {format} from 'node:util';
import {SoloError} from '../errors/solo-error.js';
import {OperatingSystem} from '../../business/utils/operating-system.js';
const KUBECTL_RELEASE_BASE_URL: string = 'https://dl.k8s.io/release';
const KUBECTL_ARTIFACT_TEMPLATE: string = '%s/bin/%s/%s/kubectl';
const KUBECTL_WINDOWS_ARTIFACT_TEMPLATE: string = '%s/bin/%s/%s/kubectl.exe';
()
export class KubectlDependencyManager extends BaseDependencyManager {
public constructor(
(InjectTokens.PackageDownloader) downloader: PackageDownloader,
(InjectTokens.KubectlInstallationDirectory) installationDirectory: string,
(InjectTokens.OsArch) osArch: string,
(InjectTokens.KubectlVersion) kubectlVersion: string,
) {
// Call the base constructor with the Kubectl-specific parameters
super(
patchInject(downloader, InjectTokens.PackageDownloader, KubectlDependencyManager.name),
patchInject(installationDirectory, InjectTokens.KubectlInstallationDirectory, KubectlDependencyManager.name),
patchInject(osArch, InjectTokens.OsArch, KubectlDependencyManager.name),
patchInject(kubectlVersion, InjectTokens.KubectlVersion, KubectlDependencyManager.name) ||
version.KUBECTL_VERSION,
constants.KUBECTL,
KUBECTL_RELEASE_BASE_URL,
);
}
/**
* Get the Kubectl artifact name based on version, OS, and architecture
*/
protected getArtifactName(): string {
return format(
OperatingSystem.isWin32() ? KUBECTL_WINDOWS_ARTIFACT_TEMPLATE : KUBECTL_ARTIFACT_TEMPLATE,
this.getRequiredVersion(),
OperatingSystem.getFormattedPlatform(),
this.osArch,
);
}
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.
// Using the null device ensures kubectl only reports the client version without any
// server or credential-related operations.
const nullDevice: string = OperatingSystem.isWin32() ? 'nul' : '/dev/null';
const output: string[] = await this.run(
`"${executableWithPath}" version --client`,
[],
false,
false,
{KUBECONFIG: nullDevice},
30_000,
);
this.logger.debug(`Raw kubectl version output: ${output.join('\n')}`);
if (output.length > 0) {
for (const line of output) {
if (line.trim().startsWith('Client Version')) {
const match: RegExpMatchArray | null = line.trim().match(/(\d+\.\d+\.\d+)/);
if (match) {
const detectedVersion: string = match[1];
this.logger.info(`Kubectl version: ${detectedVersion}`);
return detectedVersion;
}
}
}
}
} catch (error) {
throw new SoloError('Failed to check kubectl version', error);
}
throw new SoloError('Failed to get kubectl version');
}
protected getDownloadURL(): string {
return `${this.downloadBaseUrl}/${this.artifactName}`;
}
/**
* Handle any post-download processing before copying to destination
* Child classes can override this for custom extraction or processing
*/
protected async processDownloadedPackage(packageFilePath: string): Promise<string[]> {
// For kubectl, the downloaded file is the executable itself, so we can return it directly
return [packageFilePath];
}
protected getChecksumURL(): string {
return `${this.downloadURL}.sha256`;
}
}