forked from hyperledger-cacti/cacti
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(test-tooling): add container image builder utilities
1. Currently our integration tests depend on pre-published container images to be on the official registry (ghcr.io). This has pros and cons. The pro is that we can pin the tests to a specific ledger version and then have confidence that the test code works with that specific image. On the other hand if the image itself has problems we won't know it until after it was published and then tests were executed with it (unless we perform manual testing which is a lot of effrot as it requires the manual modification of the test cases). 2. In order to gives us the ability to test against the container image definitions as they are in the current revision of the source code, we are adding here a couple of utility functions to streamline writing test cases that build the container images for themselves as part of the test case. An example of how to use it in a test case: ```typescript const imgConnectorJvm = await buildImageConnectorCordaServer({ logLevel, }); // ... connector = new CordaConnectorContainer({ logLevel, imageName: imgConnectorJvm.imageName, imageVersion: imgConnectorJvm.imageVersion, envVars: [envVarSpringAppJson], }); ``` Signed-off-by: Peter Somogyvari <[email protected]>
- Loading branch information
Showing
5 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
64 changes: 64 additions & 0 deletions
64
packages/cactus-test-tooling/src/main/typescript/common/build-container-image.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
); | ||
}); | ||
} |
52 changes: 52 additions & 0 deletions
52
packages/cactus-test-tooling/src/main/typescript/corda/build-image-connector-corda-server.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; | ||
} |
49 changes: 49 additions & 0 deletions
49
packages/cactus-test-tooling/src/main/typescript/corda/build-image-corda-all-in-one-v4-12.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
``` | ||
|
||
|
||
|