<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Atsushin</title>
    <description>The latest articles on DEV Community by Atsushin (@nekia).</description>
    <link>https://dev.to/nekia</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F345094%2F03331e6f-cecc-469a-b1dd-fddea7190dc5.jpeg</url>
      <title>DEV Community: Atsushin</title>
      <link>https://dev.to/nekia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nekia"/>
    <language>en</language>
    <item>
      <title>How to manage actual Hyperledger Fabric network on your CI pipeline</title>
      <dc:creator>Atsushin</dc:creator>
      <pubDate>Wed, 04 Mar 2020 14:12:30 +0000</pubDate>
      <link>https://dev.to/nekia/how-to-manage-actual-hyperledger-fabric-network-on-your-ci-pipeline-5hfa</link>
      <guid>https://dev.to/nekia/how-to-manage-actual-hyperledger-fabric-network-on-your-ci-pipeline-5hfa</guid>
      <description>&lt;p&gt;When you develop an application running on blockchain, do you know how to organize your CI environment with using actual infrastructure? In this article, I'll share how to build a test suite for application running on blockchain network, in particularly, Hyperledger Fabric network that is one of OSS blockchain framework.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup environment
&lt;/h1&gt;

&lt;p&gt;You need to follow the steps described in the official document.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hyperledger-fabric.readthedocs.io/en/release-1.4/prereqs.html"&gt;Prerequisites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hyperledger-fabric.readthedocs.io/en/release-1.4/install.html"&gt;Install Samples, Binaries and Docker Images&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, the following environment is assumed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 18.04.3 LTS&lt;/li&gt;
&lt;li&gt;Go 1.13.8&lt;/li&gt;
&lt;li&gt;Hyperledger Fabric 1.4.6&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Create your workspace
&lt;/h1&gt;

&lt;p&gt;You can clone sample code from &lt;a href="https://github.com/nekia/devto1"&gt;https://github.com/nekia/devto1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir -p dev/hlf-e2e-test/specs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After initialize go module env, you need to get &lt;a href="mailto:fabric-test@v1.4.4"&gt;fabric-test@v1.4.4&lt;/a&gt; go package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pushd dev/hlf-e2e-test/specs
$ go mod init example.com/hlftest
$ go get github.com/hyperledger/fabric-test@v1.4.4
$ popd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  fabric-test
&lt;/h2&gt;

&lt;p&gt;fabric-test repository is tool set for testing the Hyperledger Fabric. The repository is officially maintained Hyperledger community. In this article, the following tools are mainly used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hyperledger/fabric-test/tree/master/tools/operator"&gt;Operator&lt;/a&gt;&lt;br&gt;
Operator provides some go package to manage Hyperledger Fabric network with Go program. It also provides CLI as well. In this article, I'll introduce the way how to use this package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hyperledger/fabric-test/tree/master/tools/PTE"&gt;PTE (Performance Traffic Engine)&lt;/a&gt;&lt;br&gt;
PTE provides CLI to interact with Hyperledger Fabric networks by sending requests to and receiving responses from the network via Hyperledger Fabric Node.js SDK. We don't cover this tool because in our use case PTE is used by Operator, there is no chance for user to use PTE directly in this scenario.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pushd dev/hlf-e2e-test
$ git clone https://github.com/hyperledger/fabric-test.git -b v1.4.4
$ ln -s fabric-test/tools/PTE PTE
$ cd PTE
$ npm install fabric-client@1.4.7
$ npm install fabric-ca-client@1.4.7
$ popd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;User need to not only install fabric-test as a go package by using go module function, but also close fabric-test source tree to use PTE (Node.js applicaton) included in the source repository. And PTE seems to be assumed that it is used from component within the fabric-test source tree. Because of that, we still need to keep some relative layout of directory for using PTE outside of the source tree. That's why creating symbolic link above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pushd dev/hlf-e2e-test
$ cp -a fabric-test/tools/operator/templates specs/
$ cp fabric-test/tools/operator/testdata/smoke-*.yml specs/
$ popd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And some files included in templates directory are also important to automatically generate each configuration file such as configtx.yaml, docker-compose.yaml, etc. These template are consumed by ytt tool.&lt;/p&gt;

&lt;p&gt;After all the steps up to here, you need to modify network specification file copied from testdata directory. As you can see in the comment, there is special naming rule to switch docker registry. To retrieve container image from Docker Hub, we need to remove &lt;code&gt;-stable&lt;/code&gt; postfix from &lt;code&gt;fabric_version&lt;/code&gt;. The rest of changes are optional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/specs/smoke-network-spec.yml
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/specs/smoke-network-spec.yml
&lt;/span&gt;&lt;span class="p"&gt;@@ -7,7 +7,7 @@&lt;/span&gt;
 #! Released images are pulled from docker hub hyperledger/, e.g. 1.4.1 or 2.0.0
 #! Development stream images are pulled from
 #! nexus3.hyperledger.org:10001/hyperledger/, e.g. 1.4.1-stable or 2.0.0-stable
&lt;span class="gd"&gt;-fabric_version: 1.4.4-stable
&lt;/span&gt;&lt;span class="gi"&gt;+fabric_version: 1.4.6
&lt;/span&gt; #! peer database ledger type (couchdb, goleveldb)
 db_type: goleveldb
 #! This parameter is used to define fabric logging spec in peers
&lt;span class="p"&gt;@@ -58,18 +58,18 @@&lt;/span&gt; kafka:
 orderer_organizations:
 - name: ordererorg1
   msp_id: OrdererOrgExampleCom
&lt;span class="gd"&gt;-  num_orderers: 1
&lt;/span&gt;&lt;span class="gi"&gt;+  num_orderers: 5
&lt;/span&gt;   num_ca: 0

 peer_organizations:
 - name: org1
   msp_id: Org1ExampleCom
&lt;span class="gd"&gt;-  num_peers: 1
&lt;/span&gt;&lt;span class="gi"&gt;+  num_peers: 3
&lt;/span&gt;   num_ca: 1

 - name: org2
   msp_id: Org2ExampleCom
&lt;span class="gd"&gt;-  num_peers: 1
&lt;/span&gt;&lt;span class="gi"&gt;+  num_peers: 3
&lt;/span&gt;   num_ca: 1

 #! Capabilites for Orderer, Channel, Application groups
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are 2 type of specification files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;network specification file (ex. smoke-network-spec.yml)&lt;br&gt;
This file defines the specification of Hyperledger Fabric network deployed by using operator tool. This file is mainly consumed by API of networkclient package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;input file (ex. smoke-test-input.yml)&lt;br&gt;
This file defines each action such as creating channels, joining peers to a channel, etc. This file is mainly consumed by API of testclient package.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Prepare test code
&lt;/h1&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;hlftest&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"testing"&lt;/span&gt;

    &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"github.com/onsi/ginkgo"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/onsi/ginkgo/reporters"&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"github.com/onsi/gomega"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/hyperledger/fabric-test/tools/operator/launcher"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/hyperledger/fabric-test/tools/operator/testclient"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestSmoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;RegisterFailHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Fail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;junitReporter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;reporters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewJUnitReporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"results_smoke-test-suite.xml"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;RunSpecsWithDefaultAndCustomReporters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Smoke Test Suite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Reporter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;junitReporter&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Bringing up network using BeforeSuite&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BeforeSuite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;networkSpecPath&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"smoke-network-spec.yml"&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;launcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Launcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"up"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;networkSpecPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HaveOccurred&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Operator Demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"starting fabric network"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;inputSpecPath&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"smoke-test-input.yml"&lt;/span&gt;

        &lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1) Creating channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"create"&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;testclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Testclient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputSpecPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HaveOccurred&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2) Joining Peers to channel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"join"&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testclient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Testclient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputSpecPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HaveOccurred&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c"&gt;// Cleaning up network launched from BeforeSuite and removing all chaincode containers&lt;/span&gt;
&lt;span class="c"&gt;// and chaincode container images using AfterSuite&lt;/span&gt;

&lt;span class="c"&gt;// var _ = AfterSuite(func() {&lt;/span&gt;
&lt;span class="c"&gt;//  networkSpecPath := "smoke-network-spec.yml"&lt;/span&gt;
&lt;span class="c"&gt;//  err := launcher.Launcher("down", "docker", "", networkSpecPath)&lt;/span&gt;
&lt;span class="c"&gt;//  Expect(err).NotTo(HaveOccurred())&lt;/span&gt;

&lt;span class="c"&gt;//  dockerList := []string{"ps", "-aq", "-f", "status=exited"}&lt;/span&gt;
&lt;span class="c"&gt;//  containerList, _ := networkclient.ExecuteCommand("docker", dockerList, false)&lt;/span&gt;
&lt;span class="c"&gt;//  if containerList != "" {&lt;/span&gt;
&lt;span class="c"&gt;//      list := strings.Split(containerList, "\n")&lt;/span&gt;
&lt;span class="c"&gt;//      containerArgs := []string{"rm", "-f"}&lt;/span&gt;
&lt;span class="c"&gt;//      containerArgs = append(containerArgs, list...)&lt;/span&gt;
&lt;span class="c"&gt;//      networkclient.ExecuteCommand("docker", containerArgs, true)&lt;/span&gt;
&lt;span class="c"&gt;//  }&lt;/span&gt;
&lt;span class="c"&gt;//  ccimagesList := []string{"images", "-q", "--filter=reference=dev*"}&lt;/span&gt;
&lt;span class="c"&gt;//  images, _ := networkclient.ExecuteCommand("docker", ccimagesList, false)&lt;/span&gt;
&lt;span class="c"&gt;//  if images != "" {&lt;/span&gt;
&lt;span class="c"&gt;//      list := strings.Split(images, "\n")&lt;/span&gt;
&lt;span class="c"&gt;//      imageArgs := []string{"rmi", "-f"}&lt;/span&gt;
&lt;span class="c"&gt;//      imageArgs = append(imageArgs, list...)&lt;/span&gt;
&lt;span class="c"&gt;//      networkclient.ExecuteCommand("docker", imageArgs, true)&lt;/span&gt;
&lt;span class="c"&gt;//  }&lt;/span&gt;
&lt;span class="c"&gt;// })&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Now, bring up network
&lt;/h1&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pushd dev/hlf-e2e-test/specs
$ ginkgo -v
Running Suite: Smoke Test Suite
===============================
Random Seed: 1583242390
Will run 0 of 0 specs

(snip)

Ran 0 of 0 Specs in 29.661 seconds
SUCCESS! -- 0 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS

Ginkgo ran 1 suite in 30.376068459s
Test Suite Passed

$ docker ps --format '{{.Names}}\t{{.Status}}\t{{.Image}}' | sort
ca0-org1    Up 2 minutes    hyperledger/fabric-ca:1.4.6
ca0-org2    Up 2 minutes    hyperledger/fabric-ca:1.4.6
orderer0-ordererorg1    Up 2 minutes    hyperledger/fabric-orderer:1.4.6
orderer1-ordererorg1    Up 2 minutes    hyperledger/fabric-orderer:1.4.6
orderer2-ordererorg1    Up 2 minutes    hyperledger/fabric-orderer:1.4.6
orderer3-ordererorg1    Up 2 minutes    hyperledger/fabric-orderer:1.4.6
orderer4-ordererorg1    Up 2 minutes    hyperledger/fabric-orderer:1.4.6
peer0-org1  Up 2 minutes    hyperledger/fabric-peer:1.4.6
peer0-org2  Up 2 minutes    hyperledger/fabric-peer:1.4.6
peer1-org1  Up 2 minutes    hyperledger/fabric-peer:1.4.6
peer1-org2  Up 2 minutes    hyperledger/fabric-peer:1.4.6
peer2-org1  Up 2 minutes    hyperledger/fabric-peer:1.4.6
peer2-org2  Up 2 minutes    hyperledger/fabric-peer:1.4.6

$ docker exec peer0-org1 peer channel list
Channels peers has joined: 
testorgschannel0
$ popd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
      <category>hyperledger</category>
      <category>go</category>
      <category>ci</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
