diff --git a/src/commands/flags.mjs b/src/commands/flags.mjs index 4bc64b8f2..6707badec 100644 --- a/src/commands/flags.mjs +++ b/src/commands/flags.mjs @@ -342,6 +342,15 @@ export const applicationProperties = { } } +export const applicationEnv = { + name: 'application-env', + definition: { + describe: 'application.env file for node', + defaultValue: '', + type: 'string' + } +} + export const apiPermissionProperties = { name: 'api-permission-properties', definition: { @@ -454,7 +463,8 @@ export const allFlags = [ updateAccountKeys, privateKey, accountId, - amount + amount, + applicationEnv ] export const allFlagsMap = new Map(allFlags.map(f => [f.name, f])) diff --git a/src/commands/node.mjs b/src/commands/node.mjs index b01965ad3..15f8441c8 100644 --- a/src/commands/node.mjs +++ b/src/commands/node.mjs @@ -194,7 +194,7 @@ export class NodeCommand extends BaseCommand { config.releasePrefix = Templates.prepareReleasePrefix(config.releaseTag) config.buildZipFile = `${config.cacheDir}/${config.releasePrefix}/build-${config.releaseTag}.zip` config.keysDir = path.join(config.cacheDir, 'keys') - config.stagingDir = `${config.cacheDir}/${config.releasePrefix}/staging/${config.releaseTag}` + config.stagingDir = Templates.renderStagingDir(self.configManager, flags) config.stagingKeysDir = path.join(config.stagingDir, 'keys') if (config.keyFormat === constants.KEY_FORMAT_PFX && config.generateGossipKeys) { @@ -227,15 +227,6 @@ export class NodeCommand extends BaseCommand { title: 'Identify network pods', task: (ctx, task) => self.taskCheckNetworkNodePods(ctx, task) }, - { - title: 'Fetch platform software', - task: async (ctx, _) => { - const config = ctx.config - if (config.force || !fs.existsSync(config.buildZipFile)) { - ctx.config.buildZipFile = await self.downloader.fetchPlatform(ctx.config.releaseTag, config.cacheDir) - } - } - }, { title: 'Generate Gossip keys', task: async (ctx, _) => { @@ -349,7 +340,7 @@ export class NodeCommand extends BaseCommand { } }, { - title: 'Upload platform software into network nodes', + title: 'Fetch platform software into network nodes', task: async (ctx, task) => { const config = ctx.config @@ -360,7 +351,7 @@ export class NodeCommand extends BaseCommand { subTasks.push({ title: `Node: ${chalk.yellow(nodeId)}`, task: () => - self.plaformInstaller.copyPlatform(podName, config.buildZipFile) + self.plaformInstaller.fetchPlatform(podName, config.releaseTag) }) } @@ -428,17 +419,21 @@ export class NodeCommand extends BaseCommand { ctx.config = { namespace: self.configManager.getFlag(flags.namespace), - chartDir: this.configManager.getFlag(flags.chartDirectory), - fstChartVersion: this.configManager.getFlag(flags.fstChartVersion), + chartDir: self.configManager.getFlag(flags.chartDirectory), + fstChartVersion: self.configManager.getFlag(flags.fstChartVersion), nodeIds: helpers.parseNodeIDs(self.configManager.getFlag(flags.nodeIDs)), - deployMirrorNode: this.configManager.getFlag(flags.deployMirrorNode), - deployHederaExplorer: this.configManager.getFlag(flags.deployHederaExplorer), - updateAccountKeys: self.configManager.getFlag(flags.updateAccountKeys) + deployMirrorNode: self.configManager.getFlag(flags.deployMirrorNode), + deployHederaExplorer: self.configManager.getFlag(flags.deployHederaExplorer), + updateAccountKeys: self.configManager.getFlag(flags.updateAccountKeys), + applicationEnv: self.configManager.getFlag(flags.applicationEnv), + cacheDir: self.configManager.getFlag(flags.cacheDir) } ctx.config.chartPath = await this.prepareChartPath(ctx.config.chartDir, constants.FULLSTACK_TESTING_CHART, constants.FULLSTACK_DEPLOYMENT_CHART) + ctx.config.stagingDir = Templates.renderStagingDir(self.configManager, flags) + ctx.config.valuesArg = ` --set hedera-mirror-node.enabled=${ctx.config.deployMirrorNode} --set hedera-explorer.enabled=${ctx.config.deployHederaExplorer}` if (!await this.k8.hasNamespace(ctx.config.namespace)) { @@ -460,6 +455,15 @@ export class NodeCommand extends BaseCommand { title: `Start node: ${chalk.yellow(nodeId)}`, task: async () => { await self.k8.execContainer(podName, constants.ROOT_CONTAINER, ['rm', '-rf', `${constants.HEDERA_HAPI_PATH}/data/logs`]) + + // copy application.env file if required + if (ctx.config.applicationEnv) { + const stagingDir = Templates.renderStagingDir(self.configManager, flags) + const applicationEnvFile = path.join(stagingDir, 'application.env') + fs.cpSync(ctx.config.applicationEnv, applicationEnvFile) + await self.k8.copyTo(podName, constants.ROOT_CONTAINER, applicationEnvFile, `${constants.HEDERA_HAPI_PATH}`) + } + await self.k8.execContainer(podName, constants.ROOT_CONTAINER, ['systemctl', 'restart', 'network-node']) } }) @@ -818,7 +822,8 @@ export class NodeCommand extends BaseCommand { builder: y => flags.setCommandFlags(y, flags.namespace, flags.nodeIDs, - flags.updateAccountKeys + flags.updateAccountKeys, + flags.applicationEnv ), handler: argv => { nodeCmd.logger.debug("==== Running 'node start' ===") diff --git a/src/core/platform_installer.mjs b/src/core/platform_installer.mjs index b9e9528b3..bfd9476fe 100644 --- a/src/core/platform_installer.mjs +++ b/src/core/platform_installer.mjs @@ -99,14 +99,25 @@ export class PlatformInstaller { } } - async copyPlatform (podName, buildZipSrc) { + /** + * Fetch platform code into the container + * + * It uses curl to fetch the platform code directly inside the /home/hedera directory. + * + * @param podName pod name + * @param tag platform release tag + * @return {Promise} + */ + async fetchPlatform (podName, tag) { if (!podName) throw new MissingArgumentError('podName is required') - if (!buildZipSrc) throw new MissingArgumentError('buildZipSrc is required') - if (!fs.statSync(buildZipSrc).isFile()) throw new IllegalArgumentError('buildZipFile does not exists', buildZipSrc) + if (!tag) throw new MissingArgumentError('tag is required') try { - await this.copyFiles(podName, [buildZipSrc], constants.HEDERA_USER_HOME_DIR) - return this.extractPlatform(podName, buildZipSrc) + const releaseDir = Templates.prepareReleasePrefix(tag) + const packageURL = `${constants.HEDERA_BUILDS_URL}/node/software/${releaseDir}/build-${tag}.zip` + const buildZip = path.join(constants.HEDERA_USER_HOME_DIR, `build-${tag}.zip`) + await this.k8.execContainer(podName, constants.ROOT_CONTAINER, `curl -s ${packageURL} -o ${buildZip}`) + return this.extractPlatform(podName, buildZip) } catch (e) { throw new FullstackTestingError(`failed to copy platform code in to pod '${podName}': ${e.message}`, e) } diff --git a/src/core/templates.mjs b/src/core/templates.mjs index 2040067ab..7e85e6cb6 100644 --- a/src/core/templates.mjs +++ b/src/core/templates.mjs @@ -15,7 +15,8 @@ * */ import * as x509 from '@peculiar/x509' -import { DataValidationError, MissingArgumentError } from './errors.mjs' +import path from 'path' +import { DataValidationError, IllegalArgumentError, MissingArgumentError } from './errors.mjs' export class Templates { static renderNetworkPodName (nodeId) { @@ -116,4 +117,24 @@ export class Templates { ) { return new x509.Name(`CN=${nodeId},ST=${state},L=${locality},O=${org},OU=${orgUnit},C=${country}`) } + + static renderStagingDir (configManager, flags) { + if (!configManager) throw new MissingArgumentError('configManager is required') + const cacheDir = configManager.getFlag(flags.cacheDir) + if (!cacheDir) { + throw new IllegalArgumentError('cacheDir cannot be empty') + } + + const releaseTag = configManager.getFlag(flags.releaseTag) + if (!releaseTag) { + throw new IllegalArgumentError('releaseTag cannot be empty') + } + + const releasePrefix = this.prepareReleasePrefix(releaseTag) + if (!releasePrefix) { + throw new IllegalArgumentError('releasePrefix cannot be empty') + } + + return path.resolve(`${cacheDir}/${releasePrefix}/staging/${releaseTag}`) + } } diff --git a/test/e2e/core/platform_installer_e2e.test.mjs b/test/e2e/core/platform_installer_e2e.test.mjs index 9d38eaa41..929de289a 100644 --- a/test/e2e/core/platform_installer_e2e.test.mjs +++ b/test/e2e/core/platform_installer_e2e.test.mjs @@ -37,7 +37,6 @@ describe('PackageInstallerE2E', () => { const testCacheDir = getTestCacheDir() const podName = 'network-node0-0' const packageVersion = 'v0.42.5' - let packageFile = '' beforeAll(async () => { if (!fs.existsSync(testCacheDir)) { @@ -73,8 +72,7 @@ describe('PackageInstallerE2E', () => { it('should succeed with valid tag and pod', async () => { expect.assertions(1) try { - packageFile = await downloader.fetchPlatform(packageVersion, testCacheDir) - await expect(installer.copyPlatform(podName, packageFile, true)).resolves.toBeTruthy() + await expect(installer.fetchPlatform(podName, packageVersion)).resolves.toBeTruthy() const outputs = await k8.execContainer(podName, constants.ROOT_CONTAINER, `ls -la ${constants.HEDERA_HAPI_PATH}`) testLogger.showUser(outputs) } catch (e) { diff --git a/test/unit/core/platform_installer.test.mjs b/test/unit/core/platform_installer.test.mjs index 47dc93287..cad8d3bbf 100644 --- a/test/unit/core/platform_installer.test.mjs +++ b/test/unit/core/platform_installer.test.mjs @@ -93,11 +93,11 @@ describe('PackageInstaller', () => { describe('extractPlatform', () => { it('should fail for missing pod name', async () => { expect.assertions(1) - await expect(installer.copyPlatform('', os.tmpdir())).rejects.toThrow(MissingArgumentError) + await expect(installer.fetchPlatform('', 'v0.42.5')).rejects.toThrow(MissingArgumentError) }) - it('should fail for missing buildZipFile path', async () => { + it('should fail for missing tag', async () => { expect.assertions(1) - await expect(installer.copyPlatform('network-node0-0', '')).rejects.toThrow(MissingArgumentError) + await expect(installer.fetchPlatform('network-node0-0', '')).rejects.toThrow(MissingArgumentError) }) })