Skip to content

Commit e64e9a2

Browse files
authored
feat: download helm binary based on os and platform architecture (#128)
Signed-off-by: Lenin Mehedy <[email protected]>
1 parent d890974 commit e64e9a2

21 files changed

+493
-129
lines changed

src/core/constants.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,7 @@ export const KEY_TYPE_GOSSIP = 'gossip'
113113
export const KEY_TYPE_TLS = 'tls'
114114
export const SIGNING_KEY_PREFIX = 's'
115115
export const AGREEMENT_KEY_PREFIX = 'a'
116+
117+
export const OS_WINDOWS = 'windows'
118+
export const OS_DARWIN = 'darwin'
119+
export const OS_LINUX = 'linux'

src/core/dependency_manager.mjs

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright (C) 2024 Hedera Hashgraph, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the ""License"");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an ""AS IS"" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
import os from 'os'
18+
import { FullstackTestingError, MissingArgumentError } from '../errors.mjs'
19+
import { ShellRunner } from '../shell_runner.mjs'
20+
21+
export class DependencyManager extends ShellRunner {
22+
constructor (logger, depManagerMap) {
23+
super(logger)
24+
if (!depManagerMap) throw new MissingArgumentError('A map of dependency managers are required')
25+
this.depManagerMap = depManagerMap
26+
}
27+
28+
/**
29+
* Check if the required dependency is installed or not
30+
* @param dep is the name of the program
31+
* @param shouldInstall Whether or not install the dependency if not installed
32+
* @returns {Promise<boolean>}
33+
*/
34+
async checkDependency (dep, shouldInstall = true) {
35+
this.logger.debug(`Checking for dependency: ${dep}`)
36+
37+
let status = false
38+
const manager = this.depManagerMap.get(dep)
39+
if (manager) {
40+
status = await manager.checkVersion(shouldInstall)
41+
}
42+
43+
if (!status) {
44+
throw new FullstackTestingError(`Dependency '${dep}' is not found`)
45+
}
46+
47+
this.logger.debug(`Dependency '${dep}' is found`)
48+
return true
49+
}
50+
51+
taskCheckDependencies (deps = []) {
52+
const subTasks = []
53+
deps.forEach(dep => {
54+
subTasks.push({
55+
title: `Check dependency: ${dep} [OS: ${os.platform()}, Release: ${os.release()}, Arch: ${os.arch()}]`,
56+
task: () => this.checkDependency(dep)
57+
})
58+
})
59+
60+
return subTasks
61+
}
62+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/**
2+
* Copyright (C) 2024 Hedera Hashgraph, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the ""License"");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an ""AS IS"" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
import fs from 'fs'
18+
import os from 'os'
19+
import path from 'path'
20+
import * as util from 'util'
21+
import { MissingArgumentError } from '../errors.mjs'
22+
import * as helpers from '../helpers.mjs'
23+
import { constants, Templates } from '../index.mjs'
24+
import * as version from '../../../version.mjs'
25+
import { ShellRunner } from '../shell_runner.mjs'
26+
27+
// constants required by HelmDependencyManager
28+
const HELM_RELEASE_BASE_URL = 'https://get.helm.sh'
29+
const HELM_ARTIFACT_TEMPLATE = 'helm-%s-%s-%s.%s'
30+
const HELM_ARTIFACT_EXT = new Map()
31+
.set(constants.OS_DARWIN, 'tar.gz')
32+
.set(constants.OS_LINUX, 'tar.gz')
33+
.set(constants.OS_WINDOWS, 'zip')
34+
35+
/**
36+
* Helm dependency manager installs or uninstalls helm client at SOLO_HOME_DIR/bin directory
37+
*/
38+
export class HelmDependencyManager extends ShellRunner {
39+
constructor (
40+
downloader,
41+
zippy,
42+
logger,
43+
installationDir = path.join(constants.SOLO_HOME_DIR, 'bin'),
44+
osPlatform = os.platform(),
45+
osArch = os.arch(),
46+
helmVersion = version.HELM_VERSION
47+
) {
48+
super(logger)
49+
50+
if (!downloader) throw new MissingArgumentError('An instance of core/PackageDownloader is required')
51+
if (!zippy) throw new MissingArgumentError('An instance of core/Zippy is required')
52+
if (!installationDir) throw new MissingArgumentError('installation directory is required')
53+
54+
this.downloader = downloader
55+
this.zippy = zippy
56+
this.installationDir = installationDir
57+
this.osPlatform = osPlatform
58+
this.osArch = ['x64', 'x86-64'].includes(osArch) ? 'amd64' : osArch
59+
this.helmVersion = helmVersion
60+
this.helmPath = Templates.installationPath(constants.HELM, this.installationDir, this.osPlatform, this.osArch)
61+
62+
const fileExt = HELM_ARTIFACT_EXT.get(this.osPlatform)
63+
this.artifactName = util.format(HELM_ARTIFACT_TEMPLATE, this.helmVersion, this.osPlatform, this.osArch, fileExt)
64+
this.helmURL = `${HELM_RELEASE_BASE_URL}/${this.artifactName}`
65+
}
66+
67+
getHelmPath () {
68+
return this.helmPath
69+
}
70+
71+
isInstalled () {
72+
return fs.existsSync(this.helmPath)
73+
}
74+
75+
/**
76+
* Uninstall helm from solo bin folder
77+
* @return {Promise<void>}
78+
*/
79+
async uninstall () {
80+
if (this.isInstalled()) {
81+
fs.rmSync(this.helmPath)
82+
}
83+
}
84+
85+
async install () {
86+
const tmpDir = helpers.getTmpDir()
87+
const extractedDir = path.join(tmpDir, 'extracted')
88+
const tmpFile = path.join(tmpDir, this.artifactName)
89+
let helmSrc = path.join(extractedDir, `${this.osPlatform}-${this.osArch}`, constants.HELM)
90+
91+
await this.downloader.fetchFile(this.helmURL, tmpFile)
92+
if (this.osPlatform === constants.OS_WINDOWS) {
93+
await this.zippy.unzip(tmpFile, extractedDir)
94+
// append .exe for windows
95+
helmSrc = path.join(extractedDir, `${this.osPlatform}-${this.osArch}`, `${constants.HELM}.exe`)
96+
} else {
97+
await this.zippy.untar(tmpFile, extractedDir)
98+
}
99+
100+
if (!fs.existsSync(this.installationDir)) {
101+
fs.mkdirSync(this.installationDir)
102+
}
103+
104+
// install new helm
105+
await this.uninstall()
106+
this.helmPath = Templates.installationPath(constants.HELM, this.installationDir, this.osPlatform)
107+
fs.cpSync(helmSrc, this.helmPath)
108+
109+
return this.isInstalled()
110+
}
111+
112+
async checkVersion (shouldInstall = true) {
113+
if (!this.isInstalled()) {
114+
if (shouldInstall) {
115+
await this.install()
116+
} else {
117+
return false
118+
}
119+
}
120+
121+
const output = await this.run(`${this.helmPath} version --short`)
122+
const parts = output[0].split('+')
123+
this.logger.debug(`Found ${constants.HELM}:${parts[0]}`)
124+
return helpers.compareVersion(version.HELM_VERSION, parts[0]) >= 0
125+
}
126+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (C) 2024 Hedera Hashgraph, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the ""License"");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an ""AS IS"" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
import { DependencyManager } from './dependency_manager.mjs'
18+
import { HelmDependencyManager } from './helm_dependency_manager.mjs'
19+
20+
export { HelmDependencyManager, DependencyManager }

src/core/helm.mjs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
* limitations under the License.
1515
*
1616
*/
17+
import { constants } from './index.mjs'
1718
import { ShellRunner } from './shell_runner.mjs'
19+
import { Templates } from './templates.mjs'
20+
21+
const helmPath = Templates.installationPath(constants.HELM)
1822

1923
export class Helm extends ShellRunner {
2024
/**
@@ -24,7 +28,7 @@ export class Helm extends ShellRunner {
2428
* @returns {string}
2529
*/
2630
prepareCommand (action, ...args) {
27-
let cmd = `helm ${action}`
31+
let cmd = `${helmPath} ${action}`
2832
args.forEach(arg => { cmd += ` ${arg}` })
2933
return cmd
3034
}
@@ -84,4 +88,12 @@ export class Helm extends ShellRunner {
8488
async repo (subCommand, ...args) {
8589
return this.run(this.prepareCommand('repo', subCommand, ...args))
8690
}
91+
92+
/**
93+
* Get helm version
94+
* @return {Promise<Array>}
95+
*/
96+
async version (args = ['--short']) {
97+
return this.run(this.prepareCommand('version', ...args))
98+
}
8799
}

src/core/helpers.mjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*
1616
*/
1717
import fs from 'fs'
18+
import os from 'os'
19+
import path from 'path'
1820
import { FullstackTestingError } from './errors.mjs'
1921
import * as paths from 'path'
2022
import { fileURLToPath } from 'url'
@@ -132,3 +134,6 @@ export function getRootImageRepository (releaseTag) {
132134

133135
return 'hashgraph/full-stack-testing/ubi8-init-java21'
134136
}
137+
export function getTmpDir () {
138+
return fs.mkdtempSync(path.join(os.tmpdir(), 'solo-'))
139+
}

src/core/index.mjs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import { Zippy } from './zippy.mjs'
2424
import { Templates } from './templates.mjs'
2525
import { ChartManager } from './chart_manager.mjs'
2626
import { ConfigManager } from './config_manager.mjs'
27-
import { DependencyManager } from './dependency_manager.mjs'
2827
import { KeyManager } from './key_manager.mjs'
2928

3029
// Expose components from the core module
@@ -39,6 +38,5 @@ export {
3938
Templates,
4039
ChartManager,
4140
ConfigManager,
42-
DependencyManager,
4341
KeyManager
4442
}

0 commit comments

Comments
 (0)