github.com/hyperledger-labs/bdls@v2.1.1+incompatible/integration/lifecycle/lifecycle_test.go (about)

     1  /*
     2  Copyright IBM Corp All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package lifecycle
     8  
     9  import (
    10  	"bytes"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"syscall"
    15  
    16  	docker "github.com/fsouza/go-dockerclient"
    17  	"github.com/golang/protobuf/proto"
    18  	"github.com/hyperledger/fabric-protos-go/common"
    19  	"github.com/hyperledger/fabric/common/tools/protolator"
    20  	"github.com/hyperledger/fabric/common/tools/protolator/protoext/ordererext"
    21  	"github.com/hyperledger/fabric/integration/nwo"
    22  	"github.com/hyperledger/fabric/integration/nwo/commands"
    23  	"github.com/hyperledger/fabric/integration/nwo/fabricconfig"
    24  	"github.com/hyperledger/fabric/integration/runner"
    25  	"github.com/onsi/gomega/gbytes"
    26  	"github.com/onsi/gomega/gexec"
    27  	"github.com/tedsuo/ifrit"
    28  
    29  	. "github.com/onsi/ginkgo"
    30  	. "github.com/onsi/gomega"
    31  	. "github.com/onsi/gomega/gstruct"
    32  )
    33  
    34  var _ = Describe("Lifecycle", func() {
    35  	var (
    36  		client    *docker.Client
    37  		testDir   string
    38  		network   *nwo.Network
    39  		processes = map[string]ifrit.Process{}
    40  		termFiles []string
    41  	)
    42  
    43  	BeforeEach(func() {
    44  		var err error
    45  		testDir, err = ioutil.TempDir("", "lifecycle")
    46  		Expect(err).NotTo(HaveOccurred())
    47  
    48  		client, err = docker.NewClientFromEnv()
    49  		Expect(err).NotTo(HaveOccurred())
    50  
    51  		network = nwo.New(nwo.BasicSolo(), testDir, client, StartPort(), components)
    52  
    53  		// Generate config
    54  		network.GenerateConfigTree()
    55  
    56  		// configure only one of four peers (Org1, peer0) to use couchdb.
    57  		// Note that we do not support a channel with mixed DBs.
    58  		// However, for testing, it would be fine to use couchdb for one
    59  		// peer. We're using couchdb here to ensure all supported character
    60  		// classes in chaincode names/versions work on the supported db types.
    61  		couchDB := &runner.CouchDB{}
    62  		couchProcess := ifrit.Invoke(couchDB)
    63  		Eventually(couchProcess.Ready(), runner.DefaultStartTimeout).Should(BeClosed())
    64  		Consistently(couchProcess.Wait()).ShouldNot(Receive())
    65  		couchAddr := couchDB.Address()
    66  		peer := network.Peer("Org1", "peer0")
    67  		core := network.ReadPeerConfig(peer)
    68  		core.Ledger.State.StateDatabase = "CouchDB"
    69  		core.Ledger.State.CouchDBConfig.CouchDBAddress = couchAddr
    70  		processes[couchDB.Name] = couchProcess
    71  		setTermFileEnvForBinaryExternalBuilder := func(envKey, termFile string, externalBuilders []fabricconfig.ExternalBuilder) {
    72  			os.Setenv(envKey, termFile)
    73  			for i, e := range externalBuilders {
    74  				if e.Name == "binary" {
    75  					e.EnvironmentWhitelist = append(e.EnvironmentWhitelist, envKey)
    76  					externalBuilders[i] = e
    77  				}
    78  			}
    79  		}
    80  		org1TermFile := filepath.Join(testDir, "org1-term-file")
    81  		setTermFileEnvForBinaryExternalBuilder("ORG1_TERM_FILE", org1TermFile, core.Chaincode.ExternalBuilders)
    82  		network.WritePeerConfig(peer, core)
    83  
    84  		peer = network.Peer("Org2", "peer0")
    85  		core = network.ReadPeerConfig(peer)
    86  		org2TermFile := filepath.Join(testDir, "org2-term-file")
    87  		setTermFileEnvForBinaryExternalBuilder("ORG2_TERM_FILE", org2TermFile, core.Chaincode.ExternalBuilders)
    88  		network.WritePeerConfig(peer, core)
    89  
    90  		termFiles = []string{org1TermFile, org2TermFile}
    91  
    92  		// bootstrap the network
    93  		network.Bootstrap()
    94  
    95  		for _, o := range network.Orderers {
    96  			or := network.OrdererRunner(o)
    97  			p := ifrit.Invoke(or)
    98  			processes[o.ID()] = p
    99  			Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed())
   100  		}
   101  
   102  		for _, peer := range network.Peers {
   103  			pr := network.PeerRunner(peer)
   104  			p := ifrit.Invoke(pr)
   105  			processes[peer.ID()] = p
   106  			Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed())
   107  		}
   108  	})
   109  
   110  	AfterEach(func() {
   111  		// Shutdown processes and cleanup
   112  		for _, p := range processes {
   113  			p.Signal(syscall.SIGTERM)
   114  			Eventually(p.Wait(), network.EventuallyTimeout).Should(Receive())
   115  		}
   116  		network.Cleanup()
   117  
   118  		os.RemoveAll(testDir)
   119  	})
   120  
   121  	It("deploys and executes chaincode using _lifecycle and upgrades it", func() {
   122  		orderer := network.Orderer("orderer")
   123  		testPeers := network.PeersWithChannel("testchannel")
   124  		org1peer0 := network.Peer("Org1", "peer0")
   125  
   126  		chaincodePath := components.Build("github.com/hyperledger/fabric/integration/chaincode/module")
   127  		chaincode := nwo.Chaincode{
   128  			Name:                "My_1st-Chaincode",
   129  			Version:             "Version-0.0",
   130  			Path:                chaincodePath,
   131  			Lang:                "binary",
   132  			PackageFile:         filepath.Join(testDir, "modulecc.tar.gz"),
   133  			Ctor:                `{"Args":["init","a","100","b","200"]}`,
   134  			ChannelConfigPolicy: "/Channel/Application/Endorsement",
   135  			Sequence:            "1",
   136  			InitRequired:        true,
   137  			Label:               "my_simple_chaincode",
   138  		}
   139  
   140  		By("setting up the channel")
   141  		network.CreateAndJoinChannels(orderer)
   142  		network.UpdateChannelAnchors(orderer, "testchannel")
   143  		network.VerifyMembership(network.PeersWithChannel("testchannel"), "testchannel")
   144  		nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0"))
   145  
   146  		By("deploying the chaincode")
   147  		nwo.PackageChaincodeBinary(chaincode)
   148  		chaincode.SetPackageIDFromPackageFile()
   149  
   150  		nwo.InstallChaincode(network, chaincode, testPeers...)
   151  
   152  		By("verifying the installed chaincode package matches the one that was submitted")
   153  		sess, err := network.PeerAdminSession(testPeers[0], commands.ChaincodeGetInstalledPackage{
   154  			PackageID:       chaincode.PackageID,
   155  			OutputDirectory: testDir,
   156  		})
   157  		Expect(err).NotTo(HaveOccurred())
   158  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
   159  		fileBytes, err := ioutil.ReadFile(chaincode.PackageFile)
   160  		Expect(err).NotTo(HaveOccurred())
   161  		fileBytesFromPeer, err := ioutil.ReadFile(filepath.Join(network.RootDir, chaincode.PackageID+".tar.gz"))
   162  		Expect(err).NotTo(HaveOccurred())
   163  		Expect(fileBytesFromPeer).To(Equal(fileBytes))
   164  
   165  		nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, testPeers...)
   166  
   167  		nwo.CheckCommitReadinessUntilReady(network, "testchannel", chaincode, network.PeerOrgs(), testPeers...)
   168  		nwo.CommitChaincode(network, "testchannel", orderer, chaincode, testPeers[0], testPeers...)
   169  		nwo.InitChaincode(network, "testchannel", orderer, chaincode, testPeers...)
   170  
   171  		By("ensuring the chaincode can be invoked and queried")
   172  		endorsers := []*nwo.Peer{
   173  			network.Peer("Org1", "peer0"),
   174  			network.Peer("Org2", "peer0"),
   175  		}
   176  		RunQueryInvokeQuery(network, orderer, "My_1st-Chaincode", 100, endorsers...)
   177  
   178  		By("setting a bad package ID to temporarily disable endorsements on org1")
   179  		savedPackageID := chaincode.PackageID
   180  		// note that in theory it should be sufficient to set it to an
   181  		// empty string, but the ApproveChaincodeForMyOrg
   182  		// function fills the packageID field if empty
   183  		chaincode.PackageID = "bad"
   184  		nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, org1peer0)
   185  
   186  		By("querying the chaincode and expecting the invocation to fail")
   187  		sess, err = network.PeerUserSession(org1peer0, "User1", commands.ChaincodeQuery{
   188  			ChannelID: "testchannel",
   189  			Name:      "My_1st-Chaincode",
   190  			Ctor:      `{"Args":["query","a"]}`,
   191  		})
   192  		Expect(err).NotTo(HaveOccurred())
   193  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1))
   194  		Expect(sess.Err).To(gbytes.Say("Error: endorsement failure during query. response: status:500 " +
   195  			"message:\"make sure the chaincode My_1st-Chaincode has been successfully defined on channel testchannel and try " +
   196  			"again: chaincode definition for 'My_1st-Chaincode' exists, but chaincode is not installed\""))
   197  
   198  		By("setting the correct package ID to restore the chaincode")
   199  		chaincode.PackageID = savedPackageID
   200  		nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, org1peer0)
   201  
   202  		By("querying the chaincode and expecting the invocation to succeed")
   203  		sess, err = network.PeerUserSession(org1peer0, "User1", commands.ChaincodeQuery{
   204  			ChannelID: "testchannel",
   205  			Name:      "My_1st-Chaincode",
   206  			Ctor:      `{"Args":["query","a"]}`,
   207  		})
   208  		Expect(err).NotTo(HaveOccurred())
   209  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
   210  		Expect(sess).To(gbytes.Say("90"))
   211  
   212  		By("upgrading the chaincode to sequence 2 using a new chaincode package")
   213  		previousLabel := chaincode.Label
   214  		previousPackageID := chaincode.PackageID
   215  		chaincode.Label = "my_simple_chaincode_updated"
   216  		chaincode.PackageFile = filepath.Join(testDir, "modulecc-updated.tar.gz")
   217  		chaincode.Sequence = "2"
   218  		nwo.PackageChaincodeBinary(chaincode)
   219  		chaincode.SetPackageIDFromPackageFile()
   220  		nwo.InstallChaincode(network, chaincode, testPeers...)
   221  
   222  		nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, testPeers...)
   223  
   224  		nwo.CheckCommitReadinessUntilReady(network, "testchannel", chaincode, network.PeerOrgs(), testPeers...)
   225  		nwo.CommitChaincode(network, "testchannel", orderer, chaincode, testPeers[0], testPeers...)
   226  
   227  		By("listing the installed chaincodes and verifying the channel/chaincode definitions that are using the chaincode package")
   228  		nwo.QueryInstalledReferences(network, "testchannel", chaincode.Label, chaincode.PackageID, network.Peer("Org2", "peer0"), []string{"My_1st-Chaincode", "Version-0.0"})
   229  
   230  		By("checking for term files that are written when the chaincode is stopped via SIGTERM")
   231  		for _, termFile := range termFiles {
   232  			Expect(termFile).To(BeARegularFile())
   233  		}
   234  
   235  		By("ensuring the previous chaincode package is no longer referenced by a chaincode definition on the channel")
   236  		Expect(nwo.QueryInstalled(network, network.Peer("Org2", "peer0"))()).To(
   237  			ContainElement(MatchFields(IgnoreExtras,
   238  				Fields{
   239  					"Label":      Equal(previousLabel),
   240  					"PackageId":  Equal(previousPackageID),
   241  					"References": Not(HaveKey("testchannel")),
   242  				},
   243  			)),
   244  		)
   245  
   246  		By("ensuring the chaincode can still be invoked and queried")
   247  		RunQueryInvokeQuery(network, orderer, "My_1st-Chaincode", 90, endorsers...)
   248  
   249  		By("deploying another chaincode using the same chaincode package")
   250  		anotherChaincode := nwo.Chaincode{
   251  			Name:                "Your_Chaincode",
   252  			Version:             "Version+0_0",
   253  			Path:                chaincodePath,
   254  			Lang:                "binary",
   255  			PackageFile:         filepath.Join(testDir, "modulecc.tar.gz"),
   256  			Ctor:                `{"Args":["init","a","100","b","200"]}`,
   257  			ChannelConfigPolicy: "/Channel/Application/Endorsement",
   258  			Sequence:            "1",
   259  			InitRequired:        true,
   260  			Label:               "my_simple_chaincode",
   261  		}
   262  		nwo.DeployChaincode(network, "testchannel", orderer, anotherChaincode)
   263  
   264  		By("listing the installed chaincodes and verifying the channel/chaincode definitions that are using the chaincode package")
   265  		anotherChaincode.SetPackageIDFromPackageFile()
   266  		nwo.QueryInstalledReferences(network, "testchannel", anotherChaincode.Label, anotherChaincode.PackageID, network.Peer("Org2", "peer0"), []string{"Your_Chaincode", "Version+0_0"})
   267  
   268  		By("adding a new org")
   269  		org3 := &nwo.Organization{
   270  			MSPID:         "Org3MSP",
   271  			Name:          "Org3",
   272  			Domain:        "org3.example.com",
   273  			EnableNodeOUs: true,
   274  			Users:         2,
   275  			CA: &nwo.CA{
   276  				Hostname: "ca",
   277  			},
   278  		}
   279  
   280  		org3peer0 := &nwo.Peer{
   281  			Name:         "peer0",
   282  			Organization: "Org3",
   283  			Channels:     testPeers[0].Channels,
   284  		}
   285  
   286  		network.AddOrg(org3, org3peer0)
   287  		GenerateOrgUpdateMaterials(network, org3peer0)
   288  
   289  		By("starting the org3 peer")
   290  		pr := network.PeerRunner(org3peer0)
   291  		org3Process := ifrit.Invoke(pr)
   292  		processes[org3peer0.ID()] = org3Process
   293  		Eventually(org3Process.Ready(), network.EventuallyTimeout).Should(BeClosed())
   294  
   295  		By("updating the channel config to include org3")
   296  		// get the current channel config
   297  		currentConfig := nwo.GetConfig(network, testPeers[0], orderer, "testchannel")
   298  		updatedConfig := proto.Clone(currentConfig).(*common.Config)
   299  
   300  		// get the configtx info for org3
   301  		sess, err = network.ConfigTxGen(commands.PrintOrg{
   302  			ConfigPath: network.RootDir,
   303  			PrintOrg:   "Org3",
   304  		})
   305  		Expect(err).NotTo(HaveOccurred())
   306  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
   307  		org3Group := &ordererext.DynamicOrdererOrgGroup{ConfigGroup: &common.ConfigGroup{}}
   308  		err = protolator.DeepUnmarshalJSON(bytes.NewBuffer(sess.Out.Contents()), org3Group)
   309  		Expect(err).NotTo(HaveOccurred())
   310  
   311  		// update the channel config to include org3
   312  		updatedConfig.ChannelGroup.Groups["Application"].Groups["Org3"] = org3Group.ConfigGroup
   313  		nwo.UpdateConfig(network, orderer, "testchannel", currentConfig, updatedConfig, true, testPeers[0], testPeers...)
   314  
   315  		By("joining the org3 peers to the channel")
   316  		network.JoinChannel("testchannel", orderer, org3peer0)
   317  
   318  		// update testPeers now that org3 has joined
   319  		testPeers = network.PeersWithChannel("testchannel")
   320  
   321  		// wait until all peers, particularly those in org3, have received the block
   322  		// containing the updated config
   323  		maxLedgerHeight := nwo.GetMaxLedgerHeight(network, "testchannel", testPeers...)
   324  		nwo.WaitUntilEqualLedgerHeight(network, "testchannel", maxLedgerHeight, testPeers...)
   325  
   326  		By("querying definitions by org3 before performing any chaincode actions")
   327  		sess, err = network.PeerAdminSession(network.Peer("Org2", "peer0"), commands.ChaincodeListCommitted{
   328  			ChannelID: "testchannel",
   329  		})
   330  		Expect(err).NotTo(HaveOccurred())
   331  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
   332  
   333  		By("installing the chaincode to the org3 peers")
   334  		nwo.InstallChaincode(network, chaincode, org3peer0)
   335  
   336  		By("ensuring org3 peers do not execute the chaincode before approving the definition")
   337  		org3AndOrg1PeerAddresses := []string{
   338  			network.PeerAddress(org3peer0, nwo.ListenPort),
   339  			network.PeerAddress(org1peer0, nwo.ListenPort),
   340  		}
   341  
   342  		sess, err = network.PeerUserSession(org3peer0, "User1", commands.ChaincodeInvoke{
   343  			ChannelID:     "testchannel",
   344  			Orderer:       network.OrdererAddress(orderer, nwo.ListenPort),
   345  			Name:          "My_1st-Chaincode",
   346  			Ctor:          `{"Args":["invoke","a","b","10"]}`,
   347  			PeerAddresses: org3AndOrg1PeerAddresses,
   348  			WaitForEvent:  true,
   349  		})
   350  		Expect(err).NotTo(HaveOccurred())
   351  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1))
   352  		Expect(sess.Err).To(gbytes.Say("chaincode definition for 'My_1st-Chaincode' at sequence 2 on channel 'testchannel' has not yet been approved by this org"))
   353  
   354  		By("org3 approving the chaincode definition")
   355  		nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, network.PeersInOrg("Org3")...)
   356  		nwo.EnsureChaincodeCommitted(network, "testchannel", chaincode.Name, chaincode.Version, chaincode.Sequence, []*nwo.Organization{network.Organization("Org1"), network.Organization("Org2"), network.Organization("Org3")}, org3peer0)
   357  
   358  		By("ensuring chaincode can be invoked and queried by org3")
   359  		org3andOrg1Endorsers := []*nwo.Peer{
   360  			network.Peer("Org3", "peer0"),
   361  			network.Peer("Org1", "peer0"),
   362  		}
   363  		RunQueryInvokeQuery(network, orderer, "My_1st-Chaincode", 80, org3andOrg1Endorsers...)
   364  
   365  		By("deploying a chaincode without an endorsement policy specified")
   366  		chaincode = nwo.Chaincode{
   367  			Name:         "defaultpolicycc",
   368  			Version:      "0.0",
   369  			Path:         chaincodePath,
   370  			Lang:         "binary",
   371  			PackageFile:  filepath.Join(testDir, "modulecc.tar.gz"),
   372  			Ctor:         `{"Args":["init","a","100","b","200"]}`,
   373  			Sequence:     "1",
   374  			InitRequired: true,
   375  			Label:        "my_simple_chaincode",
   376  		}
   377  
   378  		nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
   379  
   380  		By("attempting to invoke the chaincode without a majority")
   381  		sess, err = network.PeerUserSession(org3peer0, "User1", commands.ChaincodeInvoke{
   382  			ChannelID:    "testchannel",
   383  			Orderer:      network.OrdererAddress(orderer, nwo.ListenPort),
   384  			Name:         "defaultpolicycc",
   385  			Ctor:         `{"Args":["invoke","a","b","10"]}`,
   386  			WaitForEvent: true,
   387  		})
   388  		Expect(err).ToNot(HaveOccurred())
   389  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1))
   390  		Expect(sess.Err).To(gbytes.Say(`\QError: transaction invalidated with status (ENDORSEMENT_POLICY_FAILURE)\E`))
   391  
   392  		By("attempting to invoke the chaincode with a majority")
   393  		sess, err = network.PeerUserSession(org3peer0, "User1", commands.ChaincodeInvoke{
   394  			ChannelID:     "testchannel",
   395  			Orderer:       network.OrdererAddress(orderer, nwo.ListenPort),
   396  			Name:          "defaultpolicycc",
   397  			Ctor:          `{"Args":["invoke","a","b","10"]}`,
   398  			PeerAddresses: org3AndOrg1PeerAddresses,
   399  			WaitForEvent:  true,
   400  		})
   401  		Expect(err).NotTo(HaveOccurred())
   402  		Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
   403  		Expect(sess.Err).To(gbytes.Say(`\Qcommitted with status (VALID)\E`))
   404  		Expect(sess.Err).To(gbytes.Say(`Chaincode invoke successful. result: status:200`))
   405  	})
   406  })