From bb0352dad85a1acbb4fc4b34026f39f289cfa9c0 Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Wed, 24 Nov 2021 12:00:23 -0800 Subject: [PATCH] feat(test-tooling): env injenction for Besu, Fabric, Quorum AIOs The Fabric, Quorum and Besu test ledger classes are now capable of injecting arbitrary environment variables into their containers which will help us in the future to update the supply chain app example (and other examples as well) to push their host environment variables into the containers they run internally. The hope with this is that it will make it possible for people in restricted environments (corporate firewalls/proxies) to run the example as well. Fixes #1580 Signed-off-by: Peter Somogyvari --- .../typescript/common/env-map-to-docker.ts | 19 +++++++ .../typescript/common/env-node-to-docker.ts | 23 +++++++++ .../main/typescript/common/env-node-to-map.ts | 23 +++++++++ .../fabric/fabric-test-ledger-v1.ts | 5 +- .../src/main/typescript/public-api.ts | 3 ++ .../typescript/quorum/quorum-test-ledger.ts | 8 +-- .../common/env-to-docker-conversions.test.ts | 50 +++++++++++++++++++ 7 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 packages/cactus-test-tooling/src/main/typescript/common/env-map-to-docker.ts create mode 100644 packages/cactus-test-tooling/src/main/typescript/common/env-node-to-docker.ts create mode 100644 packages/cactus-test-tooling/src/main/typescript/common/env-node-to-map.ts create mode 100644 packages/cactus-test-tooling/src/test/typescript/unit/common/env-to-docker-conversions.test.ts diff --git a/packages/cactus-test-tooling/src/main/typescript/common/env-map-to-docker.ts b/packages/cactus-test-tooling/src/main/typescript/common/env-map-to-docker.ts new file mode 100644 index 0000000000..6ad44c5f25 --- /dev/null +++ b/packages/cactus-test-tooling/src/main/typescript/common/env-map-to-docker.ts @@ -0,0 +1,19 @@ +import { Checks } from "@hyperledger/cactus-common"; + +/** + * Converts an ES6 Map of environment variables into an + * array of strings which is the expected format of the Dockerode library that + * we heavily use in our testing code to launch containers for infrastructure + * simulation. + * + * @param envMap Environment variables as an ES6 map that will be converted into + * an array of strings. + */ +export function envMapToDocker(envMap: Map): string[] { + Checks.truthy(envMap, "test-tooling#envMapToDocker()"); + const out = []; + for (const [key, value] of envMap) { + out.push(`${key}=${value}`); + } + return out; +} diff --git a/packages/cactus-test-tooling/src/main/typescript/common/env-node-to-docker.ts b/packages/cactus-test-tooling/src/main/typescript/common/env-node-to-docker.ts new file mode 100644 index 0000000000..4f385cb47c --- /dev/null +++ b/packages/cactus-test-tooling/src/main/typescript/common/env-node-to-docker.ts @@ -0,0 +1,23 @@ +import { Checks } from "@hyperledger/cactus-common"; + +/** + * Converts the NodeJS formatted (POJO) environment variable object into an + * array of strings which is the expected format of the Dockerode library that + * we heavily use in our testing code to launch containers for infrastructure + * simulation. + * + * @param envNodeJs Environment variables in the format NodeJS provides it to + * the script file that it is running. It's just a plain on Javascript object + * that maps the environment variable names to values like this: + * ```json + * { + * "MY_ENV_VAR": "SomeInterestingValueOfMyEnvVar" + * } + * ``` + */ +export function envNodeToDocker(envNodeJs: NodeJS.ProcessEnv): string[] { + Checks.truthy(envNodeJs, "test-tooling#envNodeToDocker()"); + return Object.entries(envNodeJs).map((parts: [string, unknown]) => { + return `${parts[0]}=${parts[1]}`; + }); +} diff --git a/packages/cactus-test-tooling/src/main/typescript/common/env-node-to-map.ts b/packages/cactus-test-tooling/src/main/typescript/common/env-node-to-map.ts new file mode 100644 index 0000000000..41381f75ec --- /dev/null +++ b/packages/cactus-test-tooling/src/main/typescript/common/env-node-to-map.ts @@ -0,0 +1,23 @@ +import { Checks } from "@hyperledger/cactus-common"; + +/** + * Converts the NodeJS formatted (POJO) environment variable object into an ES6 + * Map object containing the same information. + * + * @param envNodeJs Environment variables in the format NodeJS provides it to + * the script file that it is running. It's just a plain on Javascript object + * that maps the environment variable names to values like this: + * ```json + * { + * "MY_ENV_VAR": "SomeInterestingValueOfMyEnvVar" + * } + * ``` + */ +export function envNodeToMap( + envNodeJs: NodeJS.ProcessEnv, +): Map { + Checks.truthy(envNodeJs, "test-tooling#envNodeToDocker()"); + const map = new Map(); + Object.entries(envNodeJs).forEach(([key, value]) => map.set(key, value)); + return map; +} diff --git a/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts b/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts index afc857ef2b..ed17b2be75 100644 --- a/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts +++ b/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts @@ -32,6 +32,7 @@ import { import path from "path"; import fs from "fs"; import yaml from "js-yaml"; +import { envMapToDocker } from "../common/env-map-to-docker"; export interface organizationDefinitionFabricV2 { path: string; @@ -1204,9 +1205,7 @@ export class FabricTestLedgerV1 implements ITestLedger { public async start(ops?: LedgerStartOptions): Promise { const containerNameAndTag = this.getContainerImageName(); - const dockerEnvVars: string[] = new Array(...this.envVars).map( - (pairs) => `${pairs[0]}=${pairs[1]}`, - ); + const dockerEnvVars = envMapToDocker(this.envVars); if (this.container) { await this.container.stop(); diff --git a/packages/cactus-test-tooling/src/main/typescript/public-api.ts b/packages/cactus-test-tooling/src/main/typescript/public-api.ts index d7c4fb7cf0..94354ab4bf 100755 --- a/packages/cactus-test-tooling/src/main/typescript/public-api.ts +++ b/packages/cactus-test-tooling/src/main/typescript/public-api.ts @@ -153,3 +153,6 @@ export { isRunningInGithubAction } from "./github-actions/is-running-in-github-a export { pruneDockerAllIfGithubAction } from "./github-actions/prune-docker-all-if-github-action"; export { IDockerPullProgress } from "./common/i-docker-pull-progress"; export { IDockerPullProgressDetail } from "./common/i-docker-pull-progress"; +export { envNodeToDocker } from "./common/env-node-to-docker"; +export { envMapToDocker } from "./common/env-map-to-docker"; +export { envNodeToMap } from "./common/env-node-to-map"; diff --git a/packages/cactus-test-tooling/src/main/typescript/quorum/quorum-test-ledger.ts b/packages/cactus-test-tooling/src/main/typescript/quorum/quorum-test-ledger.ts index b2c09f5518..2a708e7e7f 100644 --- a/packages/cactus-test-tooling/src/main/typescript/quorum/quorum-test-ledger.ts +++ b/packages/cactus-test-tooling/src/main/typescript/quorum/quorum-test-ledger.ts @@ -24,6 +24,7 @@ export interface IQuorumTestLedgerConstructorOptions { rpcApiHttpPort?: number; logLevel?: LogLevelDesc; emitContainerLogs?: boolean; + readonly envVars?: string[]; } export const QUORUM_TEST_LEDGER_DEFAULT_OPTIONS = Object.freeze({ @@ -54,6 +55,7 @@ export class QuorumTestLedger implements ITestLedger { private readonly log: Logger; private container: Container | undefined; private containerId: string | undefined; + private readonly envVars: string[]; constructor( public readonly options: IQuorumTestLedgerConstructorOptions = {}, @@ -71,6 +73,8 @@ export class QuorumTestLedger implements ITestLedger { options.rpcApiHttpPort || QUORUM_TEST_LEDGER_DEFAULT_OPTIONS.rpcApiHttpPort; + this.envVars = options.envVars || []; + this.emitContainerLogs = Bools.isBooleanStrict(options.emitContainerLogs) ? (options.emitContainerLogs as boolean) : true; @@ -211,9 +215,7 @@ export class QuorumTestLedger implements ITestLedger { [], [], { - // Env: [ - // 'PRIVATE_CONFIG=ignore'// FIXME make it possible to have privacy configured programmatically for quorum - // ], + Env: this.envVars, ExposedPorts: { [`${this.rpcApiHttpPort}/tcp`]: {}, // quorum RPC - HTTP "8546/tcp": {}, // quorum RPC - WebSocket diff --git a/packages/cactus-test-tooling/src/test/typescript/unit/common/env-to-docker-conversions.test.ts b/packages/cactus-test-tooling/src/test/typescript/unit/common/env-to-docker-conversions.test.ts new file mode 100644 index 0000000000..36a97b64af --- /dev/null +++ b/packages/cactus-test-tooling/src/test/typescript/unit/common/env-to-docker-conversions.test.ts @@ -0,0 +1,50 @@ +import "jest-extended"; + +import { envNodeToDocker } from "../../../../main/typescript/public-api"; +import { envMapToDocker } from "../../../../main/typescript/public-api"; +import { envNodeToMap } from "../../../../main/typescript/public-api"; + +describe("test-tooling#envMapToDocker()", () => { + test("Empty Map", () => { + expect(envMapToDocker(new Map())).toBeArrayOfSize(0); + }); + + test("Non-empty Map", () => { + const data = new Map(); + data.set("X", "Y"); + const out = envMapToDocker(data); + expect(out).toBeArrayOfSize(1); + expect(out[0]).toEqual("X=Y"); + }); +}); + +describe("test-tooling#envNodeToDocker()", () => { + test("Empty POJO", () => { + expect(envNodeToDocker({})).toBeArrayOfSize(0); + }); + + test("Non-empty POJO", () => { + const data = { X: "Y" }; + const out = envNodeToDocker(data); + expect(out).toBeArrayOfSize(1); + expect(out[0]).toEqual("X=Y"); + }); +}); + +describe("test-tooling#envNodeToMap()", () => { + test("Empty POJO", () => { + const out = envNodeToMap({}); + expect(out).toBeTruthy(); + expect(out).toBeInstanceOf(Map); + expect(out.size).toStrictEqual(0); + }); + + test("Non-empty POJO", () => { + const data = { X: "Y" }; + const out = envNodeToMap(data); + expect(out).toBeTruthy(); + expect(out).toBeInstanceOf(Map); + expect(out.size).toStrictEqual(1); + expect(out.get("X")).toStrictEqual("Y"); + }); +});