Skip to content

Commit

Permalink
feat(test-tooling): add OpenEthereumTestLedger
Browse files Browse the repository at this point in the history
Fixes hyperledger#851

Signed-off-by: Peter Somogyvari <peter.somogyvari@accenture.com>
  • Loading branch information
petermetz committed Apr 23, 2021
1 parent d003722 commit df1b1ab
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { EventEmitter } from "events";
import Docker, { Container } from "dockerode";
import { v4 as internalIpV4 } from "internal-ip";

import {
Logger,
Checks,
LogLevelDesc,
LoggerProvider,
} from "@hyperledger/cactus-common";

import { Containers } from "../common/containers";

export interface IOpenEthereumTestLedgerOptions {
envVars?: string[];
imageVersion?: string;
imageName?: string;
logLevel?: LogLevelDesc;
}

export const K_DEFAULT_OPEN_ETHEREUM_IMAGE_NAME = "openethereum/openethereum";
export const K_DEFAULT_OPEN_ETHEREUM_IMAGE_VERSION = "3.2.4";
export const K_DEFAULT_OPEN_ETHEREUM_HTTP_PORT = 8545;

/**
* Class responsible for programmatically managing a container that is running
* the image made for hosting a keycloak instance which can be used to test
* authorization/authentication related use-cases.
*/
export class OpenEthereumTestLedger {
public static readonly CLASS_NAME = "OpenEthereumTestLedger";
private readonly log: Logger;
private readonly imageName: string;
private readonly imageVersion: string;
private readonly envVars: string[];
private _container: Container | undefined;
private _containerId: string | undefined;

public get imageFqn(): string {
return `${this.imageName}:${this.imageVersion}`;
}

public get className(): string {
return OpenEthereumTestLedger.CLASS_NAME;
}

public get container(): Container {
if (this._container) {
return this._container;
} else {
throw new Error(`Invalid state: _container is not set. Called start()?`);
}
}

constructor(public readonly options: IOpenEthereumTestLedgerOptions) {
const fnTag = `${this.className}#constructor()`;
Checks.truthy(options, `${fnTag} arg options`);

const level = this.options.logLevel || "INFO";
const label = this.className;
this.log = LoggerProvider.getOrCreate({ level, label });

this.imageName =
this.options.imageName || K_DEFAULT_OPEN_ETHEREUM_IMAGE_NAME;
this.imageVersion =
this.options.imageVersion || K_DEFAULT_OPEN_ETHEREUM_IMAGE_VERSION;
this.envVars = this.options.envVars || [];

this.log.info(`Created ${this.className} OK. Image FQN: ${this.imageFqn}`);
}

public async start(): Promise<Container> {
if (this._container) {
await this.container.stop();
await this.container.remove();
}
const docker = new Docker();

await Containers.pullImage(this.imageFqn);

const Env = [...[], ...this.envVars];
this.log.debug(`Effective Env of container: %o`, Env);

const Healthcheck = {
Test: ["CMD-SHELL", `curl -v 'http://localhost:8545/'`],
Interval: 1000000000, // 1 second
Timeout: 3000000000, // 3 seconds
Retries: 99,
StartPeriod: 1000000000, // 1 second
};

return new Promise<Container>((resolve, reject) => {
const eventEmitter: EventEmitter = docker.run(
this.imageFqn,
[],
[],
{
Env,
PublishAllPorts: true,
Healthcheck,
},
{},
(err?: Error) => {
if (err) {
reject(err);
}
},
);

eventEmitter.once("start", async (container: Container) => {
this._container = container;
this._containerId = container.id;
try {
await Containers.waitForHealthCheck(this._containerId);
resolve(container);
} catch (ex) {
reject(ex);
}
});
});
}

public async getRpcApiHttpHost(host?: string): Promise<string> {
const port = await this.getHostPortHttp();
const lanIpV4OrUndefined = await internalIpV4();
const lanAddress = host || lanIpV4OrUndefined || "127.0.0.1"; // best effort...
return `http://${lanAddress}:${port}`;
}

public async stop(): Promise<void> {
if (this._container) {
await Containers.stop(this.container);
}
}

public destroy(): Promise<void> {
const fnTag = `${this.className}#destroy()`;
if (this._container) {
return this._container.remove();
} else {
const ex = new Error(`${fnTag} Container not found, nothing to destroy.`);
return Promise.reject(ex);
}
}

public async getHostPortHttp(): Promise<number> {
const fnTag = `${this.className}#getHostPortHttp()`;
if (this._containerId) {
const cInfo = await Containers.getById(this._containerId);
return Containers.getPublicPort(K_DEFAULT_OPEN_ETHEREUM_HTTP_PORT, cInfo);
} else {
throw new Error(`${fnTag} Container ID not set. Did you call start()?`);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ export {
KeycloakContainer,
} from "./keycloak/keycloak-container";

export {
IOpenEthereumTestLedgerOptions,
K_DEFAULT_OPEN_ETHEREUM_HTTP_PORT,
K_DEFAULT_OPEN_ETHEREUM_IMAGE_NAME,
K_DEFAULT_OPEN_ETHEREUM_IMAGE_VERSION,
OpenEthereumTestLedger,
} from "./openethereum/openethereum-test-ledger";

export {
SAMPLE_CORDAPP_ROOT_DIRS,
SampleCordappEnum,
Expand Down

0 comments on commit df1b1ab

Please sign in to comment.