Skip to content

Commit

Permalink
feat(corda): dockerfile update to include cordapps
Browse files Browse the repository at this point in the history
Primary Changes
----------------
1. Created AIO to support the deployment of Cordapps in Corda 5
2. Created corda-v5-cordapp-deployment.test.ts to test the AIO
Fixes #3442

Signed-off-by: adrianbatuto <[email protected]>
  • Loading branch information
adrianbatuto committed Aug 27, 2024
1 parent ec9683d commit 0081177
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/cactus-plugin-ledger-connector-corda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"axios": "1.6.0",
"express": "4.19.2",
"express-openapi-validator": "5.2.0",
"form-data": "4.0.0",
"http-errors-enhanced-cjs": "2.0.1",
"internal-ip": "6.2.0",
"joi": "17.13.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import "jest-extended";
import { CordaV5TestLedger } from "@hyperledger/cactus-test-tooling";
import fetch from "node-fetch";
import FormData from "form-data";
import https from "https";

describe("Corda Test Case", () => {
const cordaV5TestLedger = new CordaV5TestLedger({
imageName: "ghcr.io/hyperledger/cacti-corda-5-all-in-one-cordapps",
imageVersion: "2024-08-20-issue3442",
});
beforeAll(async () => {
await cordaV5TestLedger.start();
expect(cordaV5TestLedger).toBeTruthy();
});
afterAll(async () => {
await cordaV5TestLedger.stop();
await cordaV5TestLedger.destroy();
});

describe("Upload Certificates and CPI ", () => {
const username = "admin";
const password = "admin";
const auth =
"Basic " + Buffer.from(`${username}:${password}`).toString("base64");
let cpiHash = "";
let sampleHoldingId = "";
const agent = new https.Agent({ rejectUnauthorized: false });
test("Get and upload digicert-ca", async () => {
const cpiFilePath = "/CSDE-cordapp-template-kotlin/config/r3-ca-key.pem";
const r3KeyBuffer =
await cordaV5TestLedger.getFileFromContainer(cpiFilePath);
const form = new FormData();
form.append("alias", "digicert-ca");
form.append("certificate", r3KeyBuffer, "r3-ca-key.pem");
const response = await fetch(
"https://localhost:8888/api/v1/certificates/cluster/code-signer",
{
method: "PUT",
body: form,
headers: {
accept: "*/*",
Authorization: auth,
...form.getHeaders(),
},
agent: agent,
},
);
expect(response.status).toBe(204);
});
test("Get and upload default key", async () => {
const defaultKeyFilePath =
"/CSDE-cordapp-template-kotlin/config/gradle-plugin-default-key.pem";
const defaultKeyBuffer =
await cordaV5TestLedger.getFileFromContainer(defaultKeyFilePath);
const form = new FormData();
form.append("alias", "gradle-plugin-default-key");
form.append(
"certificate",
defaultKeyBuffer,
"gradle-plugin-default-key.pem",
);
const response = await fetch(
"https://localhost:8888/api/v1/certificates/cluster/code-signer",
{
method: "PUT",
body: form,
headers: {
accept: "*/*",
Authorization: auth,
...form.getHeaders(),
},
agent: agent,
},
);
expect(response.status).toBe(204);
});
test("Get and upload signing key", async () => {
const signingKeyFilePath =
"/CSDE-cordapp-template-kotlin/workspace/signingkey1.pem";
const signingKeyBuffer =
await cordaV5TestLedger.getFileFromContainer(signingKeyFilePath);
const form = new FormData();
form.append("alias", "my-signing-key");
form.append("certificate", signingKeyBuffer, "signingkey1.pem");
const response = await fetch(
"https://localhost:8888/api/v1/certificates/cluster/code-signer",
{
method: "PUT",
body: form,
headers: {
accept: "*/*",
Authorization: auth,
...form.getHeaders(),
},
agent: agent,
},
);
expect(response.status).toBe(204);
});
test("Query Certificates", async () => {
const response = await fetch(
"https://localhost:8888/api/v1/certificates/cluster/code-signer",
{
method: "GET",
headers: {
Authorization: auth,
},
agent: agent,
},
);
expect(response.status).toBe(200);
});
test("Get and upload CPI", async () => {
const cpiFilePath =
"/CSDE-cordapp-template-kotlin/workflows/build/MyCorDapp-1.0-SNAPSHOT.cpi";
const cpiBuffer =
await cordaV5TestLedger.getFileFromContainer(cpiFilePath);
const form = new FormData();
form.append("upload", cpiBuffer, "MyCorDapp-1.0-SNAPSHOT.cpi");
let response = await fetch("https://localhost:8888/api/v1/cpi", {
method: "POST",
body: form,
headers: {
accept: "*/*",
Authorization: auth,
...form.getHeaders(),
},
agent: agent,
});
let responseBody = await response.json();
expect(response.status).toBe(200);
const requestId = responseBody.id;

// Wait time to make sure upload is done
await new Promise((resolve) => setTimeout(resolve, 5000));

response = await fetch(
`https://localhost:8888/api/v1/cpi/status/${requestId}`,
{
method: "GET",
headers: {
Authorization: auth,
},
agent: agent,
},
);
responseBody = await response.json();
cpiHash = responseBody.cpiFileChecksum;
});
test("Create Virtual Nodes", async () => {
const X500 = "CN=IRunCorDapps, OU=Application, O=R3, L=London, C=GB";
let response = await fetch(`https://localhost:8888/api/v1/virtualnode`, {
method: "POST",
body: JSON.stringify({
cpiFileChecksum: cpiHash,
x500Name: X500,
}),
headers: {
Authorization: auth,
},
agent: agent,
});
let responseBody = await response.json();
sampleHoldingId = responseBody.requestId;

// Wait time to make sure Node creation is done
await new Promise((resolve) => setTimeout(resolve, 5000));

response = await fetch(
`https://localhost:8888/api/v1/virtualnode/status/${sampleHoldingId}`,
{
method: "GET",
headers: {
Authorization: auth,
},
agent: agent,
},
);
responseBody = await response.json();
expect(responseBody.status).toBe("SUCCEEDED");
});
test("Start Sample Flow", async () => {
const createSampleFlow = {
clientRequestId: "create-1",
flowClassName:
"com.r3.developers.csdetemplate.utxoexample.workflows.CreateNewChatFlow",
requestBody: {
chatName: "Chat with Bob",
otherMember: "CN=Bob, OU=Test Dept, O=R3, L=London, C=GB",
message: "Hello Bob",
},
};
const cordaReqBuff = Buffer.from(JSON.stringify(createSampleFlow));
const response = await fetch(
`https://localhost:8888/api/v1/flow/${sampleHoldingId}`,
{
method: `POST`,
headers: {
Authorization: auth,
},
body: cordaReqBuff,
agent,
},
);
const responseBody = await response.json();
expect(responseBody.flowStatus).toBe("START_REQUESTED");
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ export class CordaV5TestLedger implements ITestLedger {
throw new Error(`${fnTag} ${validationResult.error.annotate()}`);
}
}
public async getFileFromContainer(filePath: string): Promise<Buffer> {
const container = this.getContainer();
const binaryFile = await Containers.pullBinaryFile(container, filePath);
return binaryFile;
}
}

export function extractShortHash(shortHashID: string, name: string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
FROM docker:20.10.3-dind

RUN apk update
RUN apk add py-pip python3-dev libffi-dev openssl-dev gcc libc-dev make

# Need curl for healthchecks
RUN apk add --no-cache curl

# Need to run shell script run.sh for Corda CLI
RUN apk add --no-cache bash

# The file binary is used to inspect exectubles when debugging container image issues
RUN apk add --no-cache file
RUN apk add --no-cache ca-certificates
RUN apk add --no-cache tzdata
RUN apk add --no-cache gnupg

#----------------- INSTALLING CORDA CDSE PREREQUISITES --------------------
#--------------------------------------------------------------------------

# Installing Zulu17 JDK
RUN wget -P /etc/apk/keys/ \
https://cdn.azul.com/public_keys/[email protected]
RUN echo "https://repos.azul.com/zulu/alpine" | tee -a /etc/apk/repositories
RUN apk update
RUN apk add --no-cache zulu17-jdk

# Installing Corda CLI
RUN mkdir /platform-jars && \
wget -O /platform-jars/binary.tar.gz \
https://download.corda.net/c5-release-pack/f82c7008-3b72-48fb-8e25-5ca38a9483b1-5.1.0/platform-jars-5.1.0.tar.gz

RUN cd /platform-jars/ && \
tar -xvzf binary.tar.gz && \
cp net/corda/cli/deployment/corda-cli-installer/5.1.0.0/corda-cli-installer-5.1.0.0.zip . && \
unzip corda-cli-installer-5.1.0.0.zip -d corda-cli-installer && \
corda-cli-installer/./install.sh
#--------------------------------------------------------------------------
#--------------------------------------------------------------------------

# Installing CORDA CDSE
RUN apk add --no-cache git
RUN git clone https://github.com/corda/CSDE-cordapp-template-kotlin.git

RUN cd CSDE-cordapp-template-kotlin/ && \
git checkout release/corda-5-0 && \
sed -i 's/cordaNotaryPluginsVersion=5.0.0.0/cordaNotaryPluginsVersion=5.0.1.0/' gradle.properties && \
sed -i 's/combinedWorkerJarVersion=5.0.0.0/combinedWorkerJarVersion=5.0.1.0/' gradle.properties && \
sed -i 's/import static org.gradle.api.JavaVersion.VERSION_11/import static org.gradle.api.JavaVersion.VERSION_17/' build.gradle && \
sed -i 's/def javaVersion = VERSION_11/def javaVersion = VERSION_17/' build.gradle

RUN apk add --no-cache supervisor
## TO FIX: Exposing the required ports 5005, 5432 and 8888 for CSDE
## and 9001 for supervisor. Currently commented because of "--network host" being used to run the container
# EXPOSE 5005
# EXPOSE 5432
# EXPOSE 8888
# EXPOSE 9001
EXPOSE 22

COPY supervisord.conf /etc/supervisord.conf

COPY start-services.sh /start-services.sh
RUN chmod +x /start-services.sh

COPY healthcheck.sh /healthcheck.sh
RUN chmod +x /healthcheck.sh

WORKDIR /CSDE-cordapp-template-kotlin/

# Extend the parent image's entrypoint
# https://superuser.com/questions/1459466/can-i-add-an-additional-docker-entrypoint-script
ENTRYPOINT ["/usr/bin/supervisord"]
CMD ["--configuration", "/etc/supervisord.conf", "--nodaemon"]

HEALTHCHECK --interval=30s --timeout=60s --start-period=200s --retries=100 CMD /healthcheck.sh


Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Instructions to compile and run the new Corda v5.0 test network

To create an image of the dockerfile run:
`DOCKER_BUILDKIT=1 docker build ./tools/docker/corda-all-in-one/corda-v5/cordapps-deployment -t cordappdeployment`

To run the AIO, execute:
`docker run -it --rm --privileged -v /var/run/docker.sock:/var/run/docker.sock --network host -d cordappdeployment`
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/sh

# Check if CPI has been generated
check_cpi_exists() {
local file_path="/CSDE-cordapp-template-kotlin/workflows/build/MyCorDapp-1.0-SNAPSHOT.cpi"

if [ ! -f "$file_path" ]; then
echo "CPI file not found at $file_path. Exiting with status code 1."
exit 1
else
echo "CPI file found at $file_path."
fi
}

# Check if the endpoint is accessible
checkCurlSuccess() {
echo "Executing checkCurlSuccess function..."
local response_code
response_code=$(curl -k -s -o /dev/null -w "%{http_code}" -u admin:admin "https://localhost:8888/api/v1/cpi")
if [ "$response_code" -eq 200 ]; then
echo "CURL request successful."
else
echo "CURL request failed with response code: $response_code"
exit 1
fi
}


check_cpi_exists

checkCurlSuccess

WAIT_TIME=30
echo "Waiting for $WAIT_TIME seconds to ensure stability before exiting with status code 0..."
sleep $WAIT_TIME

exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

# Navigate to the specified directory
cd /CSDE-cordapp-template-kotlin/

# Check if the directory change was successful
if [ $? -eq 0 ]; then
# Run the gradlew command
./gradlew 3-buildCpis
else
echo "Failed to change directory to /CSDE-cordapp-template-kotlin/"
fi
Loading

0 comments on commit 0081177

Please sign in to comment.