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

test(test-tooling): add container image builder utilities #3418

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
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Docker, { ImageBuildContext, ImageBuildOptions } from "dockerode";

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

export interface IBuildContainerImageRequest {
readonly logLevel: LogLevelDesc;
readonly buildDir: Readonly<string>;
readonly imageFile: Readonly<string>;
readonly imageTag: Readonly<string>;
readonly dockerEngine?: Readonly<Docker>;
readonly dockerodeImageBuildOptions?: Partial<ImageBuildOptions>;
readonly dockerodeImageBuildContext?: Partial<ImageBuildContext>;
}

export async function buildContainerImage(
req: Readonly<IBuildContainerImageRequest>,
): Promise<void> {
if (!req) {
throw new Error("Expected arg req to be truthy.");
}
if (!req.buildDir) {
throw new Error("Expected arg req.buildDir to be truthy.");
}
if (!req.imageFile) {
throw new Error("Expected arg req.imageFile to be truthy.");
}
const logLevel: LogLevelDesc = req.logLevel || "INFO";
const dockerEngine = req.dockerEngine || new Docker();

const log = LoggerProvider.getOrCreate({
label: "build-container-image",
level: logLevel,
});

const imageBuildOptions: ImageBuildOptions = {
...req.dockerodeImageBuildOptions,
t: req.imageTag,
};
log.debug("imageBuildOptions=%o", imageBuildOptions);

const imageBuildContext: ImageBuildContext = {
context: req.buildDir,
src: [req.imageFile, "."],
...req.dockerodeImageBuildContext,
};
log.debug("imageBuildContext=%o", imageBuildContext);

const stream = await dockerEngine.buildImage(
imageBuildContext,
imageBuildOptions,
);

stream.on("data", (data: unknown) => {
if (data instanceof Buffer) {
log.debug("[Build]: ", data.toString("utf-8"));
}
});

await new Promise((resolve, reject) => {
dockerEngine.modem.followProgress(stream, (err, res) =>
err ? reject(err) : resolve(res),
);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import path from "node:path";
import { buildContainerImage } from "../public-api";
import { LoggerProvider, LogLevelDesc } from "@hyperledger/cactus-common";

export interface IBuildImageConnectorCordaServerResponse {
readonly imageName: Readonly<string>;
readonly imageVersion: Readonly<string>;
/**
* The concatenation of `imageName` a colon character and `imageVersion`.
*/
readonly imageTag: Readonly<string>;
}

export interface IBuildImageConnectorCordaServerRequest {
readonly logLevel?: Readonly<LogLevelDesc>;
}

export async function buildImageConnectorCordaServer(
req: IBuildImageConnectorCordaServerRequest,
): Promise<IBuildImageConnectorCordaServerResponse> {
if (!req) {
throw new Error("Expected arg req to be truthy.");
}
const logLevel: LogLevelDesc = req.logLevel || "WARN";
const log = LoggerProvider.getOrCreate({
level: logLevel,
label: "build-image-connector-corda-server.ts",
});
const projectRoot = path.join(__dirname, "../../../../../../../");

const buildDirRel =
"./packages/cactus-plugin-ledger-connector-corda/src/main-server/";

const buildDirAbs = path.join(projectRoot, buildDirRel);

log.info("Invoking container build with build dir: %s", buildDirAbs);

const imageName = "cccs";
const imageVersion = "latest";
const imageTag = `${imageName}:${imageVersion}`;

await buildContainerImage({
buildDir: buildDirAbs,
imageFile: "Dockerfile",
imageTag,
logLevel: logLevel,
});

log.info("Building Corda v4 JVM Connector finished OK");

return { imageName, imageVersion, imageTag };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import path from "node:path";
import { buildContainerImage } from "../public-api";
import { LoggerProvider, LogLevelDesc } from "@hyperledger/cactus-common";

export interface IBuildImageCordaAllInOneV412Response {
readonly imageName: Readonly<string>;
readonly imageVersion: Readonly<string>;
/**
* The concatenation of `imageName` a colon character and `imageVersion`.
*/
readonly imageTag: Readonly<string>;
}

export interface IBuildImageCordaAllInOneV412Request {
readonly logLevel?: Readonly<LogLevelDesc>;
}

export async function buildImageCordaAllInOneV412(
req: IBuildImageCordaAllInOneV412Request,
): Promise<IBuildImageCordaAllInOneV412Response> {
if (!req) {
throw new Error("Expected arg req to be truthy.");
}
const logLevel: LogLevelDesc = req.logLevel || "WARN";
const log = LoggerProvider.getOrCreate({
level: logLevel,
label: "build-image-connector-corda-server.ts",
});
const projectRoot = path.join(__dirname, "../../../../../../../");

const buildDirRel = "./tools/docker/corda-all-in-one/corda-v4_12/";

const buildDirAbs = path.join(projectRoot, buildDirRel);

log.info("Invoking container build with build dir: %s", buildDirAbs);

const imageName = "caio412";
const imageVersion = "latest";
const imageTag = `${imageName}:${imageVersion}`;

await buildContainerImage({
buildDir: buildDirAbs,
imageFile: "Dockerfile",
imageTag,
logLevel: logLevel,
});

return { imageName, imageVersion, imageTag };
}
17 changes: 17 additions & 0 deletions packages/cactus-test-tooling/src/main/typescript/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,20 @@ export {
FABRIC_25_LTS_FABRIC_SAMPLES__ORDERER_TLS_ROOTCERT_FILE_ORG_2,
IFabricOrgEnvInfo,
} from "./fabric/fabric-samples-env-constants";

export {
IBuildContainerImageRequest,
buildContainerImage,
} from "./common/build-container-image";

export {
IBuildImageConnectorCordaServerRequest,
IBuildImageConnectorCordaServerResponse,
buildImageConnectorCordaServer,
} from "./corda/build-image-connector-corda-server";

export {
IBuildImageCordaAllInOneV412Request,
IBuildImageCordaAllInOneV412Response,
buildImageCordaAllInOneV412,
} from "./corda/build-image-corda-all-in-one-v4-12";
128 changes: 128 additions & 0 deletions tools/docker/corda-all-in-one/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# cactus-corda-all-in-one

> This docker image is for `testing` and `development` only.
> Do NOT use in production!

## Usage

### Build and Run Image Locally

```sh
DOCKER_BUILDKIT=1 docker build ./tools/docker/corda-all-in-one/ -t caio
docker run --rm --privileged caio
```

# cactus-corda-4-8-all-in-one

> This docker image is for `testing` and `development` only.
> Do NOT use in production!

## Usage

### Build and Run Image Locally

```sh
DOCKER_BUILDKIT=1 docker build ./tools/docker/corda-all-in-one/ -f ./tools/docker/corda-all-in-one/corda-v4_8/Dockerfile -t caio48
docker run --rm --privileged caio48
```

# cactus-corda-4-8-all-in-one-flowdb

> This docker image is for `testing` and `development` only.
> Do NOT use in production!

## Customization

`build.gradle` file from this sample has defined a single node called PartyA. It was modified to deploy the same nodes as in the obligation sample to make it work with our CordaTestLedger:
- Notary
- ParticipantA
- ParticipantB
- ParticipantC

## Usage

### Build and Run Image Locally

```sh
DOCKER_BUILDKIT=1 docker build ./tools/docker/corda-all-in-one/corda-v4_8-flowdb/ -t caio48-flowdb
docker run --rm --privileged caio48-flowdb
```

# cactus-corda-4-12-all-in-one

> This docker image is for `testing` and `development` only.
> Do NOT use in production!

## Usage

### Build and Run Image Locally

```sh
DOCKER_BUILDKIT=1 docker build ./tools/docker/corda-all-in-one/corda-v4_12/ -f ./tools/docker/corda-all-in-one/corda-v4_12/Dockerfile -t caio412
docker run --rm --privileged caio412
```


# cactus-corda-5-all-in-one-solar

> This docker image is for `testing` and `development` only.
> Do NOT use in production!

## Usage

### Build and Run Image Locally

```sh
DOCKER_BUILDKIT=1 docker build ./tools/docker/corda-all-in-one/corda-v5/ -f ./tools/docker/corda-all-in-one/corda-v5/Dockerfile -t caio5
docker run --privileged caio5
```

### Install Application and Testing

Open container CLI:

```sh
docker exec -it <id_docker> /bin/sh
```

In container CLI, run this command to install the sample application on the network:

```sh
/root/bin/corda-cli/bin/corda-cli package install -n solar-system /corda5-solarsystem-contracts-demo/solar-system.cpb
```

To check that everything works correctly, start a flow with the following curl command:

```sh
curl -u earthling:password --insecure -X POST "https://localhost:12112/api/v1/flowstarter/startflow" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"rpcStartFlowRequest\":{\"clientId\":\"launchpad-1\",\"flowName\":\"net.corda.solarsystem.flows.LaunchProbeFlow\",\"parameters\":{\"parametersInJson\":\"{\\\"message\\\": \\\"Hello Mars\\\", \\\"target\\\": \\\"C=GB, L=FOURTH, O=MARS, OU=PLANET\\\", \\\"planetaryOnly\\\":\\\"true\\\"}\"}}}"
```
If the command is successful, it returns a 200 response, including the flowId (a uuid) and the clientId, like the following:
```json
{
"flowId":{
"uuid":"9c8d5b46-be92-4be8-9569-76cb3e41cde9"
},
"clientId":"launchpad-1"
}
```
Using the field value ```flowId``` from the answer above, you can check the flow status:
```sh
curl -u earthling:password --insecure -X GET "https://localhost:12112/api/v1/flowstarter/flowoutcome/<flowId>" -H "accept: application/json"
```
It returns a 200 response, which includes these items in the response body:

- Flow status
- Signatures of both parties
- ID of the state

Sample of response:
```json
{
"status":"COMPLETED",
"resultJson":"{ \n \"txId\" : \"SHA-256:882FCCFA0CE08FEC4F90A8BBC8B8FBC1DE3CBDA8DBED4D6562E0922234B87E4F\",\n \"outputStates\" : [\"{\\\"message\\\":\\\"Hello Mars\\\",\\\"planetaryOnly\\\":true,\\\"launcher\\\":\\\"OU\\u003dPLANET, O\\u003dEARTH, L\\u003dTHIRD, C\\u003dIE\\\",\\\"target\\\":\\\"OU\\u003dPLANET, O\\u003dMARS, L\\u003dFOURTH, C\\u003dGB\\\",\\\"linearId\\\":\\\"31800d11-b518-4fb7-a18e-18cc1c64a4ff\\\"}\"], \n \"signatures\": [\"ijMOjsLWxihWLnfxw7DoIv1gpHFaSAs+VfGSS5qaI1Z4cZu96riAo1uEFSbeskZTt2eGNwv05IP3dS08AjLRCA==\", \"2yRNwdrqKU6/lrUfgmaiXxdPYHjXxfXIYlEL8RHU2aNGQPUVXmc+jbsaNxbcig7Fs0kck28JreuUwn1lJOZODw==\"]\n}",
"exceptionDigest":null
}
```



Loading