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 })