diff --git a/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts b/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts new file mode 100644 index 0000000000..94bc499a15 --- /dev/null +++ b/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts @@ -0,0 +1,266 @@ +import Docker, { Container, ContainerInfo } from "dockerode"; +import axios from "axios"; +import Joi from "joi"; +import { EventEmitter } from "events"; +import { ITestLedger } from "../i-test-ledger"; + +/* + * Contains options for Fabric container + */ +export interface IFabricTestLedgerV1ConstructorOptions { + imageVersion?: string; + imageName?: string; + opsApiHttpPort?: number; +} + +/* + * Provides default options for Fabric container + */ +const DEFAULT_OPTS = Object.freeze({ + imageVersion: "latest", + imageName: "hyperledger/cactus-fabric-all-in-one", + opsApiHttpPort: 9443, +}); +export const FABRIC_TEST_LEDGER_DEFAULT_OPTIONS = DEFAULT_OPTS; + +/* + * Provides validations for the Fabric container's options + */ +const OPTS_JOI_SCHEMA: Joi.Schema = Joi.object().keys({ + imageVersion: Joi.string().min(5).required(), + imageName: Joi.string().min(1).required(), + opsApiHttpPort: Joi.number().integer().min(1024).max(65535).required(), +}); + +export const FABRIC_TEST_LEDGER_OPTIONS_JOI_SCHEMA = OPTS_JOI_SCHEMA; + +export class FabricTestLedgerV1 implements ITestLedger { + public readonly imageVersion: string; + public readonly imageName: string; + public readonly opsApiHttpPort: number; + + private container: Container | undefined; + + constructor( + public readonly options: IFabricTestLedgerV1ConstructorOptions = {} + ) { + if (!options) { + throw new TypeError(`FabricTestLedgerV1#ctor options was falsy.`); + } + this.imageVersion = options.imageVersion || DEFAULT_OPTS.imageVersion; + this.imageName = options.imageName || DEFAULT_OPTS.imageName; + this.opsApiHttpPort = options.opsApiHttpPort || DEFAULT_OPTS.opsApiHttpPort; + + this.validateConstructorOptions(); + } + + public getContainer(): Container { + const fnTag = "FabricTestLedgerV1#getContainer()"; + if (!this.container) { + throw new Error(`${fnTag} container not yet started by this instance.`); + } else { + return this.container; + } + } + + public getContainerImageName(): string { + return `${this.imageName}:${this.imageVersion}`; + } + + public async getOpsApiHttpHost(): Promise { + const ipAddress: string = "127.0.0.1"; + const hostPort: number = await this.getOpsApiPublicPort(); + return `http://${ipAddress}:${hostPort}/version`; + } + + public async start(): Promise { + const containerNameAndTag = this.getContainerImageName(); + + if (this.container) { + await this.container.stop(); + await this.container.remove(); + } + const docker = new Docker(); + + await this.pullContainerImage(containerNameAndTag); + + return new Promise((resolve, reject) => { + const eventEmitter: EventEmitter = docker.run( + containerNameAndTag, + [], + [], + { + ExposedPorts: { + [`${this.opsApiHttpPort}/tcp`]: {}, // Fabric Peer GRPC - HTTP + "7050/tcp": {}, // Orderer GRPC - HTTP + "7051/tcp": {}, // Peer additional - HTTP + "7052/tcp": {}, // Peer Chaincode - HTTP + "7053/tcp": {}, // Peer additional - HTTP + "7054/tcp": {}, // Fabric CA - HTTP + "9001/tcp": {}, // supervisord - HTTP + }, + // This is a workaround needed for macOS which has issues with routing + // to docker container's IP addresses directly... + // https://stackoverflow.com/a/39217691 + PublishAllPorts: true, + }, + {}, + (err: any) => { + if (err) { + reject(err); + } + } + ); + + eventEmitter.once("start", async (container: Container) => { + this.container = container; + try { + await this.waitForHealthCheck(); + resolve(container); + } catch (ex) { + reject(ex); + } + }); + }); + } + + public async waitForHealthCheck(timeoutMs: number = 120000): Promise { + const fnTag = "FabricTestLedgerV1#waitForHealthCheck()"; + const httpUrl = await this.getOpsApiHttpHost(); + const startedAt = Date.now(); + let reachable: boolean = false; + do { + try { + const res = await axios.get(httpUrl); + reachable = res.status > 199 && res.status < 300; + } catch (ex) { + reachable = false; + if (Date.now() >= startedAt + timeoutMs) { + throw new Error(`${fnTag} timed out (${timeoutMs}ms) -> ${ex.stack}`); + } + } + await new Promise((resolve2) => setTimeout(resolve2, 100)); + } while (!reachable); + } + + public stop(): Promise { + const fnTag = "FabricTestLedgerV1#stop()"; + return new Promise((resolve, reject) => { + if (this.container) { + this.container.stop({}, (err: any, result: any) => { + if (err) { + reject(err); + } else { + resolve(result); + } + }); + } else { + return reject(new Error(`${fnTag} Container was not running.`)); + } + }); + } + + public async destroy(): Promise { + const fnTag = "FabricTestLedgerV1#destroy()"; + if (this.container) { + return this.container.remove(); + } else { + throw new Error(`${fnTag} Containernot found, nothing to destroy.`); + } + } + + protected async getContainerInfo(): Promise { + const fnTag = "FabricTestLedgerV1#getContainerInfo()"; + const docker = new Docker(); + const image = this.getContainerImageName(); + const containerInfos = await docker.listContainers({}); + + const aContainerInfo = containerInfos.find((ci) => ci.Image === image); + + if (aContainerInfo) { + return aContainerInfo; + } else { + throw new Error(`${fnTag} no image "${image}"`); + } + } + + public async getOpsApiPublicPort(): Promise { + const fnTag = "FabricTestLedgerV1#getOpsApiPublicPort()"; + const aContainerInfo = await this.getContainerInfo(); + const { opsApiHttpPort: thePort } = this; + const { Ports: ports } = aContainerInfo; + + if (ports.length < 1) { + throw new Error(`${fnTag} no ports exposed or mapped at all`); + } + const mapping = ports.find((x) => x.PrivatePort === thePort); + if (mapping) { + if (!mapping.PublicPort) { + throw new Error(`${fnTag} port ${thePort} mapped but not public`); + } else if (mapping.IP !== "0.0.0.0") { + throw new Error(`${fnTag} port ${thePort} mapped to localhost`); + } else { + return mapping.PublicPort; + } + } else { + throw new Error(`${fnTag} no mapping found for ${thePort}`); + } + } + + public async getContainerIpAddress(): Promise { + const fnTag = "FabricTestLedgerV1#getContainerIpAddress()"; + const aContainerInfo = await this.getContainerInfo(); + + if (aContainerInfo) { + const { NetworkSettings } = aContainerInfo; + const networkNames: string[] = Object.keys(NetworkSettings.Networks); + if (networkNames.length < 1) { + throw new Error(`${fnTag} container not connected to any networks`); + } else { + // return IP address of container on the first network that we found it connected to. Make this configurable? + return NetworkSettings.Networks[networkNames[0]].IPAddress; + } + } else { + throw new Error(`${fnTag} cannot find docker image ${this.imageName}`); + } + } + + private pullContainerImage(containerNameAndTag: string): Promise { + return new Promise((resolve, reject) => { + const docker = new Docker(); + docker.pull(containerNameAndTag, (pullError: any, stream: any) => { + if (pullError) { + reject(pullError); + } else { + docker.modem.followProgress( + stream, + (progressError: any, output: any[]) => { + if (progressError) { + reject(progressError); + } else { + resolve(output); + } + }, + (event: any) => null // ignore the spammy docker download log, we get it in the output variable anyway + ); + } + }); + }); + } + + private validateConstructorOptions(): void { + const fnTag = "FabricTestLedgerV1#validateConstructorOptions()"; + const result = Joi.validate( + { + imageVersion: this.imageVersion, + imageName: this.imageName, + opsApiHttpPort: this.opsApiHttpPort, + }, + OPTS_JOI_SCHEMA + ); + + if (result.error) { + throw new Error(`${fnTag} ${result.error.annotate()}`); + } + } +} diff --git a/packages/cactus-test-tooling/src/main/typescript/public-api.ts b/packages/cactus-test-tooling/src/main/typescript/public-api.ts index 3c11e84bba..68f2ba6fa2 100755 --- a/packages/cactus-test-tooling/src/main/typescript/public-api.ts +++ b/packages/cactus-test-tooling/src/main/typescript/public-api.ts @@ -1,11 +1,13 @@ export { ITestLedger } from "./i-test-ledger"; export { IKeyPair, isIKeyPair } from "./i-key-pair"; + export { BesuTestLedger, IBesuTestLedgerConstructorOptions, BESU_TEST_LEDGER_DEFAULT_OPTIONS, BESU_TEST_LEDGER_OPTIONS_JOI_SCHEMA, } from "./besu/besu-test-ledger"; + export { QuorumTestLedger, IQuorumTestLedgerConstructorOptions, @@ -14,9 +16,17 @@ export { } from "./quorum/quorum-test-ledger"; export * from "./quorum/i-quorum-genesis-options"; export { Containers } from "./common/containers"; + export { HttpEchoContainer, IHttpEchoContainerConstructorOptions, HTTP_ECHO_CONTAINER_CTOR_DEFAULTS, HTTP_ECHO_CONTAINER_OPTS_SCHEMA, } from "./http-echo/http-echo-container"; + +export { + FabricTestLedgerV1, + IFabricTestLedgerV1ConstructorOptions, + FABRIC_TEST_LEDGER_DEFAULT_OPTIONS, + FABRIC_TEST_LEDGER_OPTIONS_JOI_SCHEMA, +} from "./fabric/fabric-test-ledger-v1"; diff --git a/packages/cactus-test-tooling/src/test/typescript/integration/fabric/fabric-test-ledger-v1/constructor-validates-options.ts b/packages/cactus-test-tooling/src/test/typescript/integration/fabric/fabric-test-ledger-v1/constructor-validates-options.ts new file mode 100644 index 0000000000..5ebd9c01c1 --- /dev/null +++ b/packages/cactus-test-tooling/src/test/typescript/integration/fabric/fabric-test-ledger-v1/constructor-validates-options.ts @@ -0,0 +1,41 @@ +// tslint:disable-next-line: no-var-requires +const tap = require("tap"); +import isPortReachable from "is-port-reachable"; +import { Container } from "dockerode"; +import { FabricTestLedgerV1 } from "../../../../../main/typescript/public-api"; + +tap.test("constructor throws if invalid input is provided", (assert: any) => { + assert.ok(FabricTestLedgerV1); + assert.throws(() => new FabricTestLedgerV1({ imageVersion: "nope" })); + assert.end(); +}); + +tap.test( + "constructor does not throw if valid input is provided", + (assert: any) => { + assert.ok(FabricTestLedgerV1); + assert.doesNotThrow(() => new FabricTestLedgerV1()); + assert.end(); + } +); + +tap.test("starts/stops/destroys a docker container", async (assert: any) => { + const fabricTestLedger = new FabricTestLedgerV1(); + assert.tearDown(() => fabricTestLedger.stop()); + assert.tearDown(() => fabricTestLedger.destroy()); + + const container: Container = await fabricTestLedger.start(); + assert.ok(container); + const ipAddress: string = await fabricTestLedger.getContainerIpAddress(); + assert.ok(ipAddress); + assert.ok(ipAddress.length); + + const hostPort: number = await fabricTestLedger.getOpsApiPublicPort(); + assert.ok(hostPort, "getOpsApiPublicPort() returns truthy OK"); + assert.ok(isFinite(hostPort), "getOpsApiPublicPort() returns finite OK"); + + const isReachable = await isPortReachable(hostPort, { host: "localhost" }); + assert.ok(isReachable, `HostPort ${hostPort} is reachable via localhost`); + + assert.end(); +}); diff --git a/tools/docker/fabric-all-in-one/Dockerfile b/tools/docker/fabric-all-in-one/Dockerfile new file mode 100644 index 0000000000..e2b666b2a6 --- /dev/null +++ b/tools/docker/fabric-all-in-one/Dockerfile @@ -0,0 +1,65 @@ +# Dockerfile for Hyperledger fabric all-in-one development and experiments, including: +# * fabric-peer +# * fabric-orderer +# * fabric-ca +# * cryptogen +# * configtxgen +# * configtxlator + +# * gotools + +# Workdir is set to $GOPATH/src/github.com/hyperledger/fabric +# Data is stored under /var/hyperledger/db and /var/hyperledger/production + +ARG FABRIC_VERSION=1.4.8 + +FROM hyperledger/fabric-ca:$FABRIC_VERSION as ca +FROM hyperledger/fabric-orderer:$FABRIC_VERSION as orderer +FROM hyperledger/fabric-peer:$FABRIC_VERSION as peer + +COPY --from=ca /usr/local/bin/fabric-ca-server /usr/local/bin/ +COPY --from=ca /usr/local/bin/fabric-ca-client /usr/local/bin/ +COPY --from=orderer /usr/local/bin/orderer /usr/local/bin/ + +ENV FABRIC_CFG_PATH=/etc/hyperledger/fabric +ENV PROJECT_VERSION=1.4.8 + +RUN wget https://github.com/hyperledger/fabric/releases/download/v${PROJECT_VERSION}/hyperledger-fabric-linux-amd64-${PROJECT_VERSION}.tar.gz \ + && tar -xvf hyperledger-fabric-linux-amd64-${PROJECT_VERSION}.tar.gz \ + && rm hyperledger-fabric-linux-amd64-${PROJECT_VERSION}.tar.gz + +RUN mkdir -p $FABRIC_CFG_PATH/config \ + && mkdir -p /etc/hyperledger/fabric-ca-server \ + && mkdir -p /etc/hyperledger/fabric-ca-server-config \ + && mkdir -p /etc/hyperledger/fabric/orderer \ + && mkdir -p /etc/hyperledger/fabric/peer + +COPY ./configtx.yaml $FABRIC_CFG_PATH +COPY ./crypto-config.yaml $FABRIC_CFG_PATH +COPY ./generate.sh $FABRIC_CFG_PATH +COPY ./start_ca.sh /etc/hyperledger/fabric-ca-server +COPY ./start_orderer.sh /etc/hyperledger/fabric/orderer +COPY ./start_peer.sh /etc/hyperledger/fabric/peer +COPY ./join_channel.sh /etc/hyperledger/fabric/peer + +RUN ./$FABRIC_CFG_PATH/generate.sh + +# SUPERVISORD +RUN apt-get update && apt-get install -y supervisor +RUN mkdir -p /var/log/supervisor +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +# fabric-orderer +EXPOSE 7050 +# fabric-peers +EXPOSE 7051 7052 7053 +# fabric-ca-server RESTful +EXPOSE 7054 +# fabric-peer operations +EXPOSE 9443 + +# SUPERVISORD PORTS +EXPOSE 9001 + +ENTRYPOINT ["/usr/bin/supervisord"] +CMD ["--configuration", "/etc/supervisor/conf.d/supervisord.conf", "--nodaemon"] diff --git a/tools/docker/fabric-all-in-one/README.md b/tools/docker/fabric-all-in-one/README.md new file mode 100644 index 0000000000..8e790998be --- /dev/null +++ b/tools/docker/fabric-all-in-one/README.md @@ -0,0 +1,31 @@ +# fabric-docker-all-in-one + +> This docker image is for `testing` and `development` only. +> Do NOT use in production! + +An all in one fabric docker image with 1 peer, 1 orderer and 1 channel. + +Example `.vscode/tasks.json` file for building/running the image: + +```json +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Docker - BUILD and TAG: Latest", + "type": "shell", + "command": "docker build . -t hyperledger/cactus-fabric-all-in-one:latest" + }, + { + "label": "Docker Compose - BUILD", + "type": "shell", + "command": "docker-compose build --force-rm" + }, + { + "label": "Docker Compose - UP", + "type": "shell", + "command": "docker-compose up --force-recreate " + } + ] +} +``` \ No newline at end of file diff --git a/tools/docker/fabric-all-in-one/configtx.yaml b/tools/docker/fabric-all-in-one/configtx.yaml new file mode 100644 index 0000000000..fd39d7415f --- /dev/null +++ b/tools/docker/fabric-all-in-one/configtx.yaml @@ -0,0 +1,131 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +################################################################################ +# +# Section: Organizations +# +# - This section defines the different organizational identities which will +# be referenced later in the configuration. +# +################################################################################ +Organizations: + + # SampleOrg defines an MSP using the sampleconfig. It should never be used + # in production but may be used as a template for other definitions + - &OrdererOrg + # DefaultOrg defines the organization which is used in the sampleconfig + # of the fabric.git development environment + Name: OrdererOrg + + # ID to load the MSP definition as + ID: OrdererMSP + + # MSPDir is the filesystem path which contains the MSP configuration + MSPDir: crypto-config/ordererOrganizations/cactus.com/msp + + - &Org1 + # DefaultOrg defines the organization which is used in the sampleconfig + # of the fabric.git development environment + Name: Org1MSP + + # ID to load the MSP definition as + ID: Org1MSP + + MSPDir: crypto-config/peerOrganizations/org1.cactus.com/msp + + AnchorPeers: + # AnchorPeers defines the location of peers which can be used + # for cross org gossip communication. Note, this value is only + # encoded in the genesis block in the Application section context + - Host: 127.0.0.1 + Port: 7051 + +################################################################################ +# +# SECTION: Application +# +# - This section defines the values to encode into a config transaction or +# genesis block for application related parameters +# +################################################################################ +Application: &ApplicationDefaults + + # Organizations is the list of orgs which are defined as participants on + # the application side of the network + Organizations: + +################################################################################ +# +# SECTION: Orderer +# +# - This section defines the values to encode into a config transaction or +# genesis block for orderer related parameters +# +################################################################################ +Orderer: &OrdererDefaults + + # Orderer Type: The orderer implementation to start + # Available types are "solo" and "kafka" + OrdererType: solo + + Addresses: + - 127.0.0.1:7050 + + # Batch Timeout: The amount of time to wait before creating a batch + BatchTimeout: 2s + + # Batch Size: Controls the number of messages batched into a block + BatchSize: + + # Max Message Count: The maximum number of messages to permit in a batch + MaxMessageCount: 10 + + # Absolute Max Bytes: The absolute maximum number of bytes allowed for + # the serialized messages in a batch. + AbsoluteMaxBytes: 99 MB + + # Preferred Max Bytes: The preferred maximum number of bytes allowed for + # the serialized messages in a batch. A message larger than the preferred + # max bytes will result in a batch larger than preferred max bytes. + PreferredMaxBytes: 512 KB + + Kafka: + # Brokers: A list of Kafka brokers to which the orderer connects + # NOTE: Use IP:port notation + Brokers: + - 127.0.0.1:9092 + + # Organizations is the list of orgs which are defined as participants on + # the orderer side of the network + Organizations: + +################################################################################ +# +# Profile +# +# - Different configuration profiles may be encoded here to be specified +# as parameters to the configtxgen tool +# +################################################################################ +Profiles: + + OneOrgOrdererGenesis: + Orderer: + <<: *OrdererDefaults + Organizations: + - *OrdererOrg + Consortiums: + SampleConsortium: + Organizations: + - *Org1 + OneOrgChannel: + Consortium: SampleConsortium + Application: + <<: *ApplicationDefaults + Organizations: + - *Org1 + diff --git a/tools/docker/fabric-all-in-one/crypto-config.yaml b/tools/docker/fabric-all-in-one/crypto-config.yaml new file mode 100644 index 0000000000..bedf67e1a0 --- /dev/null +++ b/tools/docker/fabric-all-in-one/crypto-config.yaml @@ -0,0 +1,72 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# --------------------------------------------------------------------------- +# "OrdererOrgs" - Definition of organizations managing orderer nodes +# --------------------------------------------------------------------------- +OrdererOrgs: + # --------------------------------------------------------------------------- + # Orderer + # --------------------------------------------------------------------------- + - Name: Orderer + Domain: cactus.com + # --------------------------------------------------------------------------- + # "Specs" - See PeerOrgs below for complete description + # --------------------------------------------------------------------------- + Specs: + - Hostname: orderer +# --------------------------------------------------------------------------- +# "PeerOrgs" - Definition of organizations managing peer nodes +# --------------------------------------------------------------------------- +PeerOrgs: + # --------------------------------------------------------------------------- + # Org1 + # --------------------------------------------------------------------------- + - Name: Org1 + Domain: org1.cactus.com + # --------------------------------------------------------------------------- + # "Specs" + # --------------------------------------------------------------------------- + # Uncomment this section to enable the explicit definition of hosts in your + # configuration. Most users will want to use Template, below + # + # Specs is an array of Spec entries. Each Spec entry consists of two fields: + # - Hostname: (Required) The desired hostname, sans the domain. + # - CommonName: (Optional) Specifies the template or explicit override for + # the CN. By default, this is the template: + # + # "{{.Hostname}}.{{.Domain}}" + # + # which obtains its values from the Spec.Hostname and + # Org.Domain, respectively. + # --------------------------------------------------------------------------- + # Specs: + # - Hostname: foo # implicitly "foo.org1.cactus.com" + # CommonName: foo27.org5.cactus.com # overrides Hostname-based FQDN set above + # - Hostname: bar + # - Hostname: baz + # --------------------------------------------------------------------------- + # "Template" + # --------------------------------------------------------------------------- + # Allows for the definition of 1 or more hosts that are created sequentially + # from a template. By default, this looks like "peer%d" from 0 to Count-1. + # You may override the number of nodes (Count), the starting index (Start) + # or the template used to construct the name (Hostname). + # + # Note: Template and Specs are not mutually exclusive. You may define both + # sections and the aggregate nodes will be created for you. Take care with + # name collisions + # --------------------------------------------------------------------------- + Template: + Count: 1 + # Start: 5 + # Hostname: {{.Prefix}}{{.Index}} # default + # --------------------------------------------------------------------------- + # "Users" + # --------------------------------------------------------------------------- + # Count: The number of user accounts _in addition_ to Admin + # --------------------------------------------------------------------------- + Users: + Count: 1 diff --git a/tools/docker/fabric-all-in-one/docker-compose.yml b/tools/docker/fabric-all-in-one/docker-compose.yml new file mode 100644 index 0000000000..5b80dd4e0b --- /dev/null +++ b/tools/docker/fabric-all-in-one/docker-compose.yml @@ -0,0 +1,13 @@ +--- +version: '3.6' + +services: + fabric-aio: + image: "hyperledger/cactus-fabric-all-in-one:latest" + ports: + - 7050:7050/tcp # Fabric orderer - HTTP + - 7051:7051/tcp # Fabric peer - HTTP + - 7052:7052/tcp # Fabric Chaincode - HTTP + - 7053:7053/tcp # Fabric peer addl - HTTP + - 9443:9443/tcp # Fabric peer operations - HTTP + - 9001:9001/tcp # Supervisord - HTTP diff --git a/tools/docker/fabric-all-in-one/generate.sh b/tools/docker/fabric-all-in-one/generate.sh new file mode 100755 index 0000000000..20d04fdcc3 --- /dev/null +++ b/tools/docker/fabric-all-in-one/generate.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# +# Copyright IBM Corp All Rights Reserved +# +# SPDX-License-Identifier: Apache-2.0 +# +CHANNEL_NAME=mychannel + +# remove previous crypto material and config transactions +rm -fr $FABRIC_CFG_PATH/config/* +rm -fr $FABRIC_CFG_PATH/crypto-config/* + +# generate crypto material +cryptogen generate --config=$FABRIC_CFG_PATH/crypto-config.yaml --output="$FABRIC_CFG_PATH/crypto-config" +if [ "$?" -ne 0 ]; then + echo "Failed to generate crypto material..." + exit 1 +fi + +# generate genesis block for orderer +configtxgen -configPath $FABRIC_CFG_PATH -profile OneOrgOrdererGenesis -outputBlock $FABRIC_CFG_PATH/config/genesis.block +if [ "$?" -ne 0 ]; then + echo "Failed to generate orderer genesis block..." + exit 1 +fi + +# generate channel configuration transaction +configtxgen -configPath $FABRIC_CFG_PATH -profile OneOrgChannel -outputCreateChannelTx $FABRIC_CFG_PATH/config/channel.tx -channelID $CHANNEL_NAME +if [ "$?" -ne 0 ]; then + echo "Failed to generate channel configuration transaction..." + exit 1 +fi + +# generate anchor peer transaction +configtxgen -configPath $FABRIC_CFG_PATH -profile OneOrgChannel -outputAnchorPeersUpdate $FABRIC_CFG_PATH/config/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP +if [ "$?" -ne 0 ]; then + echo "Failed to generate anchor peer update for Org1MSP..." + exit 1 +fi diff --git a/tools/docker/fabric-all-in-one/hooks/post_push b/tools/docker/fabric-all-in-one/hooks/post_push new file mode 100755 index 0000000000..8f41b30ce4 --- /dev/null +++ b/tools/docker/fabric-all-in-one/hooks/post_push @@ -0,0 +1,18 @@ +#!/bin/bash + + +SHORTHASH="$(git rev-parse --short HEAD)" +TODAYS_DATE="$(date +%F)" + +# +# We tag every image with today's date and also the git short hash +# Today's date helps humans quickly intuit which version is older/newer +# And the short hash helps identify the exact git revision that the image was +# built from in case you are chasing some exotic bug that requires this sort of +# rabbithole diving where you are down to comparing the images at this level. +# +DOCKER_TAG="$TODAYS_DATE-$SHORTHASH" + + +docker tag $IMAGE_NAME $DOCKER_REPO:$DOCKER_TAG +docker push $DOCKER_REPO:$DOCKER_TAG diff --git a/tools/docker/fabric-all-in-one/join_channel.sh b/tools/docker/fabric-all-in-one/join_channel.sh new file mode 100755 index 0000000000..723eea27c1 --- /dev/null +++ b/tools/docker/fabric-all-in-one/join_channel.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Create the channel +CORE_PEER_LOCALMSPID=Org1MSP CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/fabric/peer/users/Admin@org1.cactus.com/msp peer channel create -o 127.0.0.1:7050 -c mychannel -f /etc/hyperledger/fabric/config/channel.tx +# Join peer0.org1.example.com to the channel. +CORE_PEER_LOCALMSPID=Org1MSP CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/fabric/peer/users/Admin@org1.cactus.com/msp peer channel join -b mychannel.block diff --git a/tools/docker/fabric-all-in-one/start_ca.sh b/tools/docker/fabric-all-in-one/start_ca.sh new file mode 100755 index 0000000000..85faaff5fe --- /dev/null +++ b/tools/docker/fabric-all-in-one/start_ca.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +cp $FABRIC_CFG_PATH/crypto-config/peerOrganizations/org1.cactus.com/ca/ca.org1.cactus.com-cert.pem /etc/hyperledger/fabric-ca-server-config +cp $FABRIC_CFG_PATH/crypto-config/peerOrganizations/org1.cactus.com/ca/*_sk /etc/hyperledger/fabric-ca-server-config/ca.org1.cactus.com-key.pem + +export FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server +export FABRIC_CA_SERVER_CA_NAME=ca.cactus.com +export FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.cactus.com-cert.pem +export FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.cactus.com-key.pem + +fabric-ca-server start -b admin:adminpw diff --git a/tools/docker/fabric-all-in-one/start_orderer.sh b/tools/docker/fabric-all-in-one/start_orderer.sh new file mode 100755 index 0000000000..1c4d7eaef5 --- /dev/null +++ b/tools/docker/fabric-all-in-one/start_orderer.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +cp -R $FABRIC_CFG_PATH/crypto-config/ordererOrganizations/cactus.com/orderers/orderer.cactus.com/msp $FABRIC_CFG_PATH/orderer/msp + +export FABRIC_LOGGING_SPEC=info +export ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 +export ORDERER_GENERAL_GENESISMETHOD=file +export ORDERER_GENERAL_GENESISFILE=$FABRIC_CFG_PATH/config/genesis.block +export ORDERER_GENERAL_LOCALMSPID=OrdererMSP +export ORDERER_GENERAL_LOCALMSPDIR=$FABRIC_CFG_PATH/orderer/msp + +orderer diff --git a/tools/docker/fabric-all-in-one/start_peer.sh b/tools/docker/fabric-all-in-one/start_peer.sh new file mode 100755 index 0000000000..37c5ffe9be --- /dev/null +++ b/tools/docker/fabric-all-in-one/start_peer.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +cp -R $FABRIC_CFG_PATH/crypto-config/peerOrganizations/org1.cactus.com/peers/peer0.org1.cactus.com/msp $FABRIC_CFG_PATH/peer/msp +cp -R $FABRIC_CFG_PATH/crypto-config/peerOrganizations/org1.cactus.com/users $FABRIC_CFG_PATH/peer/users + +export CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock +export CORE_PEER_ID=peer0.org1.cactus.com +export FABRIC_LOGGING_SPEC=info +export CORE_CHAINCODE_LOGGING_LEVEL=info +export CORE_PEER_LOCALMSPID=Org1MSP +export CORE_PEER_MSPCONFIGPATH=$FABRIC_CFG_PATH/peer/msp/ +export CORE_PEER_ADDRESS=localhost:7051 +export CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 + +peer node start diff --git a/tools/docker/fabric-all-in-one/supervisord.conf b/tools/docker/fabric-all-in-one/supervisord.conf new file mode 100644 index 0000000000..c841eddaed --- /dev/null +++ b/tools/docker/fabric-all-in-one/supervisord.conf @@ -0,0 +1,40 @@ +[supervisord] +logfile = /var/log/supervisord.log +logfile_maxbytes = 50MB +logfile_backups=10 +loglevel = info + +# Commented CA for future use, if needed +#[program:ca] +#command=/etc/hyperledger/fabric-ca-server/start_ca.sh +#autostart=true +#autorestart=true +#stderr_logfile=/var/log/fabric-ca.err.log +#stdout_logfile=/var/log/fabric-ca.out.log + +[program:orderer] +command=/etc/hyperledger/fabric/orderer/start_orderer.sh +autostart=true +autorestart=true +stderr_logfile=/var/log/orderer.err.log +stdout_logfile=/var/log/orderer.out.log + +[program:peer] +command=/etc/hyperledger/fabric/peer/start_peer.sh +autostart=true +autorestart=true +stderr_logfile=/var/log/peer.err.log +stdout_logfile=/var/log/peer.out.log + +[program:channel] +command=/etc/hyperledger/fabric/peer/join_channel.sh +autostart=true +autorestart=unexpected +startsecs=0 +exitcodes=0 +startretries=2 +stderr_logfile=/var/log/channel.err.log +stdout_logfile=/var/log/channel.out.log + +[inet_http_server] +port = 0.0.0.0:9001