├── .gitignore ├── fabctl-msp-unfurler ├── .gitignore ├── settings.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── Dockerfile ├── README.md ├── build.gradle ├── gradlew.bat ├── src │ └── main │ │ └── java │ │ └── org │ │ └── hyperledger │ │ └── fabric │ │ └── fabctl │ │ └── msp │ │ └── unfurler │ │ └── Main.java └── gradlew ├── fabctl-sandbox ├── settings.gradle ├── .gitignore ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── test │ │ ├── resources │ │ │ ├── config │ │ │ │ ├── v1 │ │ │ │ │ └── networks │ │ │ │ │ │ ├── ca-ops-guide │ │ │ │ │ │ ├── Org0-Orderer.properties │ │ │ │ │ │ ├── Org1-CA.properties │ │ │ │ │ │ ├── Org1-Peer1.properties │ │ │ │ │ │ ├── Org1-Peer2.properties │ │ │ │ │ │ ├── Org2-CA.properties │ │ │ │ │ │ ├── Org2-Peer1.properties │ │ │ │ │ │ ├── Org2-Peer2.properties │ │ │ │ │ │ └── Org0-CA.properties │ │ │ │ │ │ └── test-network │ │ │ │ │ │ ├── orderer1.properties │ │ │ │ │ │ ├── orderer2.properties │ │ │ │ │ │ ├── orderer3.properties │ │ │ │ │ │ ├── org1-peer1.properties │ │ │ │ │ │ ├── org1-peer2.properties │ │ │ │ │ │ ├── org2-peer1.properties │ │ │ │ │ │ └── org2-peer2.properties │ │ │ │ └── v0 │ │ │ │ │ ├── orderer1.properties │ │ │ │ │ ├── orderer2.properties │ │ │ │ │ ├── orderer3.properties │ │ │ │ │ ├── org1-peer1.properties │ │ │ │ │ ├── org1-peer2.properties │ │ │ │ │ ├── org2-peer1.properties │ │ │ │ │ └── org2-peer2.properties │ │ │ ├── kube │ │ │ │ ├── ns-test-network.yaml │ │ │ │ ├── pvc-fabric.yaml │ │ │ │ ├── pv-fabric.yaml │ │ │ │ ├── job-scrub-test-network.yaml │ │ │ │ ├── job-crypto-config.yaml │ │ │ │ ├── job-orderer-genesis.yaml │ │ │ │ ├── job-update-org1-anchor-peers.yaml │ │ │ │ ├── job-update-org2-anchor-peers.yaml │ │ │ │ ├── org1-peer1.yaml │ │ │ │ ├── org1-peer2.yaml │ │ │ │ ├── org2-peer1.yaml │ │ │ │ ├── org2-peer2.yaml │ │ │ │ ├── orderer1.yaml │ │ │ │ ├── orderer2.yaml │ │ │ │ └── orderer3.yaml │ │ │ └── fixtures │ │ │ │ └── msp-foo.yaml │ │ └── java │ │ │ └── org │ │ │ └── hyperledger │ │ │ └── fabric │ │ │ └── fabctl │ │ │ ├── v1 │ │ │ ├── WarmupTest.java │ │ │ ├── NetworkDescriptorTest.java │ │ │ ├── BootstrapNetworkCATest.java │ │ │ ├── ConfigTXReaderTest.java │ │ │ ├── TestNetwork.java │ │ │ └── MSPContextTest.java │ │ │ └── v0 │ │ │ ├── NoOpTest.java │ │ │ ├── CreateAndJoinChannelTest.java │ │ │ ├── TestBase.java │ │ │ ├── CreateChannelTest.java │ │ │ ├── PeerJoinScratchTest.java │ │ │ └── SandboxTest.java │ └── main │ │ └── java │ │ └── org │ │ └── hyperledger │ │ └── fabric │ │ └── fabctl │ │ ├── v1 │ │ ├── network │ │ │ ├── Metadata.java │ │ │ ├── CAConfig.java │ │ │ ├── NetworkConfig.java │ │ │ ├── PeerConfig.java │ │ │ ├── OrdererConfig.java │ │ │ ├── Environment.java │ │ │ └── OrganizationConfig.java │ │ ├── chaincode │ │ │ ├── ChaincodeConnection.java │ │ │ ├── ChaincodeMetadata.java │ │ │ └── ChaincodeDescriptor.java │ │ └── msp │ │ │ └── MSPDescriptor.java │ │ └── v0 │ │ ├── command │ │ ├── PeerCommand.java │ │ ├── ConfigTXGenCommand.java │ │ └── FabricCommand.java │ │ ├── DeploymentUtil.java │ │ └── JobUtil.java ├── build.gradle ├── config │ ├── crypto-config.yaml │ └── v0 │ │ └── crypto-config.yaml ├── gradlew.bat ├── gradlew └── README.md ├── images ├── fabctl.png └── hyper-kube.png ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /fabctl-msp-unfurler/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | -------------------------------------------------------------------------------- /fabctl-sandbox/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'fabctl-sandbox' 2 | 3 | -------------------------------------------------------------------------------- /fabctl-msp-unfurler/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'fabctl-msp-unfurler' 2 | 3 | -------------------------------------------------------------------------------- /fabctl-sandbox/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | build/ 4 | config/crypto-config/ 5 | -------------------------------------------------------------------------------- /images/fabctl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledgendary/fabric-hyper-kube/main/images/fabctl.png -------------------------------------------------------------------------------- /images/hyper-kube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledgendary/fabric-hyper-kube/main/images/hyper-kube.png -------------------------------------------------------------------------------- /fabctl-sandbox/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledgendary/fabric-hyper-kube/main/fabctl-sandbox/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /fabctl-msp-unfurler/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledgendary/fabric-hyper-kube/main/fabctl-msp-unfurler/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /fabctl-msp-unfurler/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk 2 | 3 | ARG JAR_FILE=build/libs/fabctl-msp-unfurler.jar 4 | ADD ${JAR_FILE} fabctl-msp-unfurler.jar 5 | ENTRYPOINT ["java", "-jar", "/fabctl-msp-unfurler.jar"] -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/ca-ops-guide/Org0-Orderer.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FOO=BAR -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/ca-ops-guide/Org1-CA.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FOO=BAR 7 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/ca-ops-guide/Org1-Peer1.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FOO=BAR 7 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/ca-ops-guide/Org1-Peer2.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FOO=BAR 7 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/ca-ops-guide/Org2-CA.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FOO=BAR 7 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/ca-ops-guide/Org2-Peer1.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FOO=BAR 7 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/ca-ops-guide/Org2-Peer2.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FOO=BAR 7 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/ns-test-network.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: Namespace 9 | metadata: 10 | name: test-network 11 | -------------------------------------------------------------------------------- /fabctl-sandbox/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /fabctl-msp-unfurler/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/network/Metadata.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.network; 7 | 8 | import lombok.Data; 9 | 10 | @Data 11 | public class Metadata 12 | { 13 | public final String name; 14 | } 15 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/ca-ops-guide/Org0-CA.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CA_SERVER_HOME=/tmp/hyperledger/fabric-ca/crypto 7 | FABRIC_CA_SERVER_TLS_ENABLED=true 8 | FABRIC_CA_SERVER_CSR_CN=rca-org0 9 | FABRIC_CA_SERVER_CSR_HOSTS=0.0.0.0 10 | FABRIC_CA_SERVER_DEBUG=true 11 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/pvc-fabric.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: PersistentVolumeClaim 9 | metadata: 10 | name: fabric 11 | spec: 12 | volumeName: fabric 13 | accessModes: 14 | - ReadWriteOnce 15 | resources: 16 | requests: 17 | storage: 1Gi 18 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v1/WarmupTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1; 7 | 8 | /** 9 | * Just stir the pot a little bit. Say hello to kube, scratch structures, etc. 10 | */ 11 | public class WarmupTest extends TestBase 12 | { 13 | } 14 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/pv-fabric.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: PersistentVolume 9 | metadata: 10 | name: fabric 11 | spec: 12 | storageClassName: standard 13 | accessModes: 14 | - ReadWriteOnce 15 | capacity: 16 | storage: 2Gi 17 | hostPath: 18 | path: /var/hyperledger/fabric 19 | 20 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v0/command/PeerCommand.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0.command; 7 | 8 | public class PeerCommand extends FabricCommand 9 | { 10 | public PeerCommand(final String... command) 11 | { 12 | super("hyperledger/fabric-peer", command); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/network/CAConfig.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.network; 7 | 8 | import lombok.Data; 9 | 10 | @Data 11 | public class CAConfig 12 | { 13 | public final String name; 14 | public final Environment environment; 15 | public final int port = 7054; // ? 16 | } 17 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/chaincode/ChaincodeConnection.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.chaincode; 7 | 8 | import lombok.Data; 9 | 10 | @Data 11 | public class ChaincodeConnection 12 | { 13 | public String address; 14 | public String dial_timeout; 15 | public boolean tls_required; 16 | } 17 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v0/command/ConfigTXGenCommand.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0.command; 7 | 8 | public class ConfigTXGenCommand extends FabricCommand 9 | { 10 | public ConfigTXGenCommand(final String... command) 11 | { 12 | super("hyperledger/fabric-tools", command); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v1/NetworkDescriptorTest.java: -------------------------------------------------------------------------------- 1 | package org.hyperledger.fabric.fabctl.v1; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.hyperledger.fabric.fabctl.v1.network.*; 5 | import org.junit.jupiter.api.Order; 6 | import org.junit.jupiter.api.Test; 7 | 8 | @Slf4j 9 | public class NetworkDescriptorTest extends TestBase 10 | { 11 | @Test 12 | public void testPrettyPrintNetwork() throws Exception 13 | { 14 | final NetworkConfig network = new TestNetwork(); 15 | log.info("Network Configuration:\n{}", yamlMapper.writeValueAsString(network)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/chaincode/ChaincodeMetadata.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.chaincode; 7 | 8 | import lombok.Data; 9 | 10 | @Data 11 | public class ChaincodeMetadata 12 | { 13 | public final String type = "external"; 14 | public String name; 15 | public String label; 16 | public String image; 17 | 18 | public String description; 19 | public String author; 20 | // .. other stuff... 21 | // project_URL; 22 | // org 23 | // version (not label - semantic revision) 24 | // tags / labels 25 | // ... 26 | } 27 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/chaincode/ChaincodeDescriptor.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.chaincode; 7 | 8 | import lombok.Data; 9 | 10 | /** 11 | * overly simplified data structure to describe an external chaincode endpoint. 12 | */ 13 | @Data 14 | public class ChaincodeDescriptor 15 | { 16 | // private final String apiVersion = "v1"; -- think of this like a CRD, but currently saved in a config map. 17 | // private final String kind = "ChaincodeDescriptor"; 18 | public final ChaincodeMetadata metadata; 19 | public final ChaincodeConnection connection; 20 | } 21 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v0/command/FabricCommand.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0.command; 7 | 8 | import lombok.Data; 9 | 10 | // still stirring the pot. .. don't refactor this yet. 11 | @Data 12 | public class FabricCommand 13 | { 14 | public final String image; 15 | public final String label = "2.3.2"; 16 | public final String[] command; 17 | 18 | public FabricCommand(final String image, final String[] command) 19 | { 20 | this.image = image; 21 | this.command = command; 22 | } 23 | 24 | public FabricCommand() 25 | { 26 | this.image = null; 27 | this.command = null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/job-scrub-test-network.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: batch/v1 8 | kind: Job 9 | metadata: 10 | name: job-scrub-fabric-volume 11 | spec: 12 | backoffLimit: 0 13 | completions: 1 14 | template: 15 | metadata: 16 | name: scrub-fabric-volume 17 | spec: 18 | restartPolicy: "Never" 19 | containers: 20 | - name: main 21 | image: alpine 22 | command: 23 | - sh 24 | - -c 25 | - "rm -rvf /var/hyperledger/fabric/*" 26 | volumeMounts: 27 | - name: fabric-volume 28 | mountPath: /var/hyperledger/fabric 29 | volumes: 30 | - name: fabric-volume 31 | persistentVolumeClaim: 32 | claimName: fabric 33 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/network/NetworkConfig.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.network; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import lombok.Data; 11 | 12 | /** 13 | * Nothing fancy, still just stirring the pot. Think of this as a local YAML file describing the overall structure 14 | * of a fabric network. The closest equivalent would be something from minifab's network descriptor. 15 | */ 16 | @Data 17 | public class NetworkConfig 18 | { 19 | public final Metadata metadata; 20 | public final List organizations = new ArrayList<>(); 21 | 22 | public NetworkConfig(final String name) 23 | { 24 | this.metadata = new Metadata(name); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /fabctl-msp-unfurler/README.md: -------------------------------------------------------------------------------- 1 | # MSP Unfurler 2 | 3 | This experimental routine will read YAML descriptor files from an input folder, 4 | unfurling the contents into a folder structure matching the `MSPDir` fabric 5 | configuration attribute. 6 | 7 | This will be used in conjunction with an init container to extract an MSP Context from 8 | a series of configmaps / secrets stored in Kubernetes. 9 | 10 | ## Build 11 | 12 | ```shell 13 | echo -n | ./gradlew build 14 | 15 | docker build -t hyperledgendary/fabric-hyper-kube/fabctl-msp-unfurler . 16 | 17 | kind load docker-image hyperledgendary/fabric-hyper-kube/fabctl-msp-unfurler . 18 | ``` 19 | 20 | ## Run 21 | 22 | ```shell 23 | docker run \ 24 | --rm \ 25 | -e INPUT_FOLDER=/var/hyperledger/msp/in \ 26 | -e OUTPUT_FOLDER=/var/hyperledger/msp/out \ 27 | -v /tmp/msp:/var/hyperledger/msp \ 28 | hyperledgendary/fabric-hyper-kube/fabctl-msp-unfurler 29 | ``` -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/network/PeerConfig.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.network; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import lombok.Data; 11 | import org.hyperledger.fabric.fabctl.v1.msp.MSPDescriptor; 12 | 13 | @Data 14 | public class PeerConfig 15 | { 16 | public final String name; 17 | public final Environment environment; 18 | public final List msps = new ArrayList<>(); 19 | 20 | public PeerConfig(final String name, final Environment environment, final MSPDescriptor... msps) 21 | { 22 | this.name = name; 23 | this.environment = environment; 24 | 25 | for (MSPDescriptor msp : msps) 26 | { 27 | this.msps.add(msp); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/network/OrdererConfig.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.network; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import lombok.Data; 11 | import org.hyperledger.fabric.fabctl.v1.msp.MSPDescriptor; 12 | 13 | @Data 14 | public class OrdererConfig 15 | { 16 | public final String name; 17 | public final Environment environment; 18 | public final List msps = new ArrayList<>(); 19 | 20 | public OrdererConfig(final String name, final Environment environment, final MSPDescriptor... msps) 21 | { 22 | this.name = name; 23 | this.environment = environment; 24 | 25 | for (MSPDescriptor msp : msps) 26 | { 27 | this.msps.add(msp); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v0/NoOpTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0; 7 | 8 | import lombok.Data; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertTrue; 13 | import static org.junit.jupiter.api.Assertions.fail; 14 | 15 | @Slf4j 16 | public class NoOpTest 17 | { 18 | @Data 19 | public static class SimpleStruct 20 | { 21 | int x; 22 | double y; 23 | String z; 24 | } 25 | 26 | @Test 27 | public void testNothing() 28 | { 29 | final SimpleStruct s = new SimpleStruct(); 30 | s.setX(10); 31 | s.setY(20.0); 32 | s.setZ("a string"); 33 | 34 | log.info("this is a log message: {}", s); 35 | assertTrue(true); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/network/Environment.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.network; 7 | 8 | import io.fabric8.kubernetes.api.model.EnvVar; 9 | import io.fabric8.kubernetes.api.model.EnvVarBuilder; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.TreeMap; 14 | 15 | public class Environment extends TreeMap 16 | { 17 | public List asEnvVarList() 18 | { 19 | final List env = new ArrayList<>(); 20 | 21 | for (final Map.Entry e : entrySet()) 22 | { 23 | env.add(new EnvVarBuilder() 24 | .withName(e.getKey()) 25 | .withValue(e.getValue()) 26 | .build()); 27 | } 28 | 29 | return env; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fabctl-sandbox/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'org.hyperledger.fabric' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | 14 | implementation 'io.fabric8:kubernetes-client:5.7.0' 15 | implementation 'org.slf4j:slf4j-api:1.7.+' 16 | implementation 'ch.qos.logback:logback-classic:1.2.+' 17 | implementation 'org.apache.commons:commons-compress:1.21' 18 | implementation group: 'commons-codec', name: 'commons-codec', version: '1.15' 19 | 20 | 21 | 22 | compileOnly "org.projectlombok:lombok:1.18.20" 23 | testCompileOnly "org.projectlombok:lombok:1.18.20" 24 | annotationProcessor "org.projectlombok:lombok:1.18.20" 25 | testAnnotationProcessor "org.projectlombok:lombok:1.18.20" 26 | 27 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' 28 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' 29 | } 30 | 31 | test { 32 | useJUnitPlatform() 33 | testLogging { 34 | outputs.upToDateWhen {false} 35 | showStandardStreams = true 36 | } 37 | } -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/test-network/orderer1.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info 8 | ORDERER_GENERAL_BOOTSTRAPFILE=/var/hyperledger/fabric/channel-artifacts/genesis.block 9 | 10 | ORDERER_FILELEDGER_LOCATION=/var/hyperledger/fabric/data/orderer1 11 | ORDERER_CONSENSUS_WALDIR=/var/hyperledger/fabric/data/orderer1/etcdraft/wal 12 | ORDERER_CONSENSUS_SNAPDIR=/var/hyperledger/fabric/data/orderer1/etcdraft/wal 13 | 14 | ORDERER_GENERAL_LOCALMSPID=OrdererMSP 15 | ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/fabric/xyzzy/orderer1.example.com/msp 16 | ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/fabric/xyzzy/orderer1.example.com/tls/server.key 17 | ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/fabric/xyzzy/orderer1.example.com/tls/server.crt 18 | ORDERER_GENERAL_TLS_ROOTCAS=/var/hyperledger/fabric/xyzzy/orderer1.example.com/tls/ca.crt 19 | ORDERER_GENERAL_TLS_ENABLED=true 20 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/test-network/orderer2.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info 8 | ORDERER_GENERAL_BOOTSTRAPFILE=/var/hyperledger/fabric/channel-artifacts/genesis.block 9 | 10 | ORDERER_FILELEDGER_LOCATION=/var/hyperledger/fabric/data/orderer2 11 | ORDERER_CONSENSUS_WALDIR=/var/hyperledger/fabric/data/orderer2/etcdraft/wal 12 | ORDERER_CONSENSUS_SNAPDIR=/var/hyperledger/fabric/data/orderer2/etcdraft/wal 13 | 14 | ORDERER_GENERAL_LOCALMSPID=OrdererMSP 15 | ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/fabric/xyzzy/orderer2.example.com/msp 16 | ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/fabric/xyzzy/orderer2.example.com/tls/server.key 17 | ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/fabric/xyzzy/orderer2.example.com/tls/server.crt 18 | ORDERER_GENERAL_TLS_ROOTCAS=/var/hyperledger/fabric/xyzzy/orderer2.example.com/tls/ca.crt 19 | ORDERER_GENERAL_TLS_ENABLED=true 20 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/test-network/orderer3.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info 8 | ORDERER_GENERAL_BOOTSTRAPFILE=/var/hyperledger/fabric/channel-artifacts/genesis.block 9 | 10 | ORDERER_FILELEDGER_LOCATION=/var/hyperledger/fabric/data/orderer3 11 | ORDERER_CONSENSUS_WALDIR=/var/hyperledger/fabric/data/orderer3/etcdraft/wal 12 | ORDERER_CONSENSUS_SNAPDIR=/var/hyperledger/fabric/data/orderer3/etcdraft/wal 13 | 14 | ORDERER_GENERAL_LOCALMSPID=OrdererMSP 15 | ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/fabric/xyzzy/orderer3.example.com/msp 16 | ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/fabric/xyzzy/orderer3.example.com/tls/server.key 17 | ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/fabric/xyzzy/orderer3.example.com/tls/server.crt 18 | ORDERER_GENERAL_TLS_ROOTCAS=/var/hyperledger/fabric/xyzzy/orderer3.example.com/tls/ca.crt 19 | ORDERER_GENERAL_TLS_ENABLED=true 20 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/network/OrganizationConfig.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.network; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import lombok.Data; 11 | import org.hyperledger.fabric.fabctl.v1.msp.MSPDescriptor; 12 | 13 | @Data 14 | public class OrganizationConfig 15 | { 16 | public final String name; 17 | public final String mspID; 18 | public final List msps = new ArrayList<>(); 19 | 20 | public final List cas = new ArrayList<>(); 21 | public final List peers = new ArrayList<>(); 22 | public final List orderers = new ArrayList<>(); 23 | 24 | public OrganizationConfig(final String name, 25 | final String mspID, 26 | final MSPDescriptor... msps) 27 | { 28 | this.name = name; 29 | this.mspID = mspID; 30 | 31 | for (MSPDescriptor msp : msps) 32 | { 33 | this.msps.add(msp); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /fabctl-msp-unfurler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'application' 3 | 4 | group 'org.hyperledger.fabric.fabctl.msp' 5 | 6 | mainClassName = 'org.hyperledger.fabric.fabctl.msp.unfurler.Main' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | implementation 'org.slf4j:slf4j-api:1.7.+' 14 | implementation 'ch.qos.logback:logback-classic:1.2.+' 15 | implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.12.5' 16 | implementation group: 'commons-io', name: 'commons-io', version: '2.11.0' 17 | 18 | compileOnly "org.projectlombok:lombok:1.18.20" 19 | testCompileOnly "org.projectlombok:lombok:1.18.20" 20 | annotationProcessor "org.projectlombok:lombok:1.18.20" 21 | testAnnotationProcessor "org.projectlombok:lombok:1.18.20" 22 | 23 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' 24 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' 25 | } 26 | 27 | test { 28 | useJUnitPlatform() 29 | } 30 | 31 | jar { 32 | manifest { 33 | attributes "Main-Class": mainClassName 34 | } 35 | 36 | from { 37 | configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/job-crypto-config.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: batch/v1 8 | kind: Job 9 | metadata: 10 | name: job-crypto-config 11 | spec: 12 | backoffLimit: 1 13 | template: 14 | metadata: 15 | name: crypto-config 16 | spec: 17 | restartPolicy: "Never" 18 | containers: 19 | - name: main 20 | image: hyperledger/fabric-tools:2.3.2 21 | # todo: fixme for KIND clusters 22 | imagePullPolicy: Always 23 | command: 24 | - cryptogen 25 | - generate 26 | - --config=/var/hyperledger/fabric/crypto-config.yaml 27 | - --output=/var/hyperledger/fabric/crypto-config 28 | volumeMounts: 29 | - name: fabric-volume 30 | mountPath: /var/hyperledger/fabric 31 | - name: config-volume 32 | mountPath: /var/hyperledger/fabric/crypto-config.yaml 33 | subPath: crypto-config.yaml 34 | volumes: 35 | - name: fabric-volume 36 | persistentVolumeClaim: 37 | claimName: fabric 38 | - name: config-volume 39 | configMap: 40 | name: fabric-config 41 | 42 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/test-network/org1-peer1.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info 8 | CORE_PEER_TLS_ENABLED=true 9 | CORE_PEER_TLS_CERT_FILE=/var/hyperledger/fabric/xyzzy/org1-peer1.org1.example.com/tls/server.crt 10 | CORE_PEER_TLS_KEY_FILE=/var/hyperledger/fabric/xyzzy/org1-peer1.org1.example.com/tls/server.key 11 | CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/fabric/xyzzy/org1-peer1.org1.example.com/tls/ca.crt 12 | CORE_PEER_ID=org1-peer1.org1.example.com 13 | CORE_PEER_ADDRESS=org1-peer1:7051 14 | CORE_PEER_LISTENADDRESS=0.0.0.0:7051 15 | CORE_PEER_CHAINCODEADDRESS=org1-peer1:7052 16 | CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 17 | CORE_PEER_GOSSIP_BOOTSTRAP=org1-peer2:7051 18 | CORE_PEER_GOSSIP_EXTERNALENDPOINT=org1-peer1:7051 19 | CORE_PEER_LOCALMSPID=Org1MSP 20 | CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/xyzzy/org1-peer1.org1.example.com/msp 21 | CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 22 | CORE_PEER_FILESYSTEMPATH=/var/hyperledger/fabric/data/org1-peer1.org1.example.com 23 | CORE_LEDGER_SNAPSHOTS_ROOTDIR=/var/hyperledger/fabric/data/org1-peer1.org1.example.com/snapshots -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/test-network/org1-peer2.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info 8 | CORE_PEER_TLS_ENABLED=true 9 | CORE_PEER_TLS_CERT_FILE=/var/hyperledger/fabric/xyzzy/org1-peer2.org1.example.com/tls/server.crt 10 | CORE_PEER_TLS_KEY_FILE=/var/hyperledger/fabric/xyzzy/org1-peer2.org1.example.com/tls/server.key 11 | CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/fabric/xyzzy/org1-peer2.org1.example.com/tls/ca.crt 12 | CORE_PEER_ID=org1-peer2.org1.example.com 13 | CORE_PEER_ADDRESS=org1-peer2:7051 14 | CORE_PEER_LISTENADDRESS=0.0.0.0:7051 15 | CORE_PEER_CHAINCODEADDRESS=org1-peer2:7052 16 | CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 17 | CORE_PEER_GOSSIP_BOOTSTRAP=org1-peer1:7051 18 | CORE_PEER_GOSSIP_EXTERNALENDPOINT=org1-peer2:7051 19 | CORE_PEER_LOCALMSPID=Org1MSP 20 | CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/xyzzy/org1-peer2.org1.example.com/msp 21 | CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 22 | CORE_PEER_FILESYSTEMPATH=/var/hyperledger/fabric/data/org1-peer2.org1.example.com 23 | CORE_LEDGER_SNAPSHOTS_ROOTDIR=/var/hyperledger/fabric/data/org1-peer2.org1.example.com/snapshots -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/test-network/org2-peer1.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info 8 | CORE_PEER_TLS_ENABLED=true 9 | CORE_PEER_TLS_CERT_FILE=/var/hyperledger/fabric/xyzzy/org2-peer1.org2.example.com/tls/server.crt 10 | CORE_PEER_TLS_KEY_FILE=/var/hyperledger/fabric/xyzzy/org2-peer1.org2.example.com/tls/server.key 11 | CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/fabric/xyzzy/org2-peer1.org2.example.com/tls/ca.crt 12 | CORE_PEER_ID=org2-peer1.org2.example.com 13 | CORE_PEER_ADDRESS=org2-peer1:7051 14 | CORE_PEER_LISTENADDRESS=0.0.0.0:7051 15 | CORE_PEER_CHAINCODEADDRESS=org2-peer1:7052 16 | CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 17 | CORE_PEER_GOSSIP_BOOTSTRAP=org2-peer2:7051 18 | CORE_PEER_GOSSIP_EXTERNALENDPOINT=org2-peer1:7051 19 | CORE_PEER_LOCALMSPID=Org2MSP 20 | CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/xyzzy/org2-peer1.org2.example.com/msp 21 | CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 22 | CORE_PEER_FILESYSTEMPATH=/var/hyperledger/fabric/data/org2-peer1.org2.example.com 23 | CORE_LEDGER_SNAPSHOTS_ROOTDIR=/var/hyperledger/fabric/data/org2-peer1.org2.example.com/snapshots -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v1/networks/test-network/org2-peer2.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info 8 | CORE_PEER_TLS_ENABLED=true 9 | CORE_PEER_TLS_CERT_FILE=/var/hyperledger/fabric/xyzzy/org2-peer2.org2.example.com/tls/server.crt 10 | CORE_PEER_TLS_KEY_FILE=/var/hyperledger/fabric/xyzzy/org2-peer2.org2.example.com/tls/server.key 11 | CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/fabric/xyzzy/org2-peer2.org2.example.com/tls/ca.crt 12 | CORE_PEER_ID=org2-peer2.org2.example.com 13 | CORE_PEER_ADDRESS=org2-peer2:7051 14 | CORE_PEER_LISTENADDRESS=0.0.0.0:7051 15 | CORE_PEER_CHAINCODEADDRESS=org2-peer2:7052 16 | CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 17 | CORE_PEER_GOSSIP_BOOTSTRAP=org2-peer1:7051 18 | CORE_PEER_GOSSIP_EXTERNALENDPOINT=org2-peer2:7051 19 | CORE_PEER_LOCALMSPID=Org2MSP 20 | CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/xyzzy/org2-peer2.org2.example.com/msp 21 | CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 22 | CORE_PEER_FILESYSTEMPATH=/var/hyperledger/fabric/data/org2-peer2.org2.example.com 23 | CORE_LEDGER_SNAPSHOTS_ROOTDIR=/var/hyperledger/fabric/data/org2-peer2.org2.example.com/snapshots -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/job-orderer-genesis.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: batch/v1 8 | kind: Job 9 | metadata: 10 | namespace: test-network 11 | generateName: orderer-genesis- 12 | spec: 13 | backoffLimit: 1 14 | template: 15 | metadata: 16 | name: orderer-genesis 17 | spec: 18 | restartPolicy: "Never" 19 | containers: 20 | - name: main 21 | image: hyperledger/fabric-tools:2.3.2 22 | # todo: latest tag is not at docker hub 23 | imagePullPolicy: Always 24 | env: 25 | - name: FABRIC_CFG_PATH 26 | value: /var/hyperledger/fabric 27 | command: [ 28 | "configtxgen", 29 | "-profile", "TwoOrgsOrdererGenesis", 30 | "-channelID", "test-system-channel-name", 31 | "-outputBlock", "/var/hyperledger/fabric/channel-artifacts/genesis.block" 32 | ] 33 | volumeMounts: 34 | - name: fabric-volume 35 | mountPath: /var/hyperledger/fabric 36 | - name: config-volume 37 | mountPath: /var/hyperledger/fabric/configtx.yaml 38 | subPath: configtx.yaml 39 | volumes: 40 | - name: fabric-volume 41 | persistentVolumeClaim: 42 | claimName: fabric 43 | - name: config-volume 44 | configMap: 45 | name: fabric-config -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v0/orderer1.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info 8 | ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 9 | ORDERER_GENERAL_LISTENPORT=6050 10 | ORDERER_GENERAL_LOCALMSPID=OrdererMSP 11 | ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/msp 12 | ORDERER_GENERAL_TLS_ENABLED=true 13 | ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.key 14 | ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.crt 15 | ORDERER_GENERAL_TLS_ROOTCAS=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt 16 | ORDERER_GENERAL_BOOTSTRAPMETHOD=file 17 | ORDERER_GENERAL_BOOTSTRAPFILE=/var/hyperledger/fabric/channel-artifacts/genesis.block 18 | ORDERER_FILELEDGER_LOCATION=/var/hyperledger/fabric/data/orderer1 19 | ORDERER_CONSENSUS_WALDIR=/var/hyperledger/fabric/data/orderer1/etcdraft/wal 20 | ORDERER_CONSENSUS_SNAPDIR=/var/hyperledger/fabric/data/orderer1/etcdraft/wal 21 | ORDERER_OPERATIONS_LISTENADDRESS=0.0.0.0:8443 22 | ORDERER_ADMIN_LISTENADDRESS=0.0.0.0:9443 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v0/orderer2.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info 8 | ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 9 | ORDERER_GENERAL_LISTENPORT=6050 10 | ORDERER_GENERAL_LOCALMSPID=OrdererMSP 11 | ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/msp 12 | ORDERER_GENERAL_TLS_ENABLED=true 13 | ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.key 14 | ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt 15 | ORDERER_GENERAL_TLS_ROOTCAS=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/ca.crt 16 | ORDERER_GENERAL_BOOTSTRAPMETHOD=file 17 | ORDERER_GENERAL_BOOTSTRAPFILE=/var/hyperledger/fabric/channel-artifacts/genesis.block 18 | ORDERER_FILELEDGER_LOCATION=/var/hyperledger/fabric/data/orderer2 19 | ORDERER_CONSENSUS_WALDIR=/var/hyperledger/fabric/data/orderer2/etcdraft/wal 20 | ORDERER_CONSENSUS_SNAPDIR=/var/hyperledger/fabric/data/orderer2/etcdraft/wal 21 | ORDERER_OPERATIONS_LISTENADDRESS=0.0.0.0:8443 22 | ORDERER_ADMIN_LISTENADDRESS=0.0.0.0:9443 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v0/orderer3.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info 8 | ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 9 | ORDERER_GENERAL_LISTENPORT=6050 10 | ORDERER_GENERAL_LOCALMSPID=OrdererMSP 11 | ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/msp 12 | ORDERER_GENERAL_TLS_ENABLED=true 13 | ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.key 14 | ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.crt 15 | ORDERER_GENERAL_TLS_ROOTCAS=/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/ca.crt 16 | ORDERER_GENERAL_BOOTSTRAPMETHOD=file 17 | ORDERER_GENERAL_BOOTSTRAPFILE=/var/hyperledger/fabric/channel-artifacts/genesis.block 18 | ORDERER_FILELEDGER_LOCATION=/var/hyperledger/fabric/data/orderer3 19 | ORDERER_CONSENSUS_WALDIR=/var/hyperledger/fabric/data/orderer3/etcdraft/wal 20 | ORDERER_CONSENSUS_SNAPDIR=/var/hyperledger/fabric/data/orderer3/etcdraft/wal 21 | ORDERER_OPERATIONS_LISTENADDRESS=0.0.0.0:8443 22 | ORDERER_ADMIN_LISTENADDRESS=0.0.0.0:9443 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v0/org1-peer1.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info 8 | CORE_PEER_TLS_ENABLED=true 9 | CORE_PEER_TLS_CERT_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/server.crt 10 | CORE_PEER_TLS_KEY_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/server.key 11 | CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/ca.crt 12 | CORE_PEER_ID=org1-peer1.org1.example.com 13 | CORE_PEER_ADDRESS=org1-peer1:7051 14 | CORE_PEER_LISTENADDRESS=0.0.0.0:7051 15 | CORE_PEER_CHAINCODEADDRESS=org1-peer1:7052 16 | CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 17 | CORE_PEER_GOSSIP_BOOTSTRAP=org1-peer2:7051 18 | CORE_PEER_GOSSIP_EXTERNALENDPOINT=org1-peer1:7051 19 | CORE_PEER_LOCALMSPID=Org1MSP 20 | CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/msp 21 | CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 22 | CORE_PEER_FILESYSTEMPATH=/var/hyperledger/fabric/data/org1-peer1.org1.example.com 23 | CORE_LEDGER_SNAPSHOTS_ROOTDIR=/var/hyperledger/fabric/data/org1-peer1.org1.example.com/snapshots -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v0/org1-peer2.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info 8 | CORE_PEER_TLS_ENABLED=true 9 | CORE_PEER_TLS_CERT_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/tls/server.crt 10 | CORE_PEER_TLS_KEY_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/tls/server.key 11 | CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/tls/ca.crt 12 | CORE_PEER_ID=org1-peer2.org1.example.com 13 | CORE_PEER_ADDRESS=org1-peer2:7051 14 | CORE_PEER_LISTENADDRESS=0.0.0.0:7051 15 | CORE_PEER_CHAINCODEADDRESS=org1-peer2:7052 16 | CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 17 | CORE_PEER_GOSSIP_BOOTSTRAP=org1-peer2:7051 18 | CORE_PEER_GOSSIP_EXTERNALENDPOINT=org1-peer2:7051 19 | CORE_PEER_LOCALMSPID=Org1MSP 20 | CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/msp 21 | CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 22 | CORE_PEER_FILESYSTEMPATH=/var/hyperledger/fabric/data/org1-peer2.org1.example.com 23 | CORE_LEDGER_SNAPSHOTS_ROOTDIR=/var/hyperledger/fabric/data/org1-peer2.org1.example.com/snapshots -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v0/org2-peer1.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info 8 | CORE_PEER_TLS_ENABLED=true 9 | CORE_PEER_TLS_CERT_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/tls/server.crt 10 | CORE_PEER_TLS_KEY_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/tls/server.key 11 | CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/tls/ca.crt 12 | CORE_PEER_ID=org2-peer1.org2.example.com 13 | CORE_PEER_ADDRESS=org2-peer1:7051 14 | CORE_PEER_LISTENADDRESS=0.0.0.0:7051 15 | CORE_PEER_CHAINCODEADDRESS=org2-peer1:7052 16 | CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 17 | CORE_PEER_GOSSIP_BOOTSTRAP=org2-peer2:7051 18 | CORE_PEER_GOSSIP_EXTERNALENDPOINT=org2-peer1:7051 19 | CORE_PEER_LOCALMSPID=Org2MSP 20 | CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/msp 21 | CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 22 | CORE_PEER_FILESYSTEMPATH=/var/hyperledger/fabric/data/org2-peer1.org2.example.com 23 | CORE_LEDGER_SNAPSHOTS_ROOTDIR=/var/hyperledger/fabric/data/org2-peer1.org2.example.com/snapshots -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/config/v0/org2-peer2.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | FABRIC_CFG_PATH=/var/hyperledger/fabric/config 7 | FABRIC_LOGGING_SPEC=debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info 8 | CORE_PEER_TLS_ENABLED=true 9 | CORE_PEER_TLS_CERT_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/tls/server.crt 10 | CORE_PEER_TLS_KEY_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/tls/server.key 11 | CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/tls/ca.crt 12 | CORE_PEER_ID=org2-peer2.org2.example.com 13 | CORE_PEER_ADDRESS=org2-peer2:7051 14 | CORE_PEER_LISTENADDRESS=0.0.0.0:7051 15 | CORE_PEER_CHAINCODEADDRESS=org2-peer2:7052 16 | CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 17 | CORE_PEER_GOSSIP_BOOTSTRAP=org2-peer2:7051 18 | CORE_PEER_GOSSIP_EXTERNALENDPOINT=org2-peer2:7051 19 | CORE_PEER_LOCALMSPID=Org2MSP 20 | CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/msp 21 | CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9443 22 | CORE_PEER_FILESYSTEMPATH=/var/hyperledger/fabric/data/org2-peer2.org2.example.com 23 | CORE_LEDGER_SNAPSHOTS_ROOTDIR=/var/hyperledger/fabric/data/org2-peer2.org2.example.com/snapshots -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/job-update-org1-anchor-peers.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: batch/v1 8 | kind: Job 9 | metadata: 10 | namespace: test-network 11 | generateName: update-org1-anchor-peers- 12 | spec: 13 | backoffLimit: 1 14 | template: 15 | metadata: 16 | name: update-org1-anchor-peers 17 | spec: 18 | restartPolicy: "Never" 19 | containers: 20 | - name: main 21 | image: hyperledger/fabric-tools:2.3.2 22 | # todo: latest tag is not at docker hub 23 | imagePullPolicy: Always 24 | env: 25 | - name: FABRIC_CFG_PATH 26 | value: /var/hyperledger/fabric 27 | command: [ 28 | "configtxgen", 29 | "-profile", "TwoOrgsChannel", 30 | "-outputAnchorPeersUpdate", "/var/hyperledger/fabric/channel-artifacts/Org1MSPanchors.tx", 31 | "-channelID", "mychannel", 32 | "-asOrg", "Org1MSP", 33 | ] 34 | volumeMounts: 35 | - name: fabric-volume 36 | mountPath: /var/hyperledger/fabric 37 | - name: config-volume 38 | mountPath: /var/hyperledger/fabric/configtx.yaml 39 | subPath: configtx.yaml 40 | volumes: 41 | - name: fabric-volume 42 | persistentVolumeClaim: 43 | claimName: fabric 44 | - name: config-volume 45 | configMap: 46 | name: fabric-config -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/job-update-org2-anchor-peers.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: batch/v1 8 | kind: Job 9 | metadata: 10 | namespace: test-network 11 | generateName: update-org2-anchor-peers- 12 | spec: 13 | backoffLimit: 1 14 | template: 15 | metadata: 16 | name: update-org2-anchor-peers 17 | spec: 18 | restartPolicy: "Never" 19 | containers: 20 | - name: main 21 | image: hyperledger/fabric-tools:2.3.2 22 | # todo: latest tag is not at docker hub 23 | imagePullPolicy: Always 24 | env: 25 | - name: FABRIC_CFG_PATH 26 | value: /var/hyperledger/fabric 27 | command: [ 28 | "configtxgen", 29 | "-profile", "TwoOrgsChannel", 30 | "-outputAnchorPeersUpdate", "/var/hyperledger/fabric/channel-artifacts/Org2MSPanchors.tx", 31 | "-channelID", "mychannel", 32 | "-asOrg", "Org2MSP", 33 | ] 34 | volumeMounts: 35 | - name: fabric-volume 36 | mountPath: /var/hyperledger/fabric 37 | - name: config-volume 38 | mountPath: /var/hyperledger/fabric/configtx.yaml 39 | subPath: configtx.yaml 40 | volumes: 41 | - name: fabric-volume 42 | persistentVolumeClaim: 43 | claimName: fabric 44 | - name: config-volume 45 | configMap: 46 | name: fabric-config 47 | -------------------------------------------------------------------------------- /fabctl-sandbox/config/crypto-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. All Rights Reserved. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | # --------------------------------------------------------------------------- 7 | # "OrdererOrgs" - Definition of organizations managing orderer nodes 8 | # --------------------------------------------------------------------------- 9 | OrdererOrgs: 10 | - Name: Orderer 11 | Domain: example.com 12 | EnableNodeOUs: true 13 | 14 | Specs: 15 | - Hostname: orderer1 16 | SANS: 17 | - 0.0.0.0 18 | - Hostname: orderer2 19 | SANS: 20 | - 0.0.0.0 21 | - Hostname: orderer3 22 | SANS: 23 | - 0.0.0.0 24 | - Hostname: orderer4 25 | SANS: 26 | - 0.0.0.0 27 | - Hostname: orderer5 28 | SANS: 29 | - 0.0.0.0 30 | 31 | # --------------------------------------------------------------------------- 32 | # "PeerOrgs" - Definition of organizations managing peer nodes 33 | # --------------------------------------------------------------------------- 34 | PeerOrgs: 35 | - Name: Org1 36 | Domain: org1.example.com 37 | EnableNodeOUs: true 38 | Specs: 39 | - Hostname: org1-peer1 40 | SANS: 41 | - 0.0.0.0 42 | - Hostname: org1-peer2 43 | SANS: 44 | - 0.0.0.0 45 | Users: 46 | Count: 1 47 | 48 | - Name: Org2 49 | Domain: org2.example.com 50 | EnableNodeOUs: true 51 | Specs: 52 | - Hostname: org2-peer1 53 | SANS: 54 | - 0.0.0.0 55 | - Hostname: org2-peer2 56 | SANS: 57 | - 0.0.0.0 58 | Users: 59 | Count: 1 60 | -------------------------------------------------------------------------------- /fabctl-sandbox/config/v0/crypto-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. All Rights Reserved. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | # --------------------------------------------------------------------------- 7 | # "OrdererOrgs" - Definition of organizations managing orderer nodes 8 | # --------------------------------------------------------------------------- 9 | OrdererOrgs: 10 | - Name: Orderer 11 | Domain: example.com 12 | EnableNodeOUs: true 13 | 14 | Specs: 15 | - Hostname: orderer1 16 | SANS: 17 | - 0.0.0.0 18 | - Hostname: orderer2 19 | SANS: 20 | - 0.0.0.0 21 | - Hostname: orderer3 22 | SANS: 23 | - 0.0.0.0 24 | - Hostname: orderer4 25 | SANS: 26 | - 0.0.0.0 27 | - Hostname: orderer5 28 | SANS: 29 | - 0.0.0.0 30 | 31 | # --------------------------------------------------------------------------- 32 | # "PeerOrgs" - Definition of organizations managing peer nodes 33 | # --------------------------------------------------------------------------- 34 | PeerOrgs: 35 | - Name: Org1 36 | Domain: org1.example.com 37 | EnableNodeOUs: true 38 | Specs: 39 | - Hostname: org1-peer1 40 | SANS: 41 | - 0.0.0.0 42 | - Hostname: org1-peer2 43 | SANS: 44 | - 0.0.0.0 45 | Users: 46 | Count: 1 47 | 48 | - Name: Org2 49 | Domain: org2.example.com 50 | EnableNodeOUs: true 51 | Specs: 52 | - Hostname: org2-peer1 53 | SANS: 54 | - 0.0.0.0 55 | - Hostname: org2-peer2 56 | SANS: 57 | - 0.0.0.0 58 | Users: 59 | Count: 1 60 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v1/BootstrapNetworkCATest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1; 7 | 8 | import org.hyperledger.fabric.fabctl.v1.network.NetworkConfig; 9 | import org.hyperledger.fabric.fabctl.v1.network.OrganizationConfig; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | import static org.junit.jupiter.api.Assertions.assertNotNull; 14 | 15 | /** 16 | * Goodbye, cryptogen. Hello, CAs. 17 | * 18 | * This imparts a little complexity as fabctl now needs to interact with the CA in order to enroll 19 | * the MSPs in the network. We can certainly set up an ingress into the CAs such that the systems 20 | * can be reached via http / https via an external URL, but that somewhat defeats the purpose of 21 | * trying to interact ENTIRELY through the kube API controller. 22 | * 23 | * On the other hand, if the enrollments occur within the cluster (e.g. running as a Job), then 24 | * network access is easy but we won't have visibility to the MSP assets on the local host. 25 | * 26 | * Here are a few ideas to resolve the above conflict: 27 | * 28 | * - leave the MSP assets in the cluster and run enrollments as k8s Jobs 29 | * 30 | * - expose an HTTP ingress into the CAs, running the fabric-ca-client (or HTTP / REST) calls locally. 31 | * 32 | * - Allow fabctl to provision a short-lived port-forward directly to the CA services. 33 | * 34 | */ 35 | public class BootstrapNetworkCATest extends TestBase 36 | { 37 | private final NetworkConfig testNetwork = new TestNetwork(); 38 | 39 | @Test 40 | public void testAllOrgsHaveCA() 41 | { 42 | int count = 0; 43 | 44 | for (OrganizationConfig org : testNetwork.organizations) 45 | { 46 | // assertNotNull(org.ecertCA, "for org " + org.getName()); 47 | count++; 48 | } 49 | 50 | assertEquals(3, count); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v1/msp/MSPDescriptor.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1.msp; 7 | 8 | import com.fasterxml.jackson.databind.JsonNode; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.fasterxml.jackson.databind.node.ObjectNode; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.nio.charset.Charset; 15 | import lombok.Data; 16 | import org.apache.commons.compress.utils.IOUtils; 17 | 18 | /** 19 | * An MSP descriptor is a json / yaml rendering of an MSP folder structure 20 | * 21 | * Still stirring the pot. Nothing final in here... 22 | */ 23 | @Data 24 | public class MSPDescriptor 25 | { 26 | private static final ObjectMapper objectMapper = new ObjectMapper(); 27 | 28 | public final String name; 29 | public final String id; 30 | public final JsonNode msp; 31 | public final JsonNode tls; 32 | 33 | public MSPDescriptor(final String name, final File basedir) throws IOException 34 | { 35 | this.name = name; 36 | this.id = basedir.getName(); 37 | this.msp = helper(new File(basedir, "msp")); 38 | this.tls = helper(new File(basedir, "tls")); 39 | } 40 | 41 | private JsonNode helper(final File f) throws IOException 42 | { 43 | if (! f.exists()) 44 | { 45 | // e.g.. no 'tls' folder under root folder. 46 | return null; 47 | } 48 | else if (f.isDirectory()) 49 | { 50 | final ObjectNode node = objectMapper.createObjectNode(); 51 | 52 | for (File child : f.listFiles()) 53 | { 54 | node.set(child.getName(), helper(child)); 55 | } 56 | 57 | return node; 58 | } 59 | else 60 | { 61 | // copy file contents directly as a string. maybe need some b64 encoding here? 62 | try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) 63 | { 64 | IOUtils.copy(f, baos); 65 | return objectMapper.getNodeFactory().textNode(baos.toString(Charset.defaultCharset())); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /fabctl-sandbox/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /fabctl-msp-unfurler/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v1/ConfigTXReaderTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1; 7 | 8 | import com.fasterxml.jackson.databind.JsonNode; 9 | import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; 10 | import java.io.File; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.junit.jupiter.api.Test; 13 | 14 | /** 15 | * configtx.yaml defines, in broad strokes, the topology of a Fabric network sufficient to generate the 16 | * genesis blocks on a system channel or orderer admin. 17 | * 18 | * In network.sh there is a need to bring up a network, which has been hacked in these tests as a quick 19 | * data structure specifying a hierarchy of orderers, peers, and their relevant context (k/v env) for 20 | * launching within a pod/deployment in k8s. 21 | * 22 | * What would be ideal is if the fabctl system had thet ability to specify multi-organization fabric 23 | * networks in a holistic fashion. I.e.. rather than have a single "network descriptor" document, perhaps 24 | * we should be focusing on the mechanics of automating the workflow for a single organization, then 25 | * expand outward to apply the single-org mechanics across a set of org descriptors. Effectively the 26 | * "network descriptor" becomes: 27 | * 28 | * --- 29 | * network: 30 | * organizations: 31 | * - orgRef: 32 | * id: org1.example.com 33 | * - orgRef: 34 | * id: org2.example.com 35 | * - orgRef: 36 | * id: orderer.example.com 37 | * 38 | * --- 39 | * organization: 40 | * metadata: 41 | * id: org1.example.com 42 | * name: org1 43 | * ... 44 | * peers: 45 | * - name: org1-peer1 46 | * id: org1-peer1.org1.example.com 47 | * ... 48 | * - name: org1-peer2 49 | * id: org1-peer2.org1.example.com 50 | * 51 | * --- 52 | * organization: 53 | * metadata: 54 | * id: org2.example.com 55 | * name: org2 56 | * ... 57 | * peers: 58 | * - name: org2-peer1 59 | * url: peer1:7070 60 | * ... 61 | * 62 | * --- 63 | * organization: 64 | * metadata: 65 | * id: orderer.example.com 66 | * name: org3 67 | * orderers: 68 | * - name: orderer1 69 | * url: orderer1.orderer.example.com:6050 70 | * - name: orderer2 71 | * url: orderer2.orderer.example.com:6050 72 | * - name: orderer3 73 | * url: orderer3.orderer.example.com:6050 74 | */ 75 | @Slf4j 76 | public class ConfigTXReaderTest 77 | { 78 | private static final YAMLMapper yamlMapper = new YAMLMapper(); 79 | 80 | private static final File FIXTURES_DIR = new File("src/test/resources/fixtures"); 81 | 82 | /** 83 | * Just a warm-up. Can we even read the configtx.yaml? 84 | */ 85 | @Test 86 | public void testReadConfigTXYAML() throws Exception 87 | { 88 | final File configtxFile = new File(FIXTURES_DIR, "configtx.yaml"); 89 | final JsonNode config = yamlMapper.readTree(configtxFile); 90 | 91 | log.info("read config:\n{}", yamlMapper.writeValueAsString(config)); 92 | 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/org1-peer1.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | namespace: test-network 11 | name: org1-peer1-config 12 | data: 13 | FABRIC_CFG_PATH: /var/hyperledger/fabric/config 14 | FABRIC_LOGGING_SPEC: "debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info" 15 | CORE_PEER_TLS_ENABLED: "true" 16 | CORE_PEER_TLS_CERT_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/server.crt 17 | CORE_PEER_TLS_KEY_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/server.key 18 | CORE_PEER_TLS_ROOTCERT_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/ca.crt 19 | CORE_PEER_ID: org1-peer1.org1.example.com 20 | CORE_PEER_ADDRESS: org1-peer1:7051 21 | CORE_PEER_LISTENADDRESS: 0.0.0.0:7051 22 | CORE_PEER_CHAINCODEADDRESS: org1-peer1:7052 23 | CORE_PEER_CHAINCODELISTENADDRESS: 0.0.0.0:7052 24 | # bootstrap peer is the other peer in the same org 25 | CORE_PEER_GOSSIP_BOOTSTRAP: org1-peer2:7051 26 | CORE_PEER_GOSSIP_EXTERNALENDPOINT: org1-peer1:7051 27 | CORE_PEER_LOCALMSPID: Org1MSP 28 | CORE_PEER_MSPCONFIGPATH: /var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/msp 29 | CORE_OPERATIONS_LISTENADDRESS: 0.0.0.0:9443 30 | CORE_PEER_FILESYSTEMPATH: /var/hyperledger/fabric/data/org1-peer1.org1.example.com 31 | CORE_LEDGER_SNAPSHOTS_ROOTDIR: /var/hyperledger/fabric/data/org1-peer1.org1.example.com/snapshots 32 | 33 | --- 34 | apiVersion: apps/v1 35 | kind: Deployment 36 | metadata: 37 | namespace: test-network 38 | name: org1-peer1 39 | spec: 40 | replicas: 1 41 | selector: 42 | matchLabels: 43 | app: org1-peer1 44 | template: 45 | metadata: 46 | labels: 47 | app: org1-peer1 48 | spec: 49 | containers: 50 | - name: main 51 | image: hyperledger/fabric-peer:2.3.2 52 | imagePullPolicy: Always 53 | envFrom: 54 | - configMapRef: 55 | name: org1-peer1-config 56 | ports: 57 | - containerPort: 7051 58 | - containerPort: 7052 59 | - containerPort: 9443 60 | volumeMounts: 61 | - name: fabric-volume 62 | mountPath: /var/hyperledger/fabric 63 | - name: fabric-config 64 | mountPath: /var/hyperledger/fabric/config 65 | volumes: 66 | - name: fabric-volume 67 | persistentVolumeClaim: 68 | claimName: fabric 69 | - name: fabric-config 70 | configMap: 71 | name: fabric-config 72 | 73 | --- 74 | apiVersion: v1 75 | kind: Service 76 | metadata: 77 | namespace: test-network 78 | name: org1-peer1 79 | spec: 80 | ports: 81 | - name: gossip 82 | port: 7051 83 | protocol: TCP 84 | - name: chaincode 85 | port: 7052 86 | protocol: TCP 87 | - name: operations 88 | port: 9443 89 | protocol: TCP 90 | selector: 91 | app: org1-peer1 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/org1-peer2.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | namespace: test-network 11 | name: org1-peer2-config 12 | data: 13 | FABRIC_CFG_PATH: /var/hyperledger/fabric/config 14 | FABRIC_LOGGING_SPEC: "debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info" 15 | CORE_PEER_TLS_ENABLED: "true" 16 | CORE_PEER_TLS_CERT_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/tls/server.crt 17 | CORE_PEER_TLS_KEY_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/tls/server.key 18 | CORE_PEER_TLS_ROOTCERT_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/tls/ca.crt 19 | CORE_PEER_ID: org1-peer2.org1.example.com 20 | CORE_PEER_ADDRESS: org1-peer2:7051 21 | CORE_PEER_LISTENADDRESS: 0.0.0.0:7051 22 | CORE_PEER_CHAINCODEADDRESS: org1-peer2:7052 23 | CORE_PEER_CHAINCODELISTENADDRESS: 0.0.0.0:7052 24 | # bootstrap peer is the other peer in the same org 25 | CORE_PEER_GOSSIP_BOOTSTRAP: org1-peer1:7051 26 | CORE_PEER_GOSSIP_EXTERNALENDPOINT: org1-peer2:7051 27 | CORE_PEER_LOCALMSPID: Org1MSP 28 | CORE_PEER_MSPCONFIGPATH: /var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/msp 29 | CORE_OPERATIONS_LISTENADDRESS: 0.0.0.0:9443 30 | CORE_PEER_FILESYSTEMPATH: /var/hyperledger/fabric/data/org1-peer2.org1.example.com 31 | CORE_LEDGER_SNAPSHOTS_ROOTDIR: /var/hyperledger/fabric/data/org1-peer2.org1.example.com/snapshots 32 | 33 | --- 34 | apiVersion: apps/v1 35 | kind: Deployment 36 | metadata: 37 | namespace: test-network 38 | name: org1-peer2 39 | spec: 40 | replicas: 1 41 | selector: 42 | matchLabels: 43 | app: org1-peer2 44 | template: 45 | metadata: 46 | labels: 47 | app: org1-peer2 48 | spec: 49 | containers: 50 | - name: main 51 | image: hyperledger/fabric-peer:2.3.2 52 | imagePullPolicy: Always 53 | envFrom: 54 | - configMapRef: 55 | name: org1-peer2-config 56 | ports: 57 | - containerPort: 7051 58 | - containerPort: 7052 59 | - containerPort: 9443 60 | volumeMounts: 61 | - name: fabric-volume 62 | mountPath: /var/hyperledger/fabric 63 | - name: fabric-config 64 | mountPath: /var/hyperledger/fabric/config 65 | volumes: 66 | - name: fabric-volume 67 | persistentVolumeClaim: 68 | claimName: fabric 69 | - name: fabric-config 70 | configMap: 71 | name: fabric-config 72 | 73 | --- 74 | apiVersion: v1 75 | kind: Service 76 | metadata: 77 | namespace: test-network 78 | name: org1-peer2 79 | spec: 80 | ports: 81 | - name: gossip 82 | port: 7051 83 | protocol: TCP 84 | - name: chaincode 85 | port: 7052 86 | protocol: TCP 87 | - name: operations 88 | port: 9443 89 | protocol: TCP 90 | selector: 91 | app: org1-peer2 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/org2-peer1.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | namespace: test-network 11 | name: org2-peer1-config 12 | data: 13 | FABRIC_CFG_PATH: /var/hyperledger/fabric/config 14 | FABRIC_LOGGING_SPEC: "debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info" 15 | CORE_PEER_TLS_ENABLED: "true" 16 | CORE_PEER_TLS_CERT_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/tls/server.crt 17 | CORE_PEER_TLS_KEY_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/tls/server.key 18 | CORE_PEER_TLS_ROOTCERT_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/tls/ca.crt 19 | CORE_PEER_ID: org2-peer1.org2.example.com 20 | CORE_PEER_ADDRESS: org2-peer1:7051 21 | CORE_PEER_LISTENADDRESS: 0.0.0.0:7051 22 | CORE_PEER_CHAINCODEADDRESS: org2-peer1:7052 23 | CORE_PEER_CHAINCODELISTENADDRESS: 0.0.0.0:7052 24 | # bootstrap peer is the other peer in the same org 25 | CORE_PEER_GOSSIP_BOOTSTRAP: org2-peer2:7051 26 | CORE_PEER_GOSSIP_EXTERNALENDPOINT: org2-peer1:7051 27 | CORE_PEER_LOCALMSPID: Org2MSP 28 | CORE_PEER_MSPCONFIGPATH: /var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/msp 29 | CORE_OPERATIONS_LISTENADDRESS: 0.0.0.0:9443 30 | CORE_PEER_FILESYSTEMPATH: /var/hyperledger/fabric/data/org2-peer1.org2.example.com 31 | CORE_LEDGER_SNAPSHOTS_ROOTDIR: /var/hyperledger/fabric/data/org2-peer1.org2.example.com/snapshots 32 | 33 | --- 34 | apiVersion: apps/v1 35 | kind: Deployment 36 | metadata: 37 | namespace: test-network 38 | name: org2-peer1 39 | spec: 40 | replicas: 1 41 | selector: 42 | matchLabels: 43 | app: org2-peer1 44 | template: 45 | metadata: 46 | labels: 47 | app: org2-peer1 48 | spec: 49 | containers: 50 | - name: main 51 | image: hyperledger/fabric-peer:2.3.2 52 | imagePullPolicy: Always 53 | envFrom: 54 | - configMapRef: 55 | name: org2-peer1-config 56 | ports: 57 | - containerPort: 7051 58 | - containerPort: 7052 59 | - containerPort: 9443 60 | volumeMounts: 61 | - name: fabric-volume 62 | mountPath: /var/hyperledger/fabric 63 | - name: fabric-config 64 | mountPath: /var/hyperledger/fabric/config 65 | volumes: 66 | - name: fabric-volume 67 | persistentVolumeClaim: 68 | claimName: fabric 69 | - name: fabric-config 70 | configMap: 71 | name: fabric-config 72 | 73 | --- 74 | apiVersion: v1 75 | kind: Service 76 | metadata: 77 | namespace: test-network 78 | name: org2-peer1 79 | spec: 80 | ports: 81 | - name: gossip 82 | port: 7051 83 | protocol: TCP 84 | - name: chaincode 85 | port: 7052 86 | protocol: TCP 87 | - name: operations 88 | port: 9443 89 | protocol: TCP 90 | selector: 91 | app: org2-peer1 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/org2-peer2.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | namespace: test-network 11 | name: org2-peer2-config 12 | data: 13 | FABRIC_CFG_PATH: /var/hyperledger/fabric/config 14 | FABRIC_LOGGING_SPEC: "debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info" 15 | CORE_PEER_TLS_ENABLED: "true" 16 | CORE_PEER_TLS_CERT_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/tls/server.crt 17 | CORE_PEER_TLS_KEY_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/tls/server.key 18 | CORE_PEER_TLS_ROOTCERT_FILE: /var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/tls/ca.crt 19 | CORE_PEER_ID: org2-peer2.org2.example.com 20 | CORE_PEER_ADDRESS: org2-peer2:7051 21 | CORE_PEER_LISTENADDRESS: 0.0.0.0:7051 22 | CORE_PEER_CHAINCODEADDRESS: org2-peer2:7052 23 | CORE_PEER_CHAINCODELISTENADDRESS: 0.0.0.0:7052 24 | # bootstrap peer is the other peer in the same org 25 | CORE_PEER_GOSSIP_BOOTSTRAP: org2-peer1:7051 26 | CORE_PEER_GOSSIP_EXTERNALENDPOINT: org2-peer2:7051 27 | CORE_PEER_LOCALMSPID: Org2MSP 28 | CORE_PEER_MSPCONFIGPATH: /var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/msp 29 | CORE_OPERATIONS_LISTENADDRESS: 0.0.0.0:9443 30 | CORE_PEER_FILESYSTEMPATH: /var/hyperledger/fabric/data/org2-peer2.org2.example.com 31 | CORE_LEDGER_SNAPSHOTS_ROOTDIR: /var/hyperledger/fabric/data/org2-peer2.org2.example.com/snapshots 32 | 33 | --- 34 | apiVersion: apps/v1 35 | kind: Deployment 36 | metadata: 37 | namespace: test-network 38 | name: org2-peer2 39 | spec: 40 | replicas: 1 41 | selector: 42 | matchLabels: 43 | app: org2-peer2 44 | template: 45 | metadata: 46 | labels: 47 | app: org2-peer2 48 | spec: 49 | containers: 50 | - name: main 51 | image: hyperledger/fabric-peer:2.3.2 52 | imagePullPolicy: Always 53 | envFrom: 54 | - configMapRef: 55 | name: org2-peer2-config 56 | ports: 57 | - containerPort: 7051 58 | - containerPort: 7052 59 | - containerPort: 9443 60 | volumeMounts: 61 | - name: fabric-volume 62 | mountPath: /var/hyperledger/fabric 63 | - name: fabric-config 64 | mountPath: /var/hyperledger/fabric/config 65 | volumes: 66 | - name: fabric-volume 67 | persistentVolumeClaim: 68 | claimName: fabric 69 | - name: fabric-config 70 | configMap: 71 | name: fabric-config 72 | 73 | --- 74 | apiVersion: v1 75 | kind: Service 76 | metadata: 77 | namespace: test-network 78 | name: org2-peer2 79 | spec: 80 | ports: 81 | - name: gossip 82 | port: 7051 83 | protocol: TCP 84 | - name: chaincode 85 | port: 7052 86 | protocol: TCP 87 | - name: operations 88 | port: 9443 89 | protocol: TCP 90 | selector: 91 | app: org2-peer2 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/orderer1.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | namespace: test-network 11 | name: orderer1-config 12 | data: 13 | FABRIC_CFG_PATH: /var/hyperledger/fabric/config 14 | FABRIC_LOGGING_SPEC: "debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info" 15 | ORDERER_GENERAL_LISTENADDRESS: "0.0.0.0" 16 | ORDERER_GENERAL_LISTENPORT: "6050" 17 | ORDERER_GENERAL_LOCALMSPID: OrdererMSP 18 | ORDERER_GENERAL_LOCALMSPDIR: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/msp 19 | ORDERER_GENERAL_TLS_ENABLED: "true" 20 | ORDERER_GENERAL_TLS_PRIVATEKEY: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.key 21 | ORDERER_GENERAL_TLS_CERTIFICATE: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.crt 22 | # following setting is not really needed at runtime since channel config has ca root certs, but we need to override the default in orderer.yaml 23 | ORDERER_GENERAL_TLS_ROOTCAS: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt 24 | ORDERER_GENERAL_BOOTSTRAPMETHOD: file 25 | ORDERER_GENERAL_BOOTSTRAPFILE: /var/hyperledger/fabric/channel-artifacts/genesis.block 26 | ORDERER_FILELEDGER_LOCATION: /var/hyperledger/fabric/data/orderer1 27 | ORDERER_CONSENSUS_WALDIR: /var/hyperledger/fabric/data/orderer1/etcdraft/wal 28 | ORDERER_CONSENSUS_SNAPDIR: /var/hyperledger/fabric/data/orderer1/etcdraft/wal 29 | ORDERER_OPERATIONS_LISTENADDRESS: "0.0.0.0:8443" 30 | ORDERER_ADMIN_LISTENADDRESS: "0.0.0.0:9443" 31 | 32 | --- 33 | apiVersion: apps/v1 34 | kind: Deployment 35 | metadata: 36 | namespace: test-network 37 | name: orderer1 38 | spec: 39 | replicas: 1 40 | selector: 41 | matchLabels: 42 | app: orderer1 43 | template: 44 | metadata: 45 | labels: 46 | app: orderer1 47 | spec: 48 | containers: 49 | - name: main 50 | image: hyperledger/fabric-orderer:2.3.2 51 | imagePullPolicy: Always 52 | envFrom: 53 | - configMapRef: 54 | name: orderer1-config 55 | ports: 56 | - containerPort: 6050 57 | - containerPort: 8443 58 | - containerPort: 9443 59 | volumeMounts: 60 | - name: fabric-volume 61 | mountPath: /var/hyperledger/fabric 62 | - name: fabric-config 63 | mountPath: /var/hyperledger/fabric/config 64 | volumes: 65 | - name: fabric-volume 66 | persistentVolumeClaim: 67 | claimName: fabric 68 | - name: fabric-config 69 | configMap: 70 | name: fabric-config 71 | 72 | --- 73 | apiVersion: v1 74 | kind: Service 75 | metadata: 76 | namespace: test-network 77 | name: orderer1 78 | spec: 79 | ports: 80 | - name: general 81 | port: 6050 82 | protocol: TCP 83 | - name: operations 84 | port: 8443 85 | protocol: TCP 86 | - name: admin 87 | port: 9443 88 | protocol: TCP 89 | selector: 90 | app: orderer1 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/orderer2.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | namespace: test-network 11 | name: orderer2-config 12 | data: 13 | FABRIC_CFG_PATH: /var/hyperledger/fabric/config 14 | FABRIC_LOGGING_SPEC: "debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info" 15 | ORDERER_GENERAL_LISTENADDRESS: "0.0.0.0" 16 | ORDERER_GENERAL_LISTENPORT: "6050" 17 | ORDERER_GENERAL_LOCALMSPID: OrdererMSP 18 | ORDERER_GENERAL_LOCALMSPDIR: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/msp 19 | ORDERER_GENERAL_TLS_ENABLED: "true" 20 | ORDERER_GENERAL_TLS_PRIVATEKEY: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.key 21 | ORDERER_GENERAL_TLS_CERTIFICATE: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt 22 | # following setting is not really needed at runtime since channel config has ca root certs, but we need to override the default in orderer.yaml 23 | ORDERER_GENERAL_TLS_ROOTCAS: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/ca.crt 24 | ORDERER_GENERAL_BOOTSTRAPMETHOD: file 25 | ORDERER_GENERAL_BOOTSTRAPFILE: /var/hyperledger/fabric/channel-artifacts/genesis.block 26 | ORDERER_FILELEDGER_LOCATION: /var/hyperledger/fabric/data/orderer2 27 | ORDERER_CONSENSUS_WALDIR: /var/hyperledger/fabric/data/orderer2/etcdraft/wal 28 | ORDERER_CONSENSUS_SNAPDIR: /var/hyperledger/fabric/data/orderer2/etcdraft/wal 29 | ORDERER_OPERATIONS_LISTENADDRESS: "0.0.0.0:8443" 30 | ORDERER_ADMIN_LISTENADDRESS: "0.0.0.0:9443" 31 | 32 | --- 33 | apiVersion: apps/v1 34 | kind: Deployment 35 | metadata: 36 | namespace: test-network 37 | name: orderer2 38 | spec: 39 | replicas: 1 40 | selector: 41 | matchLabels: 42 | app: orderer2 43 | template: 44 | metadata: 45 | labels: 46 | app: orderer2 47 | spec: 48 | containers: 49 | - name: main 50 | image: hyperledger/fabric-orderer:2.3.2 51 | imagePullPolicy: Always 52 | envFrom: 53 | - configMapRef: 54 | name: orderer2-config 55 | ports: 56 | - containerPort: 6050 57 | - containerPort: 8443 58 | - containerPort: 9443 59 | volumeMounts: 60 | - name: fabric-volume 61 | mountPath: /var/hyperledger/fabric 62 | - name: fabric-config 63 | mountPath: /var/hyperledger/fabric/config 64 | volumes: 65 | - name: fabric-volume 66 | persistentVolumeClaim: 67 | claimName: fabric 68 | - name: fabric-config 69 | configMap: 70 | name: fabric-config 71 | 72 | --- 73 | apiVersion: v1 74 | kind: Service 75 | metadata: 76 | namespace: test-network 77 | name: orderer2 78 | spec: 79 | ports: 80 | - name: general 81 | port: 6050 82 | protocol: TCP 83 | - name: operations 84 | port: 8443 85 | protocol: TCP 86 | - name: admin 87 | port: 9443 88 | protocol: TCP 89 | selector: 90 | app: orderer2 -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/kube/orderer3.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | --- 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | namespace: test-network 11 | name: orderer3-config 12 | data: 13 | FABRIC_CFG_PATH: /var/hyperledger/fabric/config 14 | FABRIC_LOGGING_SPEC: "debug:cauthdsl,policies,msp,common.configtx,common.channelconfig=info" 15 | ORDERER_GENERAL_LISTENADDRESS: "0.0.0.0" 16 | ORDERER_GENERAL_LISTENPORT: "6050" 17 | ORDERER_GENERAL_LOCALMSPID: OrdererMSP 18 | ORDERER_GENERAL_LOCALMSPDIR: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/msp 19 | ORDERER_GENERAL_TLS_ENABLED: "true" 20 | ORDERER_GENERAL_TLS_PRIVATEKEY: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.key 21 | ORDERER_GENERAL_TLS_CERTIFICATE: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.crt 22 | # following setting is not really needed at runtime since channel config has ca root certs, but we need to override the default in orderer.yaml 23 | ORDERER_GENERAL_TLS_ROOTCAS: /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/ca.crt 24 | ORDERER_GENERAL_BOOTSTRAPMETHOD: file 25 | ORDERER_GENERAL_BOOTSTRAPFILE: /var/hyperledger/fabric/channel-artifacts/genesis.block 26 | ORDERER_FILELEDGER_LOCATION: /var/hyperledger/fabric/data/orderer3 27 | ORDERER_CONSENSUS_WALDIR: /var/hyperledger/fabric/data/orderer3/etcdraft/wal 28 | ORDERER_CONSENSUS_SNAPDIR: /var/hyperledger/fabric/data/orderer3/etcdraft/wal 29 | ORDERER_OPERATIONS_LISTENADDRESS: "0.0.0.0:8443" 30 | ORDERER_ADMIN_LISTENADDRESS: "0.0.0.0:9443" 31 | 32 | --- 33 | apiVersion: apps/v1 34 | kind: Deployment 35 | metadata: 36 | namespace: test-network 37 | name: orderer3 38 | spec: 39 | replicas: 1 40 | selector: 41 | matchLabels: 42 | app: orderer3 43 | template: 44 | metadata: 45 | labels: 46 | app: orderer3 47 | spec: 48 | containers: 49 | - name: main 50 | image: hyperledger/fabric-orderer:2.3.2 51 | imagePullPolicy: Always 52 | envFrom: 53 | - configMapRef: 54 | name: orderer3-config 55 | ports: 56 | - containerPort: 6050 57 | - containerPort: 8443 58 | - containerPort: 9443 59 | volumeMounts: 60 | - name: fabric-volume 61 | mountPath: /var/hyperledger/fabric 62 | - name: fabric-config 63 | mountPath: /var/hyperledger/fabric/config 64 | volumes: 65 | - name: fabric-volume 66 | persistentVolumeClaim: 67 | claimName: fabric 68 | - name: fabric-config 69 | configMap: 70 | name: fabric-config 71 | 72 | --- 73 | apiVersion: v1 74 | kind: Service 75 | metadata: 76 | namespace: test-network 77 | name: orderer3 78 | spec: 79 | ports: 80 | - name: general 81 | port: 6050 82 | protocol: TCP 83 | - name: operations 84 | port: 8443 85 | protocol: TCP 86 | - name: admin 87 | port: 9443 88 | protocol: TCP 89 | selector: 90 | app: orderer3 -------------------------------------------------------------------------------- /fabctl-msp-unfurler/src/main/java/org/hyperledger/fabric/fabctl/msp/unfurler/Main.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.msp.unfurler; 7 | 8 | import com.fasterxml.jackson.databind.JsonNode; 9 | import com.fasterxml.jackson.databind.node.ObjectNode; 10 | import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; 11 | import java.io.File; 12 | import java.io.FileFilter; 13 | import java.io.FileOutputStream; 14 | import java.io.IOException; 15 | import java.nio.charset.Charset; 16 | import java.util.Iterator; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.apache.commons.io.IOUtils; 19 | import org.apache.commons.io.filefilter.WildcardFileFilter; 20 | 21 | /** 22 | * This is an incredibly rough prototype routine that will unfurl 23 | * fabric MSP descriptors into MSP Folder structures on disk. The intent 24 | * is that this entrypoint be set up as an init container on pods running 25 | * fabric binaries. 26 | */ 27 | @Slf4j 28 | public class Main 29 | { 30 | private static final YAMLMapper yamlMapper = new YAMLMapper(); 31 | 32 | /** 33 | * Really, really, really simple. Just enough to see if this scheme will work. 34 | */ 35 | public static void main(final String[] args) 36 | { 37 | final String inputFolder = System.getenv("INPUT_FOLDER"); 38 | final String outputFolder = System.getenv("OUTPUT_FOLDER"); 39 | 40 | log.info("Scanning {} for msp descriptors", inputFolder); 41 | log.info("Writing output MSP structures to {}", outputFolder); 42 | 43 | final File inputDir = new File(inputFolder); 44 | final File outputDir = new File(outputFolder); 45 | 46 | if (! inputDir.exists()) 47 | { 48 | log.error("input folder {} does not exist.", inputDir); 49 | System.exit(1); 50 | } 51 | 52 | // not realistic - just process any file ending in .yaml 53 | final FileFilter fileFilter = new WildcardFileFilter("msp-*.yaml"); 54 | 55 | for (File descriptor : inputDir.listFiles(fileFilter)) 56 | { 57 | try 58 | { 59 | unfurl(outputDir, descriptor); 60 | } 61 | catch (Exception ex) 62 | { 63 | log.error("Could not unfurl " + descriptor, ex); 64 | System.exit(1); 65 | } 66 | } 67 | 68 | System.exit(0); 69 | } 70 | 71 | private static void unfurl(final File outputDir, final File descriptor) 72 | throws IOException 73 | { 74 | final JsonNode node = yamlMapper.readTree(descriptor); 75 | // log.debug(yamlMapper.writeValueAsString(node)); // yaml contains secret spec - do not emit to stdout. 76 | 77 | final File mspDir = new File(outputDir, node.get("id").asText()); 78 | log.info("Unfurling {} --> {}", descriptor, mspDir); 79 | 80 | if (mspDir.exists()) 81 | { 82 | throw new RuntimeException("msp folder " + mspDir + " exists and will not be overwritten."); 83 | } 84 | 85 | unfurl(new File(mspDir, "msp"), node.get("msp")); 86 | unfurl(new File(mspDir, "tls"), node.get("tls")); 87 | } 88 | 89 | private static void unfurl(final File f, final JsonNode node) throws IOException 90 | { 91 | // no node? no unfurling! 92 | if (node == null || node.isNull()) 93 | { 94 | return; 95 | } 96 | 97 | if (node.isTextual()) 98 | { 99 | log.info(" {}", f); 100 | 101 | try (final FileOutputStream fos = new FileOutputStream(f)) 102 | { 103 | IOUtils.write(node.textValue(), fos, Charset.defaultCharset()); 104 | } 105 | } 106 | else if (node instanceof ObjectNode) 107 | { 108 | f.mkdirs(); 109 | 110 | final ObjectNode on = (ObjectNode) node; 111 | final Iterator i = on.fieldNames(); 112 | while (i.hasNext()) 113 | { 114 | final String field = i.next(); 115 | final JsonNode child = on.get(field); 116 | 117 | unfurl(new File(f, field), child); 118 | } 119 | } 120 | else 121 | { 122 | throw new RuntimeException("can not unfurl node " + node); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HYPER KUBE 2 | 3 | **Hyperkube** provides a collection of utilities, libraries, and configurations to operate Hyperleder Fabric as a 4 | _Kube-native_ platform. 5 | 6 | This project is **very new** and currently in genesis / experimental status. Contributions, feedback, and reviews are 7 | both welcome and encouraged. 8 | 9 | ## Overview: 10 | Hyperkube provides: 11 | 12 | - Reference patterns for deployment to provider-agnostic Kubernetes clusters. 13 | - Simplified operational practices for common fabric activities (network, channel, and chaincode lifecycle.) 14 | - A common, unified approach for configuration, connection profiles, and remote administration. 15 | - _Kube Native (but not Kube Only)_ operational practices. 16 | - A Fabric reference network, equivalent to [test-network](url), suitable for local application development. 17 | - A focused environment for development and management of [external chaincode](url) smart contracts. 18 | 19 | ![hyper-kube](images/hyper-kube.png) 20 | 21 | ## Objectives: 22 | 23 | - Build and maintain momentum for a migration from Docker (compose/swarm/virtualbox) to Kubernetes 24 | - Provide vendor-agnostic patterns for running Fabric on Kubernetes. 25 | - Study of fabric configuration : catalog for fragmented configuration files, utilities, and patterns. 26 | - Constrain / eliminate reliance on local Docker daemon (and docker-in-docker) to advance _chaincode as a service_ 27 | deployment practices. 28 | - Provide near-term value with local, CLI-based Kubernetes control interfaces. 29 | - Provide long-term value by aligning with the kube-native "Operator Pattern" 30 | 31 | ## Mechanics: 32 | 33 | Hyperkube provides the `fabctl` command for instantiating and manipulating hyperledger networks running somewhere "in 34 | the cloud." In traditional fabric networks, a network administrator is responsible for crafting a configuration, 35 | determining port mappings, managing crypto certificates and key specs, managing the lifecycle of application components, 36 | and running a series of `peer` CLI binaries to reflect updates and configuration across a collection of machines. 37 | This administration burden is tremendous! 38 | 39 | `fabctl` reduces the complexity of operations by providing a single entrypoint for common fabric objectives, communicating 40 | entirely with the Kubernetes API Controller to affect changes in a remote cluster. By implementing fabric 41 | activities as Kube API calls, the system is entirely vendor-agnostic, language-neutral, and compatible with community 42 | best practices for remote cluster management. 43 | 44 | Hyperkube is NOT a [Kubernetes Operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). In early 45 | genesis, this project is an assembly area for cataloging reference practices, codifying an API bridge to k8s, and 46 | providing a practical gateway for interacting with a remote Fabric network. With this in mind, it is a stated goal 47 | of this project to **align** with the operator pattern, such that the routines and functionality implemented by `fabctl` 48 | may be refactored into a first-class controller and collection of K8s CRDs. 49 | 50 | ![fabctl.png](images/fabctl.png) 51 | 52 | The general structure of `fabctl` divides Fabric administration activities into three functional sub-areas: 53 | - `network` 54 | - `channel` 55 | - `chaincode` 56 | 57 | To affect changes in a fabric network, the administrator applies a series of local configuration files, selects a 58 | _connection profile_, and issues `fabctl` commands to reflect the change in a remote cluster. All API updates to k8s 59 | are made via a Kubernetes API client, inheriting the current `kubectl` context and configuration. 60 | 61 | 62 | ## Notes / Scratch / TODO: 63 | 64 | - Developed in golang as a go neophyte. (Would consider Java + Fabric8 for rapid development, but chose to align with Fabric patterns.) 65 | - Hyperkube will use the [Tekton Operator](https://tekton.dev) to run `Tasks` on K8s. (Currently k8s batch Job CRDs) 66 | - Consider using [Argo Workflows](https://argoproj.github.io/workflows) to run `Workflows` on K8s (not Jobs, not tkn Tasks, ...) 67 | - Hyperkube uses [KIND](https://kind.sigs.k8s.io) as a development platform. Write a KIND.md or QUICKSTART.md 68 | - Start with [test-network-kind](https://github.com/hyperledger/fabric-samples/pull/471) and use `fabctl` to emulate `network.sh` 69 | - Install [asset-exchange-basic](link) and integrate with [rest-sample-application](link) to illustrate high-level dev activities. 70 | - Supplement / Pattern on [Fabric Getting Started - Run Fabric](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html) - reduce to **minimal** fabctl activities. 71 | - Link up with practices from [weft](link) for management and conversion of fabric connection descriptors. 72 | - Link up with IDE integration (e.g. VSCode extension) 73 | - Link up with ephemeral Fabric instances on cloud (e.g. fab-playground) -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v0/DeploymentUtil.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0; 7 | 8 | import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; 9 | import io.fabric8.kubernetes.api.model.apps.Deployment; 10 | import io.fabric8.kubernetes.api.model.apps.DeploymentStatus; 11 | import io.fabric8.kubernetes.client.KubernetesClient; 12 | import io.fabric8.kubernetes.client.Watch; 13 | import io.fabric8.kubernetes.client.Watcher; 14 | import io.fabric8.kubernetes.client.WatcherException; 15 | import java.util.concurrent.CountDownLatch; 16 | import java.util.concurrent.TimeUnit; 17 | import lombok.extern.slf4j.Slf4j; 18 | 19 | /** 20 | * Helper utilities for working with k8s deployments. 21 | */ 22 | @Slf4j 23 | public class DeploymentUtil 24 | { 25 | private static final YAMLMapper yamlMapper = new YAMLMapper(); 26 | 27 | /** 28 | * Wait a little while for all the pods in a deployment to reach ready status. 29 | */ 30 | public static void waitForDeployment(final KubernetesClient client, 31 | final Deployment deployment, 32 | final long timeout, 33 | final TimeUnit units) 34 | throws Exception 35 | { 36 | log.info("Waiting {} {} for deployment {} to be available.", 37 | timeout, 38 | units, 39 | deployment.getMetadata().getName()); 40 | 41 | final CountDownLatch latch = new CountDownLatch(1); 42 | 43 | try (Watch watch = client.apps() 44 | .deployments() 45 | .withName(deployment.getMetadata().getName()) 46 | .watch(new Watcher<>() 47 | { 48 | @Override public void eventReceived(Action action, Deployment resource) 49 | { 50 | try 51 | { 52 | log.info("action {} {}", action, yamlMapper.writeValueAsString(resource)); 53 | 54 | final DeploymentStatus status = resource.getStatus(); 55 | if (status == null) 56 | { 57 | log.warn("Deployment has no status?"); 58 | return; 59 | } 60 | 61 | // 62 | // release the latch if the deployment is up or deleted. 63 | // 64 | if (Action.DELETED.equals(action)) 65 | { 66 | log.info("deployment was deleted. abort!"); 67 | latch.countDown(); 68 | } 69 | else if (status.getUnavailableReplicas() == null) 70 | { 71 | log.info("All replicas are ready. Let's go!"); 72 | latch.countDown(); 73 | } 74 | } 75 | catch (Exception ex) 76 | { 77 | log.error("Could not process callback event", ex); 78 | // todo: this should log the error but trap the exception below, deleting the deployment/service if it was created. 79 | // todo: this assertion failure is NOT caught by the test runner! 80 | } 81 | } 82 | 83 | @Override public void onClose(WatcherException cause) 84 | { 85 | if (cause != null) 86 | { 87 | log.error("Watch forcibly closed", cause); 88 | } 89 | 90 | latch.countDown(); 91 | } 92 | })) 93 | { 94 | log.info("Awaiting a maximum of {} {} for deployment.", timeout, units); 95 | 96 | boolean completed = latch.await(timeout, units); 97 | if (! completed) 98 | { 99 | // 100 | // The deployment / services can be removed here, but this will scrub any debugging info from the event history. 101 | // TODO: we can detect that the deployment has stalled, but the root cause will be in the POD status conditions... trap the error here and present to the user. 102 | // 103 | throw new Exception("Deployment was not ready after " + 104 | timeout + " " + units + 105 | ". Most likely this is an error pulling the Docker image."); 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/resources/fixtures/msp-foo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "org2-peer2.org1.example.com" 3 | id: "org2-peer2.org2.example.com" 4 | msp: 5 | keystore: 6 | priv_sk: "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgkn8kbcoTh95MvgfO\n\ 7 | Jfhl+V/PDH5yFSsahsLqU3bdJCuhRANCAASjUpqHxu24htwRuyJxfiDXSnNwXCJa\nP0XO2bhmkl5hX8njGnCDB/D6ApAg8N2bg3CWihaoODJm8J4Wy3qFKQZP\n\ 8 | -----END PRIVATE KEY-----\n" 9 | config.yaml: "NodeOUs:\n Enable: true\n ClientOUIdentifier:\n Certificate:\ 10 | \ cacerts/ca.org2.example.com-cert.pem\n OrganizationalUnitIdentifier: client\n\ 11 | \ PeerOUIdentifier:\n Certificate: cacerts/ca.org2.example.com-cert.pem\n\ 12 | \ OrganizationalUnitIdentifier: peer\n AdminOUIdentifier:\n Certificate:\ 13 | \ cacerts/ca.org2.example.com-cert.pem\n OrganizationalUnitIdentifier: admin\n\ 14 | \ OrdererOUIdentifier:\n Certificate: cacerts/ca.org2.example.com-cert.pem\n\ 15 | \ OrganizationalUnitIdentifier: orderer\n" 16 | admincerts: {} 17 | tlscacerts: 18 | tlsca.org2.example.com-cert.pem: "-----BEGIN CERTIFICATE-----\nMIICVjCCAf2gAwIBAgIQC8U+KzCCbD7VbF56qqDH0TAKBggqhkjOPQQDAjB2MQsw\n\ 19 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz\n\ 20 | Y2Eub3JnMi5leGFtcGxlLmNvbTAeFw0yMTA4MzExNDM0MDBaFw0zMTA4MjkxNDM0\nMDBaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\n\ 21 | Ew1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcyLmV4YW1wbGUuY29tMR8wHQYD\nVQQDExZ0bHNjYS5vcmcyLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D\n\ 22 | AQcDQgAE+Ge7GT1KO1iG+SOgYENEBzO1i1zaO26+mxpN9yJuBhdyrPMJe3mnvIVq\nBvEpXSwMqNis/JzYmDYpd24pNtmj5KNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1Ud\n\ 23 | JQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1Ud\nDgQiBCDwFs8Sj2EOuZVwsMR3W/bgs2ZKgxLoD6iljkZuRe2LpjAKBggqhkjOPQQD\n\ 24 | AgNHADBEAiAcVV7MLCbw+bGZi+bM1fZeO0T2ZSFgqgm62gQpjR2WTQIgJr9p82yl\nb3y7DRKEMD+PCOvQTOc5dOKcUtu+kyr+yyA=\n\ 25 | -----END CERTIFICATE-----\n" 26 | cacerts: 27 | ca.org2.example.com-cert.pem: "-----BEGIN CERTIFICATE-----\nMIICUDCCAfegAwIBAgIQBc6wEySBw1QWYg7kihS7aDAKBggqhkjOPQQDAjBzMQsw\n\ 28 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\n\ 29 | b3JnMi5leGFtcGxlLmNvbTAeFw0yMTA4MzExNDM0MDBaFw0zMTA4MjkxNDM0MDBa\nMHMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\n\ 30 | YW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcyLmV4YW1wbGUuY29tMRwwGgYDVQQD\nExNjYS5vcmcyLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n\ 31 | J8ysBhKKMfDXN0Jqm/G8MwYgs+Tmtqjsx2P+SM76a1dUaWqTg7d0n4WoP+H182f1\nrN+0xPmAjimVE77mqgsiqKNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1UdJQQWMBQG\n\ 32 | CCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdDgQiBCDh\nXUICRzgy/RzjmNz4sJHLKmLeOVW8tMOsReTNhzACRDAKBggqhkjOPQQDAgNHADBE\n\ 33 | AiANZnrTvznZsNAx/24eOyPOHA9uX/7WqZetukW4AYIQTQIgByTi+uDqwwFLnEVe\nm7VmRYm/x/m13EeWXdAprl536iE=\n\ 34 | -----END CERTIFICATE-----\n" 35 | signcerts: 36 | org2-peer2.org2.example.com-cert.pem: "-----BEGIN CERTIFICATE-----\nMIICLDCCAdOgAwIBAgIQPwL4x5FN8cbAVe1r/HTiCjAKBggqhkjOPQQDAjBzMQsw\n\ 37 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\n\ 38 | b3JnMi5leGFtcGxlLmNvbTAeFw0yMTA4MzExNDM0MDBaFw0zMTA4MjkxNDM0MDBa\nMG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\n\ 39 | YW4gRnJhbmNpc2NvMQ0wCwYDVQQLEwRwZWVyMSQwIgYDVQQDExtvcmcyLXBlZXIy\nLm9yZzIuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASjUpqH\n\ 40 | xu24htwRuyJxfiDXSnNwXCJaP0XO2bhmkl5hX8njGnCDB/D6ApAg8N2bg3CWihao\nODJm8J4Wy3qFKQZPo00wSzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAr\n\ 41 | BgNVHSMEJDAigCDhXUICRzgy/RzjmNz4sJHLKmLeOVW8tMOsReTNhzACRDAKBggq\nhkjOPQQDAgNHADBEAiB2yYFpZGkaOl5GuQ1nNfCj5RZNpqoor2Cj0fbMs6w1dwIg\n\ 42 | UWIcs4QVZXMvPrQuQvp1kVKzKEH+EwhRuDB1GBxSv0c=\n-----END CERTIFICATE-----\n" 43 | tls: 44 | server.key: "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFO23Zk6k6SbHJ6Y8\n\ 45 | CoKlAM+uirnAF+M5h1Kk/FPYDOuhRANCAATdKWXOIhQLIZMY/Z0ns9BYy+m/cBiW\n0J7Bym/e3eqStPFwrj6GBEWzYB2s7cpjew6X7QY0g8xx3WyG88Ak2J3E\n\ 46 | -----END PRIVATE KEY-----\n" 47 | ca.crt: "-----BEGIN CERTIFICATE-----\nMIICVjCCAf2gAwIBAgIQC8U+KzCCbD7VbF56qqDH0TAKBggqhkjOPQQDAjB2MQsw\n\ 48 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz\n\ 49 | Y2Eub3JnMi5leGFtcGxlLmNvbTAeFw0yMTA4MzExNDM0MDBaFw0zMTA4MjkxNDM0\nMDBaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\n\ 50 | Ew1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcyLmV4YW1wbGUuY29tMR8wHQYD\nVQQDExZ0bHNjYS5vcmcyLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D\n\ 51 | AQcDQgAE+Ge7GT1KO1iG+SOgYENEBzO1i1zaO26+mxpN9yJuBhdyrPMJe3mnvIVq\nBvEpXSwMqNis/JzYmDYpd24pNtmj5KNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1Ud\n\ 52 | JQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1Ud\nDgQiBCDwFs8Sj2EOuZVwsMR3W/bgs2ZKgxLoD6iljkZuRe2LpjAKBggqhkjOPQQD\n\ 53 | AgNHADBEAiAcVV7MLCbw+bGZi+bM1fZeO0T2ZSFgqgm62gQpjR2WTQIgJr9p82yl\nb3y7DRKEMD+PCOvQTOc5dOKcUtu+kyr+yyA=\n\ 54 | -----END CERTIFICATE-----\n" 55 | server.crt: "-----BEGIN CERTIFICATE-----\nMIICfDCCAiKgAwIBAgIQK2/RNima9tzczlVLqDCr9TAKBggqhkjOPQQDAjB2MQsw\n\ 56 | CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz\n\ 57 | Y2Eub3JnMi5leGFtcGxlLmNvbTAeFw0yMTA4MzExNDM0MDBaFw0zMTA4MjkxNDM0\nMDBaMGAxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\n\ 58 | Ew1TYW4gRnJhbmNpc2NvMSQwIgYDVQQDExtvcmcyLXBlZXIyLm9yZzIuZXhhbXBs\nZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATdKWXOIhQLIZMY/Z0ns9BY\n\ 59 | y+m/cBiW0J7Bym/e3eqStPFwrj6GBEWzYB2s7cpjew6X7QY0g8xx3WyG88Ak2J3E\no4GnMIGkMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB\n\ 60 | BQUHAwIwDAYDVR0TAQH/BAIwADArBgNVHSMEJDAigCDwFs8Sj2EOuZVwsMR3W/bg\ns2ZKgxLoD6iljkZuRe2LpjA4BgNVHREEMTAvghtvcmcyLXBlZXIyLm9yZzIuZXhh\n\ 61 | bXBsZS5jb22CCm9yZzItcGVlcjKHBAAAAAAwCgYIKoZIzj0EAwIDSAAwRQIhAMTC\nwOwxzAuQtbCK2YmcL4IemVXvszmEvutshQrsvLK0AiAwO7T6qUP3Yt5zepMbPmmE\n\ 62 | ueE8s6dHDHJW+7FUvndK9Q==\n-----END CERTIFICATE-----\n" 63 | -------------------------------------------------------------------------------- /fabctl-sandbox/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /fabctl-msp-unfurler/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /fabctl-sandbox/README.md: -------------------------------------------------------------------------------- 1 | # fabctl sandbox 2 | 3 | This is a quickstart sandbox for emulating the functionality of `network.sh`, kube-native style. 4 | 5 | This code base is intentionally rough. It is a sandbox for rapidly iterating through various mechanics 6 | for cluster config, job control, and design of _peer connection profiles_ to achieve fabric administration 7 | objectives on a remote / cloud-based cluster. 8 | 9 | The usage scenarios are intended to be run interactively through an IDE (+debugger) or in a scripted sequence 10 | through gradle unit test invocations. Remember, this project is just using gradle + java + JUnit as a 11 | mechanism to quickly thrash and iterate through various approaches. After things start to settle down the 12 | routines may be refactored into CLIs, service APIs, or eventually into a fully-fledged k8s Controller. 13 | 14 | 15 | ## Quickstart 16 | 17 | ### Prereqs 18 | 19 | - Build [fabric-ccs-builder](https://github.com/jkneubuh/fabric-samples/tree/feature/kind-test-network/test-network-kind#fabric-ccs-builder) docker image 20 | - Build [fabctl-msp-unfurler](../fabctl-msp-unfurler#build) docker image 21 | - Build [fabric-rest-sample](https://github.com/hyperledgendary/fabric-rest-sample#docker-image) docker image 22 | - Build [asset-transfer-basic](https://github.com/hyperledger/fabric-samples/tree/main/asset-transfer-basic/chaincode-external#running-the-asset-transfer-basic-external-service) docker image 23 | 24 | 25 | ### Kube 26 | 27 | ```shell 28 | kind create cluster 29 | 30 | kind load docker-image hyperledger/chaincode/asset-transfer-basic 31 | kind load docker-image hyperledgendary/fabric-ccs-builder 32 | kind load docker-image hyperledgendary/fabric-hyper-kube/fabctl-msp-unfurler 33 | kind load docker-image hyperledgendary/fabric-rest-sample 34 | ``` 35 | 36 | ```shell 37 | kubectl create -f src/test/resources/kube/pv-fabric.yaml 38 | kubectl create -f src/test/resources/kube/ns-test-network.yaml 39 | kubectl -n test-network create -f src/test/resources/kube/pvc-fabric.yaml 40 | ``` 41 | 42 | - TODO: add an nginx ingress controller to KIND cluster 43 | - TODO: add a kustomization base and overlays for installation to KIND, IKS, and OCP (pvc + ingress) 44 | 45 | 46 | ### Test Network 47 | 48 | ```shell 49 | rm -rf config/crypto-config/ 50 | docker run \ 51 | --rm \ 52 | -v ${PWD}/config:/config \ 53 | hyperledger/fabric-tools \ 54 | cryptogen generate \ 55 | --config=/config/crypto-config.yaml \ 56 | --output=/config/crypto-config 57 | ``` 58 | 59 | ```shell 60 | echo -n | ./gradlew test --tests org.hyperledger.fabric.fabctl.v1.InitFabricNetworkTest # network.sh up 61 | echo -n | ./gradlew test --tests org.hyperledger.fabric.fabctl.v1.CreateAndJoinChannelTest # network.sh createChannel 62 | echo -n | ./gradlew test --tests org.hyperledger.fabric.fabctl.v1.ChaincodeSandboxTest # network.sh deployCC 63 | ``` 64 | 65 | ### Chaincode Query 66 | 67 | ```shell 68 | kubectl -n test-network exec deploy/org1-peer1 -i -t -- /bin/sh 69 | export CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/xyzzy/Admin@org1.example.com/msp 70 | export FABRIC_LOGGING_SPEC=INFO 71 | 72 | peer chaincode \ 73 | invoke \ 74 | -o orderer1:6050 \ 75 | -C mychannel \ 76 | -n basic \ 77 | -c '{"Args":["CreateAsset","1","blue","35","tom","1000"]}' \ 78 | --tls \ 79 | --cafile /var/hyperledger/fabric/xyzzy/orderer1.example.com/tls/ca.crt \ 80 | 81 | sleep 5 82 | 83 | peer chaincode query -C mychannel -n basic -c '{"Args":["ReadAsset","1"]}' 84 | 85 | exit 86 | ``` 87 | 88 | ### REST Easy 89 | 90 | ```shell 91 | echo -n | ./gradlew test --tests org.hyperledger.fabric.fabctl.v1.FabricRESTSampleTest 92 | ``` 93 | 94 | Open a port forward to the REST sample service in a new shell: 95 | ```shell 96 | kubectl -n test-network port-forward svc/fabric-rest-sample 3000:3000 97 | ``` 98 | 99 | Follow the HTTP examples from [fabric-rest-sample](https://github.com/hyperledgendary/fabric-rest-sample#rest-api) 100 | ```shell 101 | export SAMPLE_APIKEY=97834158-3224-4CE7-95F9-A148C886653E 102 | 103 | curl --header "X-Api-Key: ${SAMPLE_APIKEY}" http://localhost:3000/api/assets | jq 104 | ``` 105 | 106 | 107 | 108 | - TODO: Run the rest endpoint locally (docker/main()/...) and connect via ingress or port-forward 109 | 110 | 111 | ## Teardown 112 | 113 | ```shell 114 | kubectl -n test-network delete deployment --all 115 | kubectl -n test-network delete pod --all 116 | kubectl -n test-network delete service --all 117 | kubectl -n test-network delete configmap --all 118 | kubectl -n test-network delete secret --all 119 | kubectl -n test-network create -f src/test/resources/kube/job-scrub-test-network.yaml 120 | kubectl -n test-network wait --for=condition=complete --timeout=60s job/job-scrub-fabric-volume 121 | kubectl -n test-network delete job --all 122 | ``` 123 | [GOTO Network](#test-network) 124 | 125 | or ... 126 | ```shell 127 | kind delete cluster 128 | ``` 129 | [GOTO Kube](#kube) 130 | 131 | 132 | ## Iterations (Legacy) 133 | 134 | ### v0 135 | 136 | In the first approach for this project, the MSP assets were generated IN the cluster and written to a persistent 137 | volume by calling `cryptogen` from a batch Job. Mechanically this works OK, but makes it nigh-impossible to 138 | dynamically construct a Gateway Connection Profile for client-side application development. 139 | 140 | ```shell 141 | kubectl -n test-network create configmap fabric-config --from-file=config/v0/ 142 | kubectl -n test-network create -f src/test/resources/kube/job-crypto-config.yaml 143 | kubectl -n test-network wait --for=condition=complete --timeout=120s job/job-crypto-config 144 | 145 | echo -n | ./gradlew test --tests org.hyperledger.fabric.fabctl.v0.InitFabricNetworkTest # network.sh up 146 | echo -n | ./gradlew test --tests org.hyperledger.fabric.fabctl.v0.CreateAndJoinChannelTest # network.sh createChannel 147 | echo -n | ./gradlew test --tests org.hyperledger.fabric.fabctl.v0.ChaincodeSandboxTest # network.sh deployCC 148 | ``` 149 | 150 | ```shell 151 | kubectl -n test-network exec deploy/org1-peer1 -i -t -- /bin/sh 152 | export CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 153 | export FABRIC_LOGGING_SPEC=INFO 154 | 155 | peer chaincode \ 156 | invoke \ 157 | -o orderer1:6050 \ 158 | -C mychannel \ 159 | -n basic \ 160 | -c '{"Args":["CreateAsset","1","blue","35","tom","1000"]}' \ 161 | --tls \ 162 | --cafile /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt 163 | 164 | sleep 5 165 | 166 | peer chaincode query -C mychannel -n basic -c '{"Args":["ReadAsset","1"]}' 167 | 168 | exit 169 | ``` 170 | 171 | ### v1 172 | 173 | In v1 (the current stake), crypto assets are generated by running `cryptogen` from a Docker container, storing the 174 | MSP assets on a LOCAL DRIVE. Local directories are read and transformed into an MSP YAML DESCRIPTOR file, mounted 175 | in the network pods as config maps, and unfurled back into folder structures by an init container. 176 | 177 | 178 | ### v2 179 | 180 | In v2 we'll carry forward the approach of the MSP and Network descriptors, introducing a dynamic mechanism to 181 | bootstrap the fabric CAs, Enrollments, Registrations, MSP Context, and mutual TLS auth. 182 | 183 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v0/CreateAndJoinChannelTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0; 7 | 8 | import java.util.*; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.hyperledger.fabric.fabctl.v0.command.ConfigTXGenCommand; 11 | import org.hyperledger.fabric.fabctl.v0.command.PeerCommand; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import static java.util.Map.entry; 15 | import static org.junit.jupiter.api.Assertions.assertEquals; 16 | 17 | /** 18 | * Build up a channel and join peers in a single flow. 19 | */ 20 | @Slf4j 21 | public class CreateAndJoinChannelTest extends TestBase 22 | { 23 | private static final String CHANNEL = "mychannel"; 24 | 25 | /** 26 | * In a future pass, pull on a thread to load the peer env context from a properties file / yaml / json / resource bundle / etc. into a real struct, not a simple map. 27 | */ 28 | private static final Map ORG1_PEER1_CONTEXT = 29 | Map.ofEntries( 30 | entry("FABRIC_LOGGING_SPEC", "INFO"), 31 | entry("CORE_PEER_TLS_ENABLED", "true"), 32 | entry("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/ca.crt"), 33 | entry("CORE_PEER_ADDRESS", "org1-peer1:7051"), 34 | entry("CORE_PEER_LOCALMSPID", "Org1MSP"), 35 | entry("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp") 36 | ); 37 | 38 | private static final Map ORG1_PEER2_CONTEXT = 39 | Map.ofEntries( 40 | entry("FABRIC_LOGGING_SPEC", "INFO"), 41 | entry("CORE_PEER_TLS_ENABLED", "true"), 42 | entry("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/tls/ca.crt"), 43 | entry("CORE_PEER_ADDRESS", "org1-peer2:7051"), 44 | entry("CORE_PEER_LOCALMSPID", "Org1MSP"), 45 | entry("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp") 46 | ); 47 | 48 | private static final Map ORG2_PEER1_CONTEXT = 49 | Map.ofEntries( 50 | entry("FABRIC_LOGGING_SPEC", "INFO"), 51 | entry("CORE_PEER_TLS_ENABLED", "true"), 52 | entry("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/tls/ca.crt"), 53 | entry("CORE_PEER_ADDRESS", "org2-peer1:7051"), 54 | entry("CORE_PEER_LOCALMSPID", "Org2MSP"), 55 | entry("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp") 56 | ); 57 | 58 | private static final Map ORG2_PEER2_CONTEXT = 59 | Map.ofEntries( 60 | entry("FABRIC_LOGGING_SPEC", "INFO"), 61 | entry("CORE_PEER_TLS_ENABLED", "true"), 62 | entry("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/tls/ca.crt"), 63 | entry("CORE_PEER_ADDRESS", "org2-peer2:7051"), 64 | entry("CORE_PEER_LOCALMSPID", "Org2MSP"), 65 | entry("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp") 66 | ); 67 | 68 | private static final List> PEER_CONTEXTS = 69 | Arrays.asList( 70 | ORG1_PEER1_CONTEXT, 71 | ORG1_PEER2_CONTEXT, 72 | ORG2_PEER1_CONTEXT, 73 | ORG2_PEER2_CONTEXT 74 | ); 75 | 76 | @Test 77 | public void testCreateAndJoinChannel() throws Exception 78 | { 79 | // 80 | // channel config 81 | // 82 | final ConfigTXGenCommand configChannel = 83 | new ConfigTXGenCommand( 84 | "configtxgen", 85 | "-channelID", "mychannel", 86 | "-profile", "TwoOrgsChannel", 87 | "-outputCreateChannelTx", "/var/hyperledger/fabric/channel-artifacts/mychannel.tx"); 88 | 89 | // todo: crypto-config is not aligned with fabric/config paths between peer and configtxgen commands. 90 | // Fix this by generating the crypto spec locally, mounting an msp / identity into the context. 91 | final Map txgenContext = Map.of("FABRIC_CFG_PATH", "/var/hyperledger/fabric"); 92 | 93 | assertEquals(0, execute(configChannel, txgenContext)); 94 | 95 | // 96 | // create the channel 97 | // 98 | final PeerCommand createChannel = 99 | new PeerCommand("peer", 100 | "channel", "create", 101 | "-c", CHANNEL, 102 | "-o", "orderer1:6050", // net config? 103 | "-f", "/var/hyperledger/fabric/channel-artifacts/mychannel.tx", // files on PVC ? 104 | "--outputBlock", "/var/hyperledger/fabric/channel-artifacts/mychannel.block", 105 | "--tls", 106 | "--cafile", "/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt"); 107 | 108 | assertEquals(0, execute(createChannel, ORG1_PEER1_CONTEXT)); 109 | 110 | 111 | // 112 | // Update the channel to anchors 113 | // 114 | final PeerCommand updateChannel = 115 | new PeerCommand("peer", 116 | "channel", "update", 117 | "-c", CHANNEL, 118 | "-o", "orderer1:6050", // net config? 119 | "-f", "/var/hyperledger/fabric/channel-artifacts/Org1MSPanchors.tx", 120 | "--tls", 121 | "--cafile", "/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt"); 122 | 123 | assertEquals(0, execute(updateChannel, ORG1_PEER1_CONTEXT)); 124 | 125 | 126 | // 127 | // Joins peers to channel 128 | // 129 | final PeerCommand joinChannel = 130 | new PeerCommand("peer", 131 | "channel", "join", 132 | "-b", "/var/hyperledger/fabric/channel-artifacts/mychannel.block"); 133 | 134 | for (Map context : PEER_CONTEXTS) 135 | { 136 | assertEquals(0, execute(joinChannel, context)); 137 | } 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v0/TestBase.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0; 7 | 8 | import com.fasterxml.jackson.databind.ObjectMapper; 9 | import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; 10 | import io.fabric8.kubernetes.api.model.*; 11 | import io.fabric8.kubernetes.api.model.batch.v1.Job; 12 | import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder; 13 | import io.fabric8.kubernetes.client.Config; 14 | import io.fabric8.kubernetes.client.ConfigBuilder; 15 | import io.fabric8.kubernetes.client.DefaultKubernetesClient; 16 | import io.fabric8.kubernetes.client.KubernetesClient; 17 | import java.io.BufferedReader; 18 | import java.io.Reader; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.Map.Entry; 23 | import java.util.concurrent.TimeUnit; 24 | import lombok.extern.slf4j.Slf4j; 25 | import org.hyperledger.fabric.fabctl.v0.command.ConfigTXGenCommand; 26 | import org.hyperledger.fabric.fabctl.v0.command.FabricCommand; 27 | import org.hyperledger.fabric.fabctl.v0.command.PeerCommand; 28 | import org.junit.jupiter.api.BeforeAll; 29 | 30 | import static org.junit.jupiter.api.Assertions.fail; 31 | 32 | @Slf4j 33 | public class TestBase 34 | { 35 | protected static final ObjectMapper objectMapper = new ObjectMapper(); 36 | 37 | protected static final YAMLMapper yamlMapper = new YAMLMapper(); 38 | 39 | protected static final String TEST_NAMESPACE = "test-network"; 40 | 41 | protected static final String CCS_BUILDER_IMAGE = "hyperledgendary/fabric-ccs-builder"; 42 | 43 | protected static final long JOB_TIMEOUT = 120; 44 | 45 | protected static final TimeUnit JOB_TIMEOUT_UNITS = TimeUnit.SECONDS; 46 | 47 | protected static Config kubeConfig; 48 | 49 | protected static KubernetesClient client; 50 | 51 | @BeforeAll 52 | public static void beforeAll() throws Exception 53 | { 54 | kubeConfig = new ConfigBuilder() 55 | .withTrustCerts(true) 56 | .withNamespace(TEST_NAMESPACE) 57 | .build(); 58 | 59 | log.info("Connecting to k8s with config:\n{}", yamlMapper.writeValueAsString(kubeConfig)); 60 | 61 | client = new DefaultKubernetesClient(kubeConfig); 62 | } 63 | 64 | protected int runJob(final Job template) throws Exception 65 | { 66 | final Job job = JobUtil.runJob(client, template, JOB_TIMEOUT, JOB_TIMEOUT_UNITS); 67 | final Pod mainPod = JobUtil.findMainPod(client, job.getMetadata().getName()); 68 | final PodStatus status = mainPod.getStatus(); 69 | final int exitCode = JobUtil.getContainerStatusCode(status, "main"); 70 | 71 | log.info("Command output:"); 72 | 73 | // 74 | // Print the [main] container / pod logs 75 | // 76 | try (final Reader logReader = 77 | client.pods() 78 | .withName(mainPod.getMetadata().getName()) 79 | .inContainer("main") 80 | .getLogReader(); 81 | final BufferedReader reader = new BufferedReader(logReader)) 82 | { 83 | String line; 84 | while ((line = reader.readLine()) != null) 85 | { 86 | log.info(line); 87 | } 88 | } 89 | 90 | log.info("Command exit: {}", exitCode); 91 | 92 | return exitCode; 93 | } 94 | 95 | protected int execute(final FabricCommand command, final Map context) throws Exception 96 | { 97 | log.info("Launching command:\n{}", yamlMapper.writeValueAsString(command)); 98 | log.info("With context:\n{}", yamlMapper.writeValueAsString(context)); 99 | 100 | final Job template = buildRemoteJob(command, context); 101 | 102 | return runJob(template); 103 | } 104 | 105 | /** 106 | * Still not ideal but keep this in the local test... what's the mechanism for passing in the config yaml 107 | * and block residue on the PVC / volume mount? 108 | *

109 | * More importantly: how will we set the peer context via a config map to specify the proper crypto config 110 | * and MSP assets? 111 | *

112 | * "context" here is just a k/v env map. But this is incorrect... the "context" for a remote peer command 113 | * also needs to specify the _identity_ (msp) of the activity. This includes an env scope as well as a set 114 | * of crypto assets, to be mounted or referenced dynamically as a set of kube secrets and/or config maps. 115 | */ 116 | protected Job buildRemoteJob(final FabricCommand command, final Map context) 117 | { 118 | final List env = new ArrayList<>(); 119 | for (Entry e : context.entrySet()) 120 | { 121 | env.add(new EnvVarBuilder() 122 | .withName(e.getKey()) 123 | .withValue(e.getValue()) 124 | .build()); 125 | } 126 | 127 | // todo: set the crypto / identity context for the MSP via reference to "connection profile" 128 | final List volumeMounts = new ArrayList<>(); 129 | volumeMounts.add(new VolumeMountBuilder() 130 | .withName("fabric-volume") 131 | .withMountPath("/var/hyperledger/fabric") 132 | .build()); 133 | 134 | // oof: this is rough. configtxgen and peer commands need the crypto-spec and fabric config in slightly different folders. 135 | if (command instanceof PeerCommand) 136 | { 137 | volumeMounts.add(new VolumeMountBuilder() 138 | .withName("fabric-config") 139 | .withMountPath("/var/hyperledger/fabric/config") 140 | .build()); 141 | } 142 | else if (command instanceof ConfigTXGenCommand) 143 | { 144 | volumeMounts.add(new VolumeMountBuilder() 145 | .withName("fabric-config") 146 | .withMountPath("/var/hyperledger/fabric/configtx.yaml") 147 | .withSubPath("configtx.yaml") 148 | .build()); 149 | } 150 | else 151 | { 152 | fail("Unknown command type: " + command); 153 | } 154 | 155 | 156 | // @formatter:off 157 | return new JobBuilder() 158 | .withApiVersion("batch/v1") 159 | .withNewMetadata() 160 | .withGenerateName("peer-job-") 161 | .endMetadata() 162 | .withNewSpec() 163 | .withBackoffLimit(0) 164 | .withCompletions(1) 165 | .withNewTemplate() 166 | .withNewSpec() 167 | .withRestartPolicy("Never") 168 | .addNewContainer() 169 | .withName("main") 170 | .withImage(command.getImage() + ":" + command.getLabel()) 171 | .withCommand(command.command) 172 | .withEnv(env) 173 | .withVolumeMounts(volumeMounts) 174 | .endContainer() 175 | .addNewVolume() 176 | .withName("fabric-volume") 177 | .withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSourceBuilder() 178 | .withClaimName("fabric") 179 | .build()) 180 | .endVolume() 181 | .addNewVolume() 182 | .withName("fabric-config") 183 | .withConfigMap(new ConfigMapVolumeSourceBuilder() 184 | .withName("fabric-config") 185 | .build()) 186 | .endVolume() 187 | .endSpec() 188 | .endTemplate() 189 | .endSpec() 190 | .build(); 191 | // @formatter:on 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/main/java/org/hyperledger/fabric/fabctl/v0/JobUtil.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0; 7 | 8 | import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; 9 | import io.fabric8.kubernetes.api.model.*; 10 | import io.fabric8.kubernetes.api.model.batch.v1.Job; 11 | import io.fabric8.kubernetes.api.model.batch.v1.JobStatus; 12 | import io.fabric8.kubernetes.client.KubernetesClient; 13 | import io.fabric8.kubernetes.client.Watch; 14 | import io.fabric8.kubernetes.client.Watcher; 15 | import io.fabric8.kubernetes.client.WatcherException; 16 | import java.io.IOException; 17 | import java.util.concurrent.CountDownLatch; 18 | import java.util.concurrent.TimeUnit; 19 | import lombok.extern.slf4j.Slf4j; 20 | 21 | @Slf4j 22 | public class JobUtil 23 | { 24 | private static final YAMLMapper yamlMapper = new YAMLMapper(); 25 | 26 | public static Job submitJob(final KubernetesClient client, final Job template) throws IOException 27 | { 28 | final Job job = 29 | client.batch() 30 | .v1() 31 | .jobs() 32 | .create(template); 33 | 34 | log.info("Created job {}:\n{}", 35 | job.getMetadata().getName(), 36 | yamlMapper.writeValueAsString(job)); 37 | 38 | return job; 39 | } 40 | 41 | public static Job runJob(final KubernetesClient client, 42 | final Job template, 43 | final long timeout, 44 | final TimeUnit units) 45 | throws InterruptedException, IOException 46 | { 47 | final Job created = submitJob(client, template); 48 | waitForJob(client, created, timeout, units); 49 | 50 | return created; // todo: better to return the final Job status / state 51 | } 52 | 53 | public static void waitForJob(final KubernetesClient client, 54 | final Job job, 55 | final long timeout, 56 | final TimeUnit units) 57 | throws InterruptedException 58 | { 59 | // 60 | // Subscribe to events for the newly created job. 61 | // 62 | final String jobName = job.getMetadata().getName(); 63 | log.info("Opening watch for job {}", jobName); 64 | 65 | // 66 | // Block the current thread until receipt of a success/failed event, or a timeout has been reached. 67 | // 68 | final CountDownLatch latch = new CountDownLatch(1); 69 | 70 | try (final Watch watch = client.batch() 71 | .v1() 72 | .jobs() 73 | .withName(jobName) 74 | .watch(new Watcher() 75 | { 76 | @Override 77 | public void eventReceived(Action action, Job resource) 78 | { 79 | try 80 | { 81 | log.info("Received Job action {}\n{}", 82 | action, 83 | yamlMapper.writeValueAsString(action)); 84 | 85 | final JobStatus jobStatus = resource.getStatus(); 86 | if (jobStatus == null) 87 | { 88 | log.info("Job has no status?"); 89 | return; 90 | } 91 | 92 | // 93 | // Release the latch if the job succeeded or failed. 94 | // 95 | if (Integer.valueOf(1).equals(jobStatus.getActive())) 96 | { 97 | log.info("Job started at " + jobStatus.getStartTime()); 98 | } 99 | else if (Integer.valueOf(1).equals(jobStatus.getFailed())) 100 | { 101 | log.info("Job failed with conditions " + 102 | yamlMapper.writeValueAsString(jobStatus.getConditions())); 103 | latch.countDown(); 104 | } 105 | else if (Integer.valueOf(1).equals(jobStatus.getSucceeded())) 106 | { 107 | log.info("Job succeeded at " + jobStatus.getCompletionTime()); 108 | latch.countDown(); 109 | } 110 | } 111 | catch (Exception ex) 112 | { 113 | log.error("Could not process action event", ex); 114 | } 115 | } 116 | 117 | @Override 118 | public void onClose(WatcherException cause) 119 | { 120 | if (cause != null) 121 | { 122 | log.error("Watch was closed with exception", cause); 123 | } 124 | 125 | latch.countDown(); 126 | } 127 | })) 128 | { 129 | log.info("Awaiting a maximum of {} {} for job completion.", timeout, units); 130 | 131 | boolean completed = latch.await(timeout, units); 132 | if (! completed) 133 | { 134 | log.error("Job is still running. Terminate it here?"); 135 | } 136 | } 137 | } 138 | 139 | 140 | /** 141 | * Find the Job's pod with a container named [main] 142 | * @param jobName 143 | * @return 144 | */ 145 | public static Pod findMainPod(final KubernetesClient client, final String jobName) 146 | { 147 | for (Pod pod : client.pods() 148 | .withLabel("job-name", jobName) 149 | .list() 150 | .getItems()) 151 | { 152 | for (Container container : pod.getSpec().getContainers()) 153 | { 154 | if ("main".equalsIgnoreCase(container.getName())) 155 | { 156 | return pod; 157 | } 158 | } 159 | } 160 | 161 | return null; 162 | } 163 | 164 | /** 165 | * Dig the [main] container's exit code out of the PodStatus 166 | * 167 | * @param podStatus 168 | * @param containerName 169 | * @return 170 | * @throws Exception 171 | */ 172 | public static int getContainerStatusCode(PodStatus podStatus, String containerName) 173 | throws Exception 174 | { 175 | for (ContainerStatus containerStatus : podStatus.getContainerStatuses()) 176 | { 177 | if (containerName.equalsIgnoreCase(containerStatus.getName())) 178 | { 179 | final ContainerState containerState = containerStatus.getState(); 180 | log.info("Final container state:\n{}", yamlMapper.writeValueAsString(containerState)); 181 | 182 | // 183 | // Carry the exit code from the [main] driver into the job runner exit. 184 | // 185 | if (containerState.getRunning() != null) 186 | { 187 | log.error("Timeout expired: container is still running."); 188 | return -1; 189 | } 190 | else if (containerState.getWaiting() != null) 191 | { 192 | log.error("Timeout expired: container is still waiting"); 193 | return -1; 194 | } 195 | else if (containerState.getTerminated() != null) 196 | { 197 | return containerState.getTerminated().getExitCode(); 198 | } 199 | } 200 | } 201 | 202 | throw new IllegalStateException("No exit status found for terminal state: \n" + podStatus); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v1/TestNetwork.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1; 7 | 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.Properties; 12 | import org.hyperledger.fabric.fabctl.v1.msp.MSPDescriptor; 13 | import org.hyperledger.fabric.fabctl.v1.network.*; 14 | 15 | import static org.junit.jupiter.api.Assertions.fail; 16 | 17 | /** 18 | * test-network is a NetworkConfiguration matching the topology of fabric-samples test-network. 19 | * 20 | * This is just a convenience for inflating a sample network for the scope of unit tests. 21 | * 22 | * This code is ugly - don't stare too hard - it's just to illustrate a dynamic structure for the fabric sample network. 23 | * 24 | * This test network will read the node environment scopes from the /resources/config/v1/networks/test-network resource bundle. 25 | * 26 | * Nodes in the test network include an MSP Descriptor, translated from a local crypto-config folder output by cryptogen. 27 | * 28 | * TEST OUTCOMES: 29 | * 30 | * - The approach works OK but it needs to be streamlined. This network descriptor is 1000% bloated in current form. 31 | * 32 | * - Where will users / enrollments / registrations occur and be stored? 33 | * 34 | * - It seems like MSP descriptors should be REFERENCED not embedded in this structure. Something inspired from k8s 35 | * such as kube references (envFrom, secretRef, configMapRef, or Argo workflowRef,... ) 36 | * 37 | */ 38 | class TestNetwork extends NetworkConfig 39 | { 40 | TestNetwork() 41 | { 42 | this("test-network"); 43 | } 44 | 45 | private TestNetwork(final String name) 46 | { 47 | super(name); 48 | 49 | try 50 | { 51 | // 52 | // orderer org: three orderers. 53 | // 54 | final OrganizationConfig ordererOrg = 55 | new OrganizationConfig("OrdererOrg", 56 | "OrdererMSP", 57 | new MSPDescriptor("msp-com.example", 58 | new File("config/crypto-config/ordererOrganizations/example.com"))); 59 | 60 | ordererOrg.getOrderers() 61 | .add(new OrdererConfig("orderer1", 62 | loadEnvironment("orderer1.properties"), 63 | new MSPDescriptor("msp-com.example.orderer1", 64 | new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com")))); 65 | 66 | ordererOrg.getOrderers() 67 | .add(new OrdererConfig("orderer2", 68 | loadEnvironment("orderer2.properties"), 69 | new MSPDescriptor("msp-com.example.orderer2", 70 | new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com")))); 71 | 72 | ordererOrg.getOrderers() 73 | .add(new OrdererConfig("orderer3", 74 | loadEnvironment("orderer3.properties"), 75 | new MSPDescriptor("msp-com.example.orderer3", 76 | new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com")))); 77 | 78 | 79 | 80 | // 81 | // org1 : two peers + admin context in peer1 82 | // 83 | final OrganizationConfig org1 = 84 | new OrganizationConfig("Org1", 85 | "Org1MSP", 86 | new MSPDescriptor("msp-com.example.org1", 87 | new File("config/crypto-config/peerOrganizations/org1.example.com"))); 88 | 89 | 90 | // 91 | // This is an awful hack but org1-peer1 needs an org Admin context MSP in order to run the CLI (cc query from shell) 92 | /// AND it needs the orderer1 tls/ca.crt... 93 | // todo: what's the correct way to specify users, enrollments, admin contexts? Figure this out when switching to the CA. 94 | // todo: doesn't the admin MSP User context belong up on the org? 95 | // todo: we don't need the entire MSP for distributing the TLS certificates 96 | // 97 | org1.getPeers() 98 | .add(new PeerConfig("org1-peer1", 99 | loadEnvironment("org1-peer1.properties"), 100 | new MSPDescriptor("msp-com.example.org1.org1-peer1", 101 | new File("config/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com")), 102 | new MSPDescriptor("msp-com.example.org1.user.admin", 103 | new File("config/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com")), 104 | new MSPDescriptor("msp-com.example.orderer1", 105 | new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com")))); 106 | 107 | org1.getPeers() 108 | .add(new PeerConfig("org1-peer2", 109 | loadEnvironment("org1-peer2.properties"), 110 | new MSPDescriptor("msp-com.example.org1.org1-peer2", 111 | new File("config/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com")))); 112 | 113 | 114 | 115 | // 116 | // org2 : two peers 117 | // 118 | final OrganizationConfig org2 = 119 | new OrganizationConfig("Org2", 120 | "Org1MSP", 121 | new MSPDescriptor("msp-com.example.org2", 122 | new File("config/crypto-config/peerOrganizations/org2.example.com"))); 123 | 124 | 125 | org2.getPeers() 126 | .add(new PeerConfig("org2-peer1", 127 | loadEnvironment("org2-peer1.properties"), 128 | new MSPDescriptor("msp-com.example.org2.org2-peer1", 129 | new File("config/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com")))); 130 | org2.getPeers() 131 | .add(new PeerConfig("org2-peer2", 132 | loadEnvironment("org2-peer2.properties"), 133 | new MSPDescriptor("msp-com.example.org2.org2-peer2", 134 | new File("config/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com")))); 135 | 136 | 137 | organizations.add(ordererOrg); 138 | organizations.add(org1); 139 | organizations.add(org2); 140 | } 141 | catch (Exception ex) 142 | { 143 | fail("Could not load test network config", ex); 144 | } 145 | } 146 | 147 | /** 148 | * This can be improved, but it's not super relevant HOW the context is initialized. Just experimenting here... 149 | */ 150 | protected static Environment loadEnvironment(final String name) 151 | { 152 | final String path = "/config/v1/networks/test-network/" + name; 153 | final Properties props = new Properties(); 154 | 155 | try 156 | { 157 | props.load(CryptoXYZZYConfigMapTest.class.getResourceAsStream(path)); 158 | } 159 | catch (IOException ex) 160 | { 161 | fail("Could not load resource bundle " + path, ex); 162 | } 163 | 164 | final Environment environment = new Environment(); 165 | for (Object o : props.keySet()) 166 | { 167 | final String key = o.toString(); 168 | environment.put(key, props.getProperty(key)); 169 | } 170 | 171 | return environment; 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v0/CreateChannelTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0; 7 | 8 | import io.fabric8.kubernetes.api.model.*; 9 | import io.fabric8.kubernetes.api.model.batch.v1.Job; 10 | import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Map.Entry; 15 | import java.util.TreeMap; 16 | import lombok.Data; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | 22 | /** 23 | * Build up to 'peer channel create': 24 | * 25 | *

 26 |  * kubectl -n test-network exec deploy/org1-peer1 -i -t -- /bin/sh
 27 |  *
 28 |  * export CORE_PEER_MSPCONFIGPATH=/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
 29 |  *
 30 |  * peer channel \
 31 |  *   create \
 32 |  *   -c mychannel \
 33 |  *   -o orderer1:6050 \
 34 |  *   -f /var/hyperledger/fabric/channel-artifacts/mychannel.tx \
 35 |  *   --outputBlock /var/hyperledger/fabric/channel-artifacts/mychannel.block \
 36 |  *   --tls \
 37 |  *   --cafile /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt
 38 |  *
 39 |  * peer channel \
 40 |  *   update \
 41 |  *   -o orderer1:6050 \
 42 |  *   -c mychannel \
 43 |  *   -f /var/hyperledger/fabric/channel-artifacts/Org1MSPanchors.tx \
 44 |  *   --tls \
 45 |  *   --cafile /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt
 46 |  *
 47 |  * exit
 48 |  * 
49 | * 50 | * 51 | * Here the "connection profile" (env) is minimal - only the MSP config path and a minimal set of env overrides are 52 | * necessary to run the command. The complexity in this case stems from: 53 | * - assumptions about the network structure (orderer host name) - needs a local "network config," e.g. minifab 54 | * - multiple commands in sequence (Possible with k8s Batch Jobs but improved greatly by Tkn Tasks) 55 | * - residue of previous commands available locally or on a PVC 56 | * - command inputs funneled into command arguments 57 | * 58 | */ 59 | @Slf4j 60 | public class CreateChannelTest extends TestBase 61 | { 62 | // pass 2 - still stirring the pot. 63 | @Data 64 | private static class PeerCommand // extends fabric command 65 | { 66 | public final String image = "hyperledger/fabric-peer"; 67 | public final String label = "2.3.2"; 68 | 69 | public final String[] command; 70 | //public final Map env = new TreeMap<>(); // better to pass as arg to execute() 71 | 72 | public PeerCommand(final String... command) 73 | { 74 | this.command = command; 75 | } 76 | } 77 | 78 | /** 79 | * rough hack - this is a placeholder for "configuration files" residing on the client's local system. 80 | * The network configuration specifies the topology of peers, orderers, core config, etc. 81 | */ 82 | @Data 83 | private static class NetworkConfig 84 | { 85 | public final List orderers = new ArrayList<>(); 86 | public final List peers = new ArrayList(); 87 | 88 | // Do the the orderer / peer TLS certs belong in this structure? 89 | // --cafile /var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt 90 | } 91 | 92 | @Test 93 | public void testCreateChannel() throws Exception 94 | { 95 | final String channelName = "mychannel"; 96 | 97 | // this is not great but trying it on for size... 98 | final NetworkConfig network = new NetworkConfig(); 99 | network.orderers.add("orderer1:6050"); 100 | network.orderers.add("orderer2:6050"); 101 | network.orderers.add("orderer3:6050"); 102 | network.peers.add("org1-peer1"); 103 | network.peers.add("org1-peer2"); 104 | network.peers.add("org2-peer1"); 105 | network.peers.add("org2-peer2"); 106 | 107 | // This environment will come from a "connection profile" 108 | final Map org1Peer1AdminScope = new TreeMap<>(); 109 | org1Peer1AdminScope.put("FABRIC_LOGGING_SPEC", "INFO"); 110 | org1Peer1AdminScope.put("CORE_PEER_TLS_ENABLED", "true"); 111 | org1Peer1AdminScope.put("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/ca.crt"); 112 | org1Peer1AdminScope.put("CORE_PEER_ADDRESS", "org1-peer1:7051"); 113 | org1Peer1AdminScope.put("CORE_PEER_LOCALMSPID", "Org1MSP"); 114 | org1Peer1AdminScope.put("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp"); 115 | 116 | final PeerCommand createChannel = 117 | new PeerCommand("peer", 118 | "channel", "create", 119 | "-c", channelName, 120 | "-o", network.orderers.get(0), // meh. 121 | "-f", "/var/hyperledger/fabric/channel-artifacts/mychannel.tx", // files on PVC ? 122 | "--outputBlock", "/var/hyperledger/fabric/channel-artifacts/mychannel.block", 123 | "--tls", 124 | "--cafile", "/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt"); 125 | 126 | assertEquals(0, executeCommand(createChannel, org1Peer1AdminScope)); 127 | 128 | 129 | final PeerCommand updateChannel = 130 | new PeerCommand("peer", 131 | "channel", "update", 132 | "-c", channelName, 133 | "-o", network.orderers.get(0), // meh. 134 | "-f", "/var/hyperledger/fabric/channel-artifacts/Org1MSPanchors.tx", 135 | "--tls", 136 | "--cafile", "/var/hyperledger/fabric/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt"); 137 | 138 | assertEquals(0, executeCommand(updateChannel, org1Peer1AdminScope)); 139 | } 140 | 141 | private int executeCommand(final PeerCommand command, final Map context) throws Exception 142 | { 143 | log.info("Launching command:\n{}", yamlMapper.writeValueAsString(command)); 144 | log.info("With context:\n{}", yamlMapper.writeValueAsString(context)); 145 | 146 | final Job template = buildRemoteJob(command, context); 147 | 148 | return runJob(template); 149 | } 150 | 151 | /** 152 | * Still not ideal but keep this in the local test... what's the mechanism for passing in the config yaml 153 | * and block residue on the PVC / volume mount? 154 | * 155 | * More importantly: how will we set the peer context via a config map to specify the proper crypto config 156 | * and MSP assets? 157 | * 158 | * "context" here is just a k/v env map. But this is incorrect... the "context" for a remote peer command 159 | * also needs to specify the _identity_ (msp) of the activity. This includes an env scope as well as a set 160 | * of crypto assets, to be mounted or referenced dynamically as a set of kube secrets and/or config maps. 161 | */ 162 | private Job buildRemoteJob(final PeerCommand command, final Map context) 163 | { 164 | final List env = new ArrayList<>(); 165 | for (Entry e : context.entrySet()) 166 | { 167 | env.add(new EnvVarBuilder() 168 | .withName(e.getKey()) 169 | .withValue(e.getValue()) 170 | .build()); 171 | } 172 | 173 | // todo: set the crypto / identity context for the MSP via reference to "connection profile" 174 | final List volumeMounts = new ArrayList<>(); 175 | volumeMounts.add(new VolumeMountBuilder() 176 | .withName("fabric-volume") 177 | .withMountPath("/var/hyperledger/fabric") 178 | .build()); 179 | volumeMounts.add(new VolumeMountBuilder() 180 | .withName("fabric-config") 181 | .withMountPath("/var/hyperledger/fabric/config") 182 | .build()); 183 | 184 | // @formatter:off 185 | return new JobBuilder() 186 | .withApiVersion("batch/v1") 187 | .withNewMetadata() 188 | .withGenerateName("peer-job-") 189 | .endMetadata() 190 | .withNewSpec() 191 | .withBackoffLimit(0) 192 | .withCompletions(1) 193 | .withNewTemplate() 194 | .withNewSpec() 195 | .withRestartPolicy("Never") 196 | .addNewContainer() 197 | .withName("main") 198 | .withImage(command.getImage() + ":" + command.getLabel()) 199 | .withCommand(command.command) 200 | .withEnv(env) 201 | .withVolumeMounts(volumeMounts) 202 | .endContainer() 203 | .addNewVolume() 204 | .withName("fabric-volume") 205 | .withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSourceBuilder() 206 | .withClaimName("fabric") 207 | .build()) 208 | .endVolume() 209 | .addNewVolume() 210 | .withName("fabric-config") 211 | .withConfigMap(new ConfigMapVolumeSourceBuilder() 212 | .withName("fabric-config") 213 | .build()) 214 | .endVolume() 215 | .endSpec() 216 | .endTemplate() 217 | .endSpec() 218 | .build(); 219 | // @formatter:on 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v0/PeerJoinScratchTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0; 7 | 8 | import io.fabric8.kubernetes.api.model.*; 9 | import io.fabric8.kubernetes.api.model.batch.v1.Job; 10 | import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder; 11 | import java.io.BufferedReader; 12 | import java.io.Reader; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Map.Entry; 17 | import java.util.TreeMap; 18 | import java.util.concurrent.TimeUnit; 19 | import lombok.Data; 20 | import lombok.extern.slf4j.Slf4j; 21 | import org.junit.jupiter.api.Test; 22 | 23 | import static org.junit.jupiter.api.Assertions.assertEquals; 24 | import static org.junit.jupiter.api.Assertions.assertNotNull; 25 | 26 | /** 27 | * Build up to a peer join channel running as a k8s Job 28 | */ 29 | @Slf4j 30 | public class PeerJoinScratchTest extends TestBase 31 | { 32 | // total hack - just stirring the pot here. 33 | @Data 34 | private static class PeerCommand // extends fabric command 35 | { 36 | public final String image = "hyperledger/fabric-peer"; 37 | public final String label = "2.3.2"; 38 | 39 | public final String[] command; 40 | public final Map env = new TreeMap<>(); 41 | 42 | public PeerCommand(String... command) 43 | { 44 | this.command = command; 45 | } 46 | } 47 | 48 | @Test 49 | public void testPeerChannelJoinCommand() throws Exception 50 | { 51 | final PeerCommand command = 52 | new PeerCommand("peer", 53 | "channel", "join", 54 | "-b", "/var/hyperledger/fabric/channel-artifacts/mychannel.block"); 55 | 56 | // 57 | // This is part of the "connection profile" - a set of k/v properties to be set in the `peer` environment. 58 | // 59 | // Connection Profiles will be saved on the client and used to swap back and forth between various 60 | // identities when running remote admin commands. 61 | // 62 | // The peer admin command needs to be run with a connection profile specifying the current user's context. 63 | // For joining channels, this needs to point to the msp folder for an Admin user on the correct org. 64 | // 65 | 66 | // org1-peer1 --> 67 | // command.env.put("FABRIC_LOGGING_SPEC", "INFO"); 68 | // command.env.put("CORE_PEER_TLS_ENABLED", "true"); 69 | // command.env.put("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/ca.crt"); 70 | // command.env.put("CORE_PEER_ADDRESS", "org1-peer1:7051"); 71 | // command.env.put("CORE_PEER_LOCALMSPID", "Org1MSP"); 72 | // command.env.put("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp"); 73 | 74 | // org1-peer2 75 | // command.env.put("FABRIC_LOGGING_SPEC", "INFO"); 76 | // command.env.put("CORE_PEER_TLS_ENABLED", "true"); 77 | // command.env.put("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer2.org1.example.com/tls/ca.crt"); 78 | // command.env.put("CORE_PEER_ADDRESS", "org1-peer2:7051"); 79 | // command.env.put("CORE_PEER_LOCALMSPID", "Org1MSP"); 80 | // command.env.put("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp"); 81 | 82 | // org2-peer1 83 | // command.env.put("FABRIC_LOGGING_SPEC", "INFO"); 84 | // command.env.put("CORE_PEER_TLS_ENABLED", "true"); 85 | // command.env.put("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer1.org2.example.com/tls/ca.crt"); 86 | // command.env.put("CORE_PEER_ADDRESS", "org2-peer1:7051"); 87 | // command.env.put("CORE_PEER_LOCALMSPID", "Org2MSP"); 88 | // command.env.put("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp"); 89 | 90 | // org2-peer2 91 | command.env.put("FABRIC_LOGGING_SPEC", "INFO"); 92 | command.env.put("CORE_PEER_TLS_ENABLED", "true"); 93 | command.env.put("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/peers/org2-peer2.org2.example.com/tls/ca.crt"); 94 | command.env.put("CORE_PEER_ADDRESS", "org2-peer2:7051"); 95 | command.env.put("CORE_PEER_LOCALMSPID", "Org2MSP"); 96 | command.env.put("CORE_PEER_MSPCONFIGPATH", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp"); 97 | 98 | 99 | 100 | final Job template = buildRemoteJob(command); 101 | final Job job = JobUtil.runJob(client, template, 120, TimeUnit.SECONDS); 102 | 103 | final Pod mainPod = JobUtil.findMainPod(client, job.getMetadata().getName()); 104 | assertNotNull(mainPod); 105 | 106 | final PodStatus status = mainPod.getStatus(); 107 | assertNotNull(status); 108 | 109 | final int exitCode = JobUtil.getContainerStatusCode(status, "main"); 110 | 111 | log.info("Executed command:\n{}", objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(command)); 112 | log.info("Command output:"); 113 | 114 | // 115 | // Print the [main] container / pod logs 116 | // 117 | try (final Reader logReader = 118 | client.pods() 119 | .withName(mainPod.getMetadata().getName()) 120 | .inContainer("main") 121 | .getLogReader(); 122 | final BufferedReader reader = new BufferedReader(logReader)) 123 | { 124 | String line; 125 | while ((line = reader.readLine()) != null) 126 | { 127 | log.info(line); 128 | } 129 | } 130 | 131 | log.info("Command exit: {}", exitCode); 132 | 133 | assertEquals(0, exitCode); 134 | } 135 | 136 | private Job buildRemoteJob(final PeerCommand command) 137 | { 138 | final List env = new ArrayList<>(); 139 | for (Entry e : command.env.entrySet()) 140 | { 141 | env.add(new EnvVarBuilder() 142 | .withName(e.getKey()) 143 | .withValue(e.getValue()) 144 | .build()); 145 | } 146 | 147 | final List volumeMounts = new ArrayList<>(); 148 | volumeMounts.add(new VolumeMountBuilder() 149 | .withName("fabric-volume") 150 | .withMountPath("/var/hyperledger/fabric") 151 | .build()); 152 | volumeMounts.add(new VolumeMountBuilder() 153 | .withName("fabric-config") 154 | .withMountPath("/var/hyperledger/fabric/config") 155 | .build()); 156 | 157 | // @formatter:off 158 | return new JobBuilder() 159 | .withApiVersion("batch/v1") 160 | .withNewMetadata() 161 | .withGenerateName("peer-job-") 162 | .endMetadata() 163 | .withNewSpec() 164 | .withBackoffLimit(0) 165 | .withCompletions(1) 166 | .withNewTemplate() 167 | .withNewSpec() 168 | .withRestartPolicy("Never") 169 | .addNewContainer() 170 | .withName("main") 171 | .withImage(command.getImage() + ":" + command.getLabel()) 172 | .withCommand(command.command) 173 | .withEnv(env) 174 | .withVolumeMounts(volumeMounts) 175 | .endContainer() 176 | .addNewVolume() 177 | .withName("fabric-volume") 178 | .withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSourceBuilder() 179 | .withClaimName("fabric") 180 | .build()) 181 | .endVolume() 182 | .addNewVolume() 183 | .withName("fabric-config") 184 | .withConfigMap(new ConfigMapVolumeSourceBuilder() 185 | .withName("fabric-config") 186 | .build()) 187 | .endVolume() 188 | .endSpec() 189 | .endTemplate() 190 | .endSpec() 191 | .build(); 192 | // @formatter:on 193 | } 194 | } 195 | 196 | 197 | 198 | 199 | // This is the full set of k/v env values from launching the peer node. Here for reference. 200 | // command.env.put("FABRIC_CFG_PATH", "/var/hyperledger/fabric/config"); 201 | // command.env.put("FABRIC_LOGGING_SPEC", "debug:cauthdsl,policies,msp,grpc,peer.gossip.mcs,gossip,leveldbhelper=info"); 202 | // command.env.put("CORE_PEER_TLS_ENABLED", "true"); 203 | // command.env.put("CORE_PEER_TLS_CERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/server.crt"); 204 | // command.env.put("CORE_PEER_TLS_KEY_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/server.key"); 205 | // command.env.put("CORE_PEER_TLS_ROOTCERT_FILE", "/var/hyperledger/fabric/crypto-config/peerOrganizations/org1.example.com/peers/org1-peer1.org1.example.com/tls/ca.crt"); 206 | // command.env.put("CORE_PEER_ID", "org1-peer1.org1.example.com"); 207 | // command.env.put("CORE_PEER_ADDRESS", "org1-peer1:7051"); 208 | // command.env.put("CORE_PEER_LISTENADDRESS", "0.0.0.0:7051"); 209 | // command.env.put("CORE_PEER_CHAINCODEADDRESS", "org1-peer1:7052"); 210 | // command.env.put("CORE_PEER_CHAINCODELISTENADDRESS", "0.0.0.0:7052"); 211 | // command.env.put("CORE_PEER_GOSSIP_BOOTSTRAP", "org1-peer2:7051"); 212 | // command.env.put("CORE_PEER_GOSSIP_EXTERNALENDPOINT", "org1-peer1:7051"); 213 | // command.env.put("CORE_PEER_LOCALMSPID", "Org1MSP"); 214 | // command.env.put("CORE_OPERATIONS_LISTENADDRESS", "0.0.0.0:9443"); 215 | // command.env.put("CORE_PEER_FILESYSTEMPATH", "/var/hyperledger/fabric/data/org1-peer1.org1.example.com"); 216 | // command.env.put("CORE_LEDGER_SNAPSHOTS_ROOTDIR", "/var/hyperledger/fabric/data/org1-peer1.org1.example.com/snapshots"); 217 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v0/SandboxTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v0; 7 | 8 | import com.fasterxml.jackson.databind.ObjectMapper; 9 | import io.fabric8.kubernetes.api.model.*; 10 | import io.fabric8.kubernetes.api.model.batch.v1.Job; 11 | import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder; 12 | import io.fabric8.kubernetes.api.model.batch.v1.JobStatus; 13 | import io.fabric8.kubernetes.client.*; 14 | import io.fabric8.kubernetes.client.Config; 15 | import io.fabric8.kubernetes.client.ConfigBuilder; 16 | import java.util.concurrent.CountDownLatch; 17 | import java.util.concurrent.TimeUnit; 18 | import lombok.extern.slf4j.Slf4j; 19 | import org.junit.jupiter.api.BeforeAll; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.junit.jupiter.api.Assertions.*; 23 | 24 | /** 25 | * This is just a sample collection of scratch routines, what-if, and spaghetti code. 26 | */ 27 | @Slf4j 28 | public class SandboxTest 29 | { 30 | private static final ObjectMapper objectMapper = new ObjectMapper(); 31 | 32 | private static final String TEST_NAMESPACE = "test-network"; 33 | 34 | private static KubernetesClient client; 35 | 36 | @BeforeAll 37 | public static void beforeAll() throws Exception 38 | { 39 | final Config kubeConfig = 40 | new ConfigBuilder() 41 | .withTrustCerts(true) 42 | .withNamespace(TEST_NAMESPACE) 43 | .build(); 44 | 45 | log.info("Connecting to k8s with config:\n" + 46 | objectMapper.writerWithDefaultPrettyPrinter() 47 | .writeValueAsString(kubeConfig)); 48 | 49 | client = new DefaultKubernetesClient(kubeConfig); 50 | assertNotNull(client); 51 | } 52 | 53 | // Just run a find command with the volume mounts for a peer context. 54 | @Test 55 | public void testLaunchBatchJob() throws Exception 56 | { 57 | assertNotNull(client); 58 | 59 | final Job template = buildTestJob("find", "/var/hyperledger/fabric"); 60 | 61 | final Job job = 62 | client.batch() 63 | .v1() 64 | .jobs() 65 | .inNamespace(TEST_NAMESPACE) 66 | .create(template); 67 | 68 | log.info("Launched job:\n{}", 69 | objectMapper.writerWithDefaultPrettyPrinter() 70 | .writeValueAsString(job)); 71 | 72 | assertNotNull(job); 73 | } 74 | 75 | /** 76 | * Run a job and wait for completion 77 | */ 78 | @Test 79 | public void testLaunchAndWaitForJob() throws Exception 80 | { 81 | //final String[] command = { "find", "/var/hyperledger/fabric" }; 82 | final String[] command = { "find", "/" }; 83 | final int deadlineSeconds = 60; 84 | 85 | final Job job = 86 | client.batch() 87 | .v1() 88 | .jobs() 89 | .inNamespace(TEST_NAMESPACE) 90 | .create(buildTestJob(command)); 91 | 92 | assertNotNull(job); 93 | 94 | waitForJob(job, 60, TimeUnit.SECONDS); 95 | 96 | // 97 | // Job has completed. What happened? 98 | // 99 | final String jobName = job.getMetadata().getName(); 100 | final Pod mainPod = findMainPod(jobName); 101 | assertNotNull(mainPod); 102 | 103 | final PodStatus mainPodStatus = mainPod.getStatus(); 104 | assertNotNull(mainPodStatus); 105 | 106 | final int exitCode = getContainerStatusCode(mainPodStatus, "main"); 107 | assertEquals(0, exitCode); 108 | 109 | // todo: stdout / stderr 110 | } 111 | 112 | @Test 113 | public void testLaunchAnotherJob() throws Exception 114 | { 115 | final Job job = 116 | JobUtil.runJob(client, 117 | buildTestJob("find", "/"), 118 | 60, 119 | TimeUnit.SECONDS); 120 | 121 | assertNotNull(job); 122 | 123 | } 124 | 125 | 126 | 127 | private void waitForJob(final Job job, final long timeout, final TimeUnit units) 128 | throws InterruptedException 129 | { 130 | // 131 | // Subscribe to events for the newly created job. 132 | // 133 | final String jobName = job.getMetadata().getName(); 134 | log.info("Opening watch for job {}", jobName); 135 | 136 | // 137 | // Block the current thread until receipt of a success/failed event, or a timeout has been reached. 138 | // 139 | final CountDownLatch latch = new CountDownLatch(1); 140 | 141 | try (final Watch watch = client.batch() 142 | .v1() 143 | .jobs() 144 | .inNamespace(TEST_NAMESPACE) 145 | .withName(jobName) 146 | .watch(new Watcher() 147 | { 148 | @Override 149 | public void eventReceived(Action action, Job resource) 150 | { 151 | try 152 | { 153 | log.info("Received Job action " + action + ":\n" + 154 | objectMapper.writerWithDefaultPrettyPrinter() 155 | .writeValueAsString(resource)); 156 | 157 | final JobStatus jobStatus = resource.getStatus(); 158 | if (jobStatus == null) 159 | { 160 | log.info("Job has no status?"); 161 | return; 162 | } 163 | 164 | // 165 | // Release the latch if the job succeeded or failed. 166 | // 167 | if (Integer.valueOf(1).equals(jobStatus.getActive())) 168 | { 169 | log.info("Job started at " + jobStatus.getStartTime()); 170 | } 171 | else if (Integer.valueOf(1).equals(jobStatus.getFailed())) 172 | { 173 | log.info("Job failed with conditions " + 174 | objectMapper.writeValueAsString(jobStatus.getConditions())); 175 | latch.countDown(); 176 | } 177 | else if (Integer.valueOf(1).equals(jobStatus.getSucceeded())) 178 | { 179 | log.info("Job succeeded at " + jobStatus.getCompletionTime()); 180 | latch.countDown(); 181 | } 182 | } 183 | catch (Exception ex) 184 | { 185 | fail("Could not process action event", ex); 186 | } 187 | } 188 | 189 | @Override 190 | public void onClose(WatcherException cause) 191 | { 192 | if (cause != null) 193 | { 194 | fail("Watch was closed with exception", cause); 195 | } 196 | 197 | latch.countDown(); 198 | } 199 | })) 200 | { 201 | log.info("Awaiting a maximum of {} {} for job completion.", timeout, units); 202 | 203 | boolean completed = latch.await(timeout, units); 204 | if (! completed) 205 | { 206 | fail("Job did not complete after " + timeout + " " + units); 207 | } 208 | } 209 | } 210 | 211 | /** 212 | * Find the Job's pod with a container named [main] 213 | * @param jobName 214 | * @return 215 | */ 216 | private Pod findMainPod(String jobName) 217 | { 218 | for (Pod pod : client.pods() 219 | .withLabel("job-name", jobName) 220 | .list() 221 | .getItems()) 222 | { 223 | for (Container container : pod.getSpec().getContainers()) 224 | { 225 | if ("main".equalsIgnoreCase(container.getName())) 226 | { 227 | return pod; 228 | } 229 | } 230 | } 231 | 232 | return null; 233 | } 234 | 235 | /** 236 | * Dig the [main] container's exit code out of the PodStatus 237 | * 238 | * @param podStatus 239 | * @param containerName 240 | * @return 241 | * @throws Exception 242 | */ 243 | private int getContainerStatusCode(PodStatus podStatus, String containerName) 244 | throws Exception 245 | { 246 | for (ContainerStatus containerStatus : podStatus.getContainerStatuses()) 247 | { 248 | if (containerName.equalsIgnoreCase(containerStatus.getName())) 249 | { 250 | final ContainerState containerState = containerStatus.getState(); 251 | log.info("Final container state: \n" + 252 | objectMapper.writerWithDefaultPrettyPrinter() 253 | .writeValueAsString(containerState)); 254 | 255 | // 256 | // Carry the exit code from the [main] driver into the job runner exit. 257 | // 258 | if (containerState.getRunning() != null) 259 | { 260 | log.error("Timeout expired: container is still running."); 261 | return -1; 262 | } 263 | else if (containerState.getWaiting() != null) 264 | { 265 | log.error("Timeout expired: container is still waiting"); 266 | return -1; 267 | } 268 | else if (containerState.getTerminated() != null) 269 | { 270 | return containerState.getTerminated().getExitCode(); 271 | } 272 | } 273 | } 274 | 275 | throw new IllegalStateException("No exit status found for terminal state: \n" + 276 | objectMapper.writerWithDefaultPrettyPrinter() 277 | .writeValueAsString(podStatus)); 278 | } 279 | 280 | 281 | private Job buildTestJob(final String... command) 282 | { 283 | // @formatter:off 284 | return new JobBuilder() 285 | .withApiVersion("batch/v1") 286 | .withNewMetadata() 287 | //.withName("foo-job") 288 | .withGenerateName("foo-job-") 289 | .endMetadata() 290 | .withNewSpec() 291 | .withBackoffLimit(0) 292 | .withCompletions(1) 293 | .withNewTemplate() 294 | .withNewSpec() 295 | .withRestartPolicy("Never") 296 | .addNewContainer() 297 | .withName("main") 298 | .withImage("alpine") 299 | .withCommand(command) 300 | .endContainer() 301 | .endSpec() 302 | .endTemplate() 303 | .endSpec() 304 | .build(); 305 | // @formatter:on 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /fabctl-sandbox/src/test/java/org/hyperledger/fabric/fabctl/v1/MSPContextTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright IBM Corp. All Rights Reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | package org.hyperledger.fabric.fabctl.v1; 7 | 8 | import com.fasterxml.jackson.databind.JsonNode; 9 | import com.fasterxml.jackson.databind.node.ObjectNode; 10 | import io.fabric8.kubernetes.api.model.ConfigMap; 11 | import io.fabric8.kubernetes.api.model.ConfigMapBuilder; 12 | import java.io.ByteArrayOutputStream; 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.nio.charset.Charset; 16 | import java.util.Map; 17 | import lombok.Data; 18 | import lombok.extern.slf4j.Slf4j; 19 | import org.apache.commons.compress.utils.IOUtils; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.junit.jupiter.api.Assertions.*; 23 | 24 | /** 25 | * With the CryptoXYZZYConfigMapTest, we tried an approach whereby the MSP assets from an enrollment 26 | * were stored as a k8s configmap, with each cert in the MSP mapped to an individual key within the CM. 27 | * 28 | * When mounting a volume within a pod with the XYZZY approach, each of the cm keys needs to be mapped 29 | * back out into the correct location in the original MSP folder structure. This is possible, but it's 30 | * a real nightmare. Let's do something different, and ... 31 | * 32 | * - use cryptogen to pre-construct a folder hierarchy of MSP assets (note: this approach also works with CA) 33 | * 34 | * - Write some code to mangle an MSP asset structure into a SINGLE YAML file. This is the "msp descriptor" 35 | * or MSP context. 36 | * 37 | * - When running fabric things, fabctl will specify the MSP context by mounting the msp descriptors into 38 | * the pods running fabric binaries. When the binary starts, each of the relevant MSP descriptors will 39 | * be mounted as a configmap / secret into the pods. 40 | * 41 | * - Each fabric pod running in k8s will include an init container, with instructions to unfurl the MSP 42 | * descriptor into the appropriate place on the local file system. Despite some fiddling at the CLI 43 | * env and args, this will allow us to carefully constrain the scope in which the fabric binaries run. 44 | * 45 | * 46 | * In this pass we'll write a companion routine (docker image) that knows how to unfurl the MSP descriptors 47 | * into the pod at launch time. A more robust solution is to simply teach core fabric how to read an 48 | * MSP from an input YAML file, rather than relying on disk-based structures referenced by the single 49 | * "MSPDir" attribute currently in fabric config. 50 | * 51 | * Let's roll up some MSP contexts, and see how this goes! 52 | * 53 | * 54 | * 55 | * 56 | * TEST OUTCOMES: 57 | * 58 | * - hyperledgendary/fabric-hyper-kube/fabctl-msp-unfurler is a VERY rough cut at a program which will 59 | * read msp descriptors mounted in CM/secrets, unfurling into an MSP folder structure in the scope 60 | * of the network nodes. This is suitable for specifying the MSPDir attribute in the fabric config. 61 | * 62 | * - While this test is incomplete, the overall approach feels right. At a minimum it is a HUGE 63 | * improvement over the flattened structures outlined in CryptoXYZZYConfigMapTest. 64 | * 65 | * - I checked the behavior of mounting the unfurled MSP context using static YAML / manual kubectl, 66 | * rather than dynamically generating the remote volumes from a test driver and making assertions 67 | * in this test routine. We could do so, but ... the approach seems sound enough to skip forward 68 | * and go through the config of the test network using the cryptogen local assets. 69 | * 70 | * 71 | * 72 | * NEXT STEPS: 73 | * 74 | * - use cryptogen to specify the network topology as MSP assets on the local drive. 75 | * 76 | * - Describe a NetworkConfig matching the topology of the test network. 77 | * 78 | * - For each of the network nodes, expand on "Context" by running deployments in ENV + { msp-context } scope. 79 | * 80 | * - Construct the org/orderer/peer network, specifying initContainers and unfurling msp-descriptors before "running things" 81 | * 82 | * - Install chaincode, run queries. 83 | * 84 | * - Use the LOCAL MSP files (or possibly the k8s msp descriptor maps??) to generate a gateway connection profile. 85 | * 86 | * AND 87 | * 88 | * - run gateway client app locally (via port-forward to gateway peer node) 89 | * 90 | * - run gateway client app in cluster 91 | * 92 | * 93 | * This is an effective plateau for the "V1" approach, using cryptogen + MSP descriptor context. 94 | * 95 | * For a "V2" approach, replace the cryptogen with a network topology describing a set of CAs. This is harder than 96 | * it seems, as the CA clients need to run somewhere with access to both the peer binaries and an HTTP(s) route into 97 | * the CAs. 98 | */ 99 | @Slf4j 100 | public class MSPContextTest extends TestBase 101 | { 102 | // 103 | // KISS : don't get fancy, just model the MSP context structure exactly as it would appear on 104 | // a directory volume. 105 | // 106 | @Data 107 | private static class MSP 108 | { 109 | final String name; 110 | final String id; 111 | final JsonNode msp; 112 | 113 | private MSP(final String name, final File basedir) 114 | { 115 | this.name = name; 116 | this.id = basedir.getName(); 117 | this.msp = helper(new File(basedir, "msp")); 118 | } 119 | 120 | private JsonNode helper(final File f) 121 | { 122 | if (f.isDirectory()) 123 | { 124 | final ObjectNode node = objectMapper.createObjectNode(); 125 | 126 | for (File child : f.listFiles()) 127 | { 128 | node.set(child.getName(), helper(child)); 129 | } 130 | 131 | return node; 132 | } 133 | else 134 | { 135 | // copy file contents directly as a string. maybe need some b64 encoding here? 136 | try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) 137 | { 138 | IOUtils.copy(f, baos); 139 | return objectMapper.getNodeFactory().textNode(baos.toString(Charset.defaultCharset())); 140 | } 141 | catch (IOException ex) 142 | { 143 | fail("Could not read msp asset", ex); 144 | return null; 145 | } 146 | } 147 | } 148 | } 149 | 150 | @Test 151 | public void testConstructMSP() throws Exception 152 | { 153 | final MSP msp = 154 | new MSP("msp-com.example.orderer1", 155 | new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com")); 156 | 157 | log.info("msp:\n{}", yamlMapper.writeValueAsString(msp)); 158 | 159 | assertEquals("orderer1.example.com", msp.name); 160 | assertNotNull(msp.msp); 161 | 162 | assertTrue(msp.msp.has("admincerts")); 163 | assertTrue(msp.msp.has("cacerts")); 164 | assertTrue(msp.msp.has("keystore")); 165 | assertTrue(msp.msp.has("signcerts")); 166 | assertTrue(msp.msp.has("tlscacerts")); 167 | assertTrue(msp.msp.has("config.yaml")); 168 | 169 | assertTrue(msp.msp.get("cacerts").has("ca.example.com-cert.pem")); 170 | assertTrue(msp.msp.get("keystore").has("priv_sk")); 171 | assertTrue(msp.msp.get("signcerts").has("orderer1.example.com-cert.pem")); 172 | assertTrue(msp.msp.get("tlscacerts").has("tlsca.example.com-cert.pem")); 173 | 174 | assertTrue(msp.msp.get("cacerts").get("ca.example.com-cert.pem").isTextual()); 175 | assertTrue(msp.msp.get("keystore").get("priv_sk").isTextual()); 176 | assertTrue(msp.msp.get("signcerts").get("orderer1.example.com-cert.pem").isTextual()); 177 | assertTrue(msp.msp.get("tlscacerts").get("tlsca.example.com-cert.pem").isTextual()); 178 | assertTrue(msp.msp.get("config.yaml").isTextual()); 179 | } 180 | 181 | private static final MSP EXAMPLE_ORG_ORDERER1 = new MSP("msp-com.example.orderer1", new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com")); 182 | private static final MSP EXAMPLE_ORG_ORDERER2 = new MSP("msp-com.example.orderer2", new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com")); 183 | private static final MSP EXAMPLE_ORG_ORDERER3 = new MSP("msp-com.example.orderer3", new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com")); 184 | private static final MSP EXAMPLE_ORG_ORDERER4 = new MSP("msp-com.example.orderer4", new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer4.example.com")); 185 | private static final MSP EXAMPLE_ORG_ORDERER5 = new MSP("msp-com.example.orderer5", new File("config/crypto-config/ordererOrganizations/example.com/orderers/orderer5.example.com")); 186 | 187 | 188 | @Test 189 | public void testCreateMSPConfigMap() throws Exception 190 | { 191 | ConfigMap cm = null; 192 | try 193 | { 194 | cm = createMSPConfigMap(EXAMPLE_ORG_ORDERER1); 195 | 196 | log.info("Created MSP config:\n{}", yamlMapper.writeValueAsString(cm)); 197 | } 198 | finally 199 | { 200 | client.configMaps().delete(cm); 201 | } 202 | } 203 | 204 | @Test 205 | public void testMakeAFew() throws Exception 206 | { 207 | createMSPConfigMap(EXAMPLE_ORG_ORDERER1); 208 | createMSPConfigMap(EXAMPLE_ORG_ORDERER2); 209 | createMSPConfigMap(EXAMPLE_ORG_ORDERER3); 210 | createMSPConfigMap(EXAMPLE_ORG_ORDERER4); 211 | createMSPConfigMap(EXAMPLE_ORG_ORDERER5); 212 | } 213 | 214 | 215 | private ConfigMap createMSPConfigMap(final MSP msp) throws Exception 216 | { 217 | return client.configMaps() 218 | .create(new ConfigMapBuilder() 219 | .withNewMetadata() 220 | .withName(msp.name) 221 | // todo: add some metadata labels. 222 | .endMetadata() 223 | .withImmutable(true) 224 | .withData(Map.of(msp.name + ".yaml", yamlMapper.writeValueAsString(msp))) 225 | .build()); 226 | } 227 | 228 | 229 | 230 | 231 | // 232 | // This is an example peer yaml that includes an init container running the msp-unfurler. 233 | // 234 | // At runtime, the contents of the MSP folder will be unfurled into /var/hyperledger/fabric/xyzzy/orderer2.example.com/msp 235 | // 236 | 237 | /* 238 | 239 | --- 240 | apiVersion: apps/v1 241 | kind: Deployment 242 | metadata: 243 | namespace: test-network 244 | name: org1-peer1 245 | spec: 246 | replicas: 1 247 | selector: 248 | matchLabels: 249 | app: org1-peer1 250 | template: 251 | metadata: 252 | labels: 253 | app: org1-peer1 254 | spec: 255 | containers: 256 | - name: main 257 | image: hyperledger/fabric-peer:2.3.2 258 | imagePullPolicy: Always 259 | envFrom: 260 | - configMapRef: 261 | name: org1-peer1-config 262 | ports: 263 | - containerPort: 7051 264 | - containerPort: 7052 265 | - containerPort: 9443 266 | volumeMounts: 267 | - name: fabric-volume 268 | mountPath: /var/hyperledger/fabric 269 | - name: fabric-config 270 | mountPath: /var/hyperledger/fabric/config 271 | 272 | - name: msp-volume 273 | mountPath: /var/hyperledger/fabric/xyzzy 274 | 275 | initContainers: 276 | - name: msp-unfurl 277 | image: hyperledgendary/fabric-hyper-kube/fabctl-msp-unfurler 278 | imagePullPolicy: IfNotPresent 279 | env: 280 | - name: INPUT_FOLDER 281 | value: /var/hyperledger/fabric/msp-descriptors 282 | - name: OUTPUT_FOLDER 283 | value: /var/hyperledger/fabric/xyzzy 284 | volumeMounts: 285 | - name: msp-config 286 | mountPath: /var/hyperledger/fabric/msp-descriptors 287 | - name: msp-volume 288 | mountPath: /var/hyperledger/fabric/xyzzy 289 | 290 | volumes: 291 | - name: fabric-volume 292 | persistentVolumeClaim: 293 | claimName: fabric 294 | - name: fabric-config 295 | configMap: 296 | name: fabric-config 297 | - name: msp-config 298 | configMap: 299 | name: msp-com.example.orderer2 300 | - name: msp-volume 301 | emptyDir: {} 302 | 303 | */ 304 | } 305 | --------------------------------------------------------------------------------