Skip to content

Commit

Permalink
feat(fabric): user defined fabric samples version
Browse files Browse the repository at this point in the history
    Primary Change
    --------------

    1. The fabric-all-in-one can now accept fabric versions as
    argument at runtime and corresponding code changes are incorporated.

    Minor refactorings that were also necessary to accomodate 1)
    ------------------------------------------------------------

    2. The interface IFabricTestLedgerV1ConstructorOptions in the
    class fabric-test-ledger-v1 is modified to incorporate new
    variable fabricVersion. Corresponding changes are made to the
    functions to include the same.

    3. The client user name has been renamed from user1 to
    user2 as Hyperledger Fabric 2.xversions has user1 already
    registered.

    4. Go has been manually setup in the Dockerfile located
    at tools/docker/fabric-all-in-one/Dockerfile to support
    Hyperledger Fabric 2.x versions, as Go isn't setup by default
    in v2.x

    5. Check for existence has been put over the
    peer1.org{org_number}.example.com as peer1 is not setup
    for test-network.

    6. fabricVersion has been removed from FabricTestLedgerV1,
     and instead envVars map<string,string> has been included to
    support more dynamic features for the fabric-all-in-one
    image like fabricVersion, caVersion

    7. The healthcheck logic for the above mentioned
    Dockerfile has moved to the healthcheck.sh script to support
    Hyperledger Fabric 2.x versions. Changes are made
    for the run-fabric-network.sh script for the same.

    8. The Readme for the Dockerfile mentioned in 4) has been updated

    9. Created 2 seperate Dockerfile for 2.x and 1.4.x versions
    of Hyperledger Fabric

    10. 2 sepearte integration tests corresponding to Fabric v1.4.x
    and v2.2.x

    Fixes #391

Signed-off-by: jagpreetsinghsasan <jagpreet.singh.sasan@accenture.com>
Signed-off-by: Peter Somogyvari <peter.somogyvari@accenture.com>
  • Loading branch information
jagpreetsinghsasan authored and petermetz committed Jan 5, 2021
1 parent f40bf9b commit 8a60717
Show file tree
Hide file tree
Showing 11 changed files with 425 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import {
RunTransactionRequest,
FabricContractInvocationType,
DefaultEventHandlerStrategy,
} from "../../../main/typescript/public-api";
} from "../../../../main/typescript/public-api";

import { IPluginLedgerConnectorFabricOptions } from "../../../main/typescript/plugin-ledger-connector-fabric";
import { IPluginLedgerConnectorFabricOptions } from "../../../../main/typescript/plugin-ledger-connector-fabric";
import { DiscoveryOptions } from "fabric-network";

/**
Expand All @@ -36,12 +36,18 @@ import { DiscoveryOptions } from "fabric-network";
* ```
*/

test("deploys contract from go source", async (t: Test) => {
test("runs tx on a Fabric v1.4.8 ledger", async (t: Test) => {
const logLevel: LogLevelDesc = "TRACE";

const ledger = new FabricTestLedgerV1({
publishAllPorts: true,
logLevel,
imageName: "hyperledger/cactus-fabric-all-in-one",
imageVersion: "2020-12-16-3ddfd8f-v1.4.8",
envVars: new Map([
["FABRIC_VERSION", "1.4.8"],
["CA_VERSION", "1.4.9"],
]),
});

await ledger.start();
Expand All @@ -61,7 +67,7 @@ test("deploys contract from go source", async (t: Test) => {

const keychainInstanceId = uuidv4();
const keychainId = uuidv4();
const keychainEntryKey = "user1";
const keychainEntryKey = "user2";
const keychainEntryValue = JSON.stringify(userIdentity);

const keychainPlugin = new PluginKeychainMemory({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import http from "http";
import { AddressInfo } from "net";

import test, { Test } from "tape";
import { v4 as uuidv4 } from "uuid";

import bodyParser from "body-parser";
import express from "express";

import { FabricTestLedgerV1 } from "@hyperledger/cactus-test-tooling";
import { PluginRegistry } from "@hyperledger/cactus-core";

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

import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory";

import {
PluginLedgerConnectorFabric,
DefaultApi as FabricApi,
RunTransactionRequest,
FabricContractInvocationType,
DefaultEventHandlerStrategy,
} from "../../../../main/typescript/public-api";

import { IPluginLedgerConnectorFabricOptions } from "../../../../main/typescript/plugin-ledger-connector-fabric";
import { DiscoveryOptions } from "fabric-network";

/**
* Use this to debug issues with the fabric node SDK
* ```sh
* export HFC_LOGGING='{"debug":"console","info":"console"}'
* ```
*/

test("runs tx on a Fabric v2.2.0 ledger", async (t: Test) => {
const logLevel: LogLevelDesc = "TRACE";

const ledger = new FabricTestLedgerV1({
publishAllPorts: true,
logLevel,
imageName: "hyperledger/cactus-fabric-all-in-one",
imageVersion: "2020-12-16-3ddfd8f-v2.2.0",
envVars: new Map([
["FABRIC_VERSION", "2.2.0"],
["CA_VERSION", "1.4.9"],
]),
});

await ledger.start();

const tearDownLedger = async () => {
await ledger.stop();
await ledger.destroy();
};
test.onFinish(tearDownLedger);

const [_, adminWallet] = await ledger.enrollAdmin();
const [userIdentity] = await ledger.enrollUser(adminWallet);

const connectionProfile = await ledger.getConnectionProfileOrg1();

const sshConfig = await ledger.getSshConfig();

const keychainInstanceId = uuidv4();
const keychainId = uuidv4();
const keychainEntryKey = "user2";
const keychainEntryValue = JSON.stringify(userIdentity);

const keychainPlugin = new PluginKeychainMemory({
instanceId: keychainInstanceId,
keychainId,
logLevel,
backend: new Map([
[keychainEntryKey, keychainEntryValue],
["some-other-entry-key", "some-other-entry-value"],
]),
});

const pluginRegistry = new PluginRegistry({ plugins: [keychainPlugin] });

const discoveryOptions: DiscoveryOptions = {
enabled: true,
asLocalhost: true,
};

const pluginOptions: IPluginLedgerConnectorFabricOptions = {
instanceId: uuidv4(),
pluginRegistry,
sshConfig,
logLevel,
connectionProfile,
discoveryOptions,
eventHandlerOptions: {
strategy: DefaultEventHandlerStrategy.NETWORKSCOPEALLFORTX,
},
};
const plugin = new PluginLedgerConnectorFabric(pluginOptions);

const expressApp = express();
expressApp.use(bodyParser.json({ limit: "250mb" }));
const server = http.createServer(expressApp);
const listenOptions: IListenOptions = {
hostname: "localhost",
port: 0,
server,
};
const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo;
test.onFinish(async () => await Servers.shutdown(server));
const { address, port } = addressInfo;
const apiHost = `http://${address}:${port}`;
const apiClient = new FabricApi({ basePath: apiHost });

await plugin.installWebServices(expressApp);

const carId = "CAR277";
const carOwner = uuidv4();

{
const res = await apiClient.runTransactionV1({
keychainId,
keychainRef: keychainEntryKey,
channelName: "mychannel",
chainCodeId: "fabcar",
invocationType: FabricContractInvocationType.CALL,
functionName: "queryAllCars",
functionArgs: [],
} as RunTransactionRequest);
t.ok(res);
t.ok(res.data);
t.equal(res.status, 200);
const cars = JSON.parse(res.data.functionOutput);
}

{
const req: RunTransactionRequest = {
keychainId,
keychainRef: keychainEntryKey,
channelName: "mychannel",
invocationType: FabricContractInvocationType.SEND,
chainCodeId: "fabcar",
functionName: "createCar",
functionArgs: [carId, "Trabant", "601", "Blue", carOwner],
};

const res = await apiClient.runTransactionV1(req);
t.ok(res);
t.ok(res.data);
t.equal(res.status, 200);
}

{
const res = await apiClient.runTransactionV1({
keychainId,
keychainRef: keychainEntryKey,
channelName: "mychannel",
chainCodeId: "fabcar",
invocationType: FabricContractInvocationType.CALL,
functionName: "queryAllCars",
functionArgs: [],
} as RunTransactionRequest);
t.ok(res);
t.ok(res.data);
t.equal(res.status, 200);
const cars = JSON.parse(res.data.functionOutput);
const car277 = cars.find((c: any) => c.Key === carId);
t.ok(car277, "Located Car record by its ID OK");
t.ok(car277.Record, `Car object has "Record" property OK`);
t.ok(car277.Record.owner, `Car object has "Record"."owner" property OK`);
t.equal(car277.Record.owner, carOwner, `Car has expected owner OK`);
}

t.end();
});
5 changes: 5 additions & 0 deletions packages/cactus-test-tooling/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/cactus-test-tooling/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"dependencies": {
"@hyperledger/cactus-common": "^0.2.0",
"axios": "0.19.2",
"compare-versions": "3.6.0",
"dockerode": "3.2.0",
"extract-zip": "2.0.0",
"fabric-client": "1.4.11",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import path from "path";
import { EventEmitter } from "events";

import compareVersions, { compare } from "compare-versions";

import Docker, {
Container,
ContainerCreateOptions,
Expand All @@ -14,7 +16,7 @@ import {
Gateway,
} from "fabric-network";
import FabricCAServices from "fabric-ca-client";
import Joi from "joi";
import Joi, { array } from "joi";
import { ITestLedger } from "../i-test-ledger";
import { Containers } from "../common/containers";
import {
Expand All @@ -31,6 +33,7 @@ export interface IFabricTestLedgerV1ConstructorOptions {
publishAllPorts: boolean;
imageVersion?: string;
imageName?: string;
envVars?: Map<string, string>;
logLevel?: LogLevelDesc;
}

Expand All @@ -40,6 +43,7 @@ export interface IFabricTestLedgerV1ConstructorOptions {
const DEFAULT_OPTS = Object.freeze({
imageVersion: "latest",
imageName: "hyperledger/cactus-fabric-all-in-one",
envVars: new Map([["FABRIC_VERSION", "1.4.8"]]),
});
export const FABRIC_TEST_LEDGER_DEFAULT_OPTIONS = DEFAULT_OPTS;

Expand All @@ -53,6 +57,10 @@ const OPTS_JOI_SCHEMA: Joi.Schema = Joi.object().keys({
.regex(/[a-z0-9]+(?:[._-]{1,2}[a-z0-9]+)*/)
.min(1)
.required(),
envVars: Joi.object().pattern(/.*/, [
Joi.string().required(),
Joi.string().min(1).required(),
]),
});

export const FABRIC_TEST_LEDGER_OPTIONS_JOI_SCHEMA = OPTS_JOI_SCHEMA;
Expand All @@ -63,6 +71,7 @@ export class FabricTestLedgerV1 implements ITestLedger {
public readonly imageVersion: string;
public readonly imageName: string;
public readonly publishAllPorts: boolean;
public readonly envVars: Map<string, string>;

private readonly log: Logger;

Expand All @@ -84,6 +93,12 @@ export class FabricTestLedgerV1 implements ITestLedger {
this.imageVersion = options.imageVersion || DEFAULT_OPTS.imageVersion;
this.imageName = options.imageName || DEFAULT_OPTS.imageName;
this.publishAllPorts = options.publishAllPorts;
this.envVars = options.envVars || DEFAULT_OPTS.envVars;

if (compareVersions.compare(this.getFabricVersion(), "1.4", "<"))
this.log.warn(
`This version of Fabric ${this.getFabricVersion()} is unsupported`
);

this.validateConstructorOptions();
}
Expand All @@ -101,6 +116,10 @@ export class FabricTestLedgerV1 implements ITestLedger {
return `${this.imageName}:${this.imageVersion}`;
}

public getFabricVersion(): string {
return `${this.envVars.get("FABRIC_VERSION")}`;
}

public getDefaultMspId(): string {
return "Org1MSP";
}
Expand All @@ -126,7 +145,7 @@ export class FabricTestLedgerV1 implements ITestLedger {
const fnTag = `${this.className}#enrollUser()`;
try {
const mspId = this.getDefaultMspId();
const enrollmentID = "user1";
const enrollmentID = "user2";
const connectionProfile = await this.getConnectionProfileOrg1();
// Create a new gateway for connecting to our peer node.
const gateway = new Gateway();
Expand Down Expand Up @@ -199,7 +218,17 @@ export class FabricTestLedgerV1 implements ITestLedger {
const fnTag = `${this.className}#getConnectionProfileOrg1()`;
const cInfo = await this.getContainerInfo();
const container = this.getContainer();
const ccpJsonPath = "/fabric-samples/first-network/connection-org1.json";
const CCP_JSON_PATH_FABRIC_V1: string =
"/fabric-samples/first-network/connection-org1.json";
const CCP_JSON_PATH_FABRIC_V2: string =
"/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/connection-org1.json";
const ccpJsonPath = compareVersions.compare(
this.getFabricVersion(),
"2.0",
"<"
)
? CCP_JSON_PATH_FABRIC_V1
: CCP_JSON_PATH_FABRIC_V2;
const ccpJson = await Containers.pullFile(container, ccpJsonPath);
const ccp = JSON.parse(ccpJson);

Expand All @@ -209,7 +238,7 @@ export class FabricTestLedgerV1 implements ITestLedger {
const hostPort = await Containers.getPublicPort(privatePort, cInfo);
ccp.peers["peer0.org1.example.com"].url = `grpcs://localhost:${hostPort}`;
}
{
if (ccp.peers["peer1.org1.example.com"]) {
// peer1.org1.example.com
const privatePort = 8051;
const hostPort = await Containers.getPublicPort(privatePort, cInfo);
Expand All @@ -236,8 +265,17 @@ export class FabricTestLedgerV1 implements ITestLedger {
const privatePort = 7050;
const hostPort = await Containers.getPublicPort(privatePort, cInfo);
const url = `grpcs://localhost:${hostPort}`;
const ordererPemPath =
const ORDERER_PEM_PATH_FABRIC_V1: string =
"/fabric-samples/first-network/crypto-config/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem";
const ORDERER_PEM_PATH_FABRIC_V2: string =
"/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem";
const ordererPemPath = compareVersions.compare(
this.getFabricVersion(),
"2.0",
"<"
)
? ORDERER_PEM_PATH_FABRIC_V1
: ORDERER_PEM_PATH_FABRIC_V2;
const pem = await Containers.pullFile(container, ordererPemPath);

ccp.orderers = {
Expand Down Expand Up @@ -301,6 +339,9 @@ export class FabricTestLedgerV1 implements ITestLedger {

public async start(omitPull: boolean = false): Promise<Container> {
const containerNameAndTag = this.getContainerImageName();
const dockerEnvVars: string[] = new Array(...this.envVars).map(
(pairs) => `${pairs[0]}=${pairs[1]}`
);

if (this.container) {
await this.container.stop();
Expand Down Expand Up @@ -329,6 +370,8 @@ export class FabricTestLedgerV1 implements ITestLedger {
"10051/tcp": {}, // peer1.org2.example.com
},

Env: dockerEnvVars,

// This is a workaround needed for macOS which has issues with routing
// to docker container's IP addresses directly...
// https://stackoverflow.com/a/39217691
Expand Down Expand Up @@ -450,6 +493,7 @@ export class FabricTestLedgerV1 implements ITestLedger {
imageVersion: this.imageVersion,
imageName: this.imageName,
publishAllPorts: this.publishAllPorts,
envVars: this.envVars,
},
OPTS_JOI_SCHEMA
);
Expand Down
Loading

0 comments on commit 8a60717

Please sign in to comment.