Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: do checksum check after downloading platform zip file #125

Merged
merged 2 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions resources/extract-jar.sh

This file was deleted.

39 changes: 39 additions & 0 deletions resources/extract-platform.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash
# This script fetch the build.zip file and checksum file from builds.hedera.com and then extract it into HapiApp2 directory
# Usage extract-platform <release-version>
# e.g. extract-platform v0.42.5

readonly tag="${1}"
if [ -z "${tag}" ]; then
echo "Release tag is required (e.g. v0.42.5)";
exit 1
fi

readonly HAPI_DIR=/opt/hgcapp/services-hedera/HapiApp2.0
readonly HEDERA_USER_HOME_DIR=/home/hedera
readonly HEDERA_BUILDS_URL='https://builds.hedera.com'
readonly RELEASE_DIR="$(awk -F'.' '{print $1"."$2}' <<< "${tag}")"
readonly BUILD_ZIP_FILE="${HEDERA_USER_HOME_DIR}/build-${tag}.zip"
readonly BUILD_ZIP_URL="${HEDERA_BUILDS_URL}/node/software/${RELEASE_DIR}/build-${tag}.zip"
readonly CHECKSUM_FILE="${HEDERA_USER_HOME_DIR}/build-${tag}.sha384"
readonly CHECKSUM_URL="${HEDERA_BUILDS_URL}/node/software/${RELEASE_DIR}/build-${tag}.sha384"

# download
echo "Downloading ${BUILD_ZIP_URL}"
[ -f "${BUILD_ZIP_FILE}" ] || curl -sSf "${BUILD_ZIP_URL}" -o "${BUILD_ZIP_FILE}"
[ $? == 0 ] || exit 1
echo "Downloading ${CHECKSUM_URL}"
[ -f "${CHECKSUM_FILE}" ] || curl -sSf "${CHECKSUM_URL}" -o "${CHECKSUM_FILE}"
[ $? == 0 ] || exit 1
readonly sum="$(openssl dgst -sha384 "${BUILD_ZIP_FILE}" | awk '{print $2}')"
readonly expected_sum="$(awk '{print $1}' < "${CHECKSUM_FILE}")"
if [ "${sum}" != "${expected_sum}" ]; then
echo "SHA sum of ${BUILD_ZIP_FILE} does not match. Aborting."
exit 1
fi

# extract
echo "Extracting ${BUILD_ZIP_FILE}"
[[ -d "${HAPI_DIR}" ]] || mkdir -p "${HAPI_DIR}"
cd "${HAPI_DIR}" && jar xvf "${BUILD_ZIP_FILE}"
[ $? == 0 ] || exit 1
2 changes: 1 addition & 1 deletion src/commands/node.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ export class NodeCommand extends BaseCommand {

// set up the sub-tasks
return task.newListr(subTasks, {
concurrent: false, // parallel uploading of the zip file seems to be unreliable, so we just upload in sequence
concurrent: true, // since we download in the container directly, we want this to be in parallel across all nodes
rendererOptions: {
collapseSubtasks: false
}
Expand Down
8 changes: 6 additions & 2 deletions src/core/k8.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import net from 'net'
import os from 'os'
import path from 'path'
import { flags } from '../commands/index.mjs'
import { FullstackTestingError, MissingArgumentError } from './errors.mjs'
import { FullstackTestingError, IllegalArgumentError, MissingArgumentError } from './errors.mjs'
import * as sb from 'stream-buffers'
import * as tar from 'tar'
import { v4 as uuid4 } from 'uuid'
Expand Down Expand Up @@ -472,7 +472,10 @@ export class K8 {

// zip the source file
const tmpFile = this._tempFileFor(srcFile)
await tar.c({ file: tmpFile, cwd: srcDir }, [srcFile])
await tar.c({
file: tmpFile,
cwd: srcDir
}, [srcFile])

const self = this
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -614,6 +617,7 @@ export class K8 {
if (!Array.isArray(command)) {
command = command.split(' ')
}
if (!await this.getPodByName(podName)) throw new IllegalArgumentError(`Invalid pod ${podName}`)

const self = this
return new Promise((resolve, reject) => {
Expand Down
40 changes: 7 additions & 33 deletions src/core/platform_installer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -100,50 +100,24 @@ export class PlatformInstaller {
}

/**
* Fetch platform code into the container
*
* It uses curl to fetch the platform code directly inside the /home/hedera directory.
*
* Fetch and extract platform code into the container
* @param podName pod name
* @param tag platform release tag
* @return {Promise<boolean|undefined>}
*/

async fetchPlatform (podName, tag) {
if (!podName) throw new MissingArgumentError('podName is required')
if (!tag) throw new MissingArgumentError('tag is required')

try {
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)
}
}
const scriptName = 'extract-platform.sh'
const sourcePath = path.join(constants.RESOURCES_DIR, scriptName) // script source path
await this.copyFiles(podName, [sourcePath], constants.HEDERA_USER_HOME_DIR)

async extractPlatform (podName, buildZipSrc) {
if (!podName) throw new MissingArgumentError('podName is required')
if (!buildZipSrc) throw new MissingArgumentError('buildZipSrc is required')

const buildZipFileName = path.basename(buildZipSrc)
const buildZip = path.join(constants.HEDERA_USER_HOME_DIR, buildZipFileName) // inside the container
const extractScriptName = 'extract-jar.sh'
const extractScriptSrc = path.join(constants.RESOURCES_DIR, extractScriptName)
const extractScript = path.join(constants.HEDERA_USER_HOME_DIR, extractScriptName) // inside the container

this.logger.debug(`Extracting platform code in pod ${podName}`, {
extractScript,
buildZip,
dest: constants.HEDERA_HAPI_PATH
})

try {
await this.copyFiles(podName, [extractScriptSrc], constants.HEDERA_USER_HOME_DIR)
const extractScript = path.join(constants.HEDERA_USER_HOME_DIR, scriptName) // inside the container
await this.k8.execContainer(podName, constants.ROOT_CONTAINER, `chmod +x ${extractScript}`)
await this.k8.execContainer(podName, constants.ROOT_CONTAINER, [extractScript, buildZip, constants.HEDERA_HAPI_PATH])

await this.k8.execContainer(podName, constants.ROOT_CONTAINER, [extractScript, tag])
return true
} catch (e) {
throw new FullstackTestingError(`failed to extract platform code in this pod '${podName}': ${e.message}`, e)
Expand Down
42 changes: 25 additions & 17 deletions test/e2e/core/platform_installer_e2e.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/
import { beforeAll, describe, expect, it } from '@jest/globals'
import {
PackageDownloader,
PlatformInstaller,
constants,
Templates,
Expand All @@ -33,7 +32,6 @@ describe('PackageInstallerE2E', () => {
const configManager = new ConfigManager(testLogger)
const k8 = new K8(configManager, testLogger)
const installer = new PlatformInstaller(testLogger, k8)
const downloader = new PackageDownloader(testLogger)
const testCacheDir = getTestCacheDir()
const podName = 'network-node0-0'
const packageVersion = 'v0.42.5'
Expand All @@ -58,28 +56,38 @@ describe('PackageInstallerE2E', () => {
})
})

describe('copyPlatform', () => {
it('should succeed fetching platform release', async () => {
const releasePrefix = Templates.prepareReleasePrefix(packageVersion)
const destPath = `${testCacheDir}/${releasePrefix}/build-${packageVersion}.zip`
await expect(downloader.fetchPlatform(packageVersion, testCacheDir)).resolves.toBe(destPath)
expect(fs.existsSync(destPath)).toBeTruthy()
testLogger.showUser(destPath)
describe('fetchPlatform', () => {
it('should fail with invalid pod', async () => {
expect.assertions(2)
try {
await installer.fetchPlatform('', packageVersion)
} catch (e) {
expect(e.message.includes('podName is required')).toBeTruthy()
}

// do not delete the cache dir
}, 200000)
try {
await installer.fetchPlatform('INVALID', packageVersion)
} catch (e) {
expect(e.message
.includes('failed to extract platform code in this pod'))
.toBeTruthy()
}
}, 20000)

it('should succeed with valid tag and pod', async () => {
it('should fail with invalid tag', async () => {
expect.assertions(1)
try {
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)
await installer.fetchPlatform(podName, 'INVALID')
} catch (e) {
console.error(e)
expect(e).toBeNull()
expect(e.message.includes('curl: (22) The requested URL returned error: 404')).toBeTruthy()
}
}, 20000)

it('should succeed with valid tag and pod', async () => {
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)
}, 60000)
})

describe('prepareConfigTxt', () => {
Expand Down