github.com/lzy4123/fabric@v2.1.1+incompatible/integration/ledger/couchdb_indexes_test.go (about) 1 /* 2 Copyright IBM Corp All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package ledger 8 9 import ( 10 "encoding/json" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "syscall" 15 16 docker "github.com/fsouza/go-dockerclient" 17 "github.com/hyperledger/fabric/integration/nwo" 18 "github.com/hyperledger/fabric/integration/nwo/commands" 19 "github.com/hyperledger/fabric/integration/nwo/fabricconfig" 20 "github.com/hyperledger/fabric/integration/runner" 21 . "github.com/onsi/ginkgo" 22 . "github.com/onsi/gomega" 23 "github.com/onsi/gomega/gbytes" 24 "github.com/onsi/gomega/gexec" 25 "github.com/tedsuo/ifrit" 26 ) 27 28 const ( 29 chaincodePathWithNoIndex = "github.com/hyperledger/fabric/integration/chaincode/marbles/cmd" 30 chaincodePathWithIndex = "github.com/hyperledger/fabric/integration/chaincode/marbles/cmdwithindexspec" 31 chaincodePathWithIndexes = "github.com/hyperledger/fabric/integration/chaincode/marbles/cmdwithindexspecs" 32 ) 33 34 var ( 35 filesWithIndex = map[string]string{ 36 "../chaincode/marbles/cmdwithindexspec/META-INF/statedb/couchdb/indexes/indexSizeSortDoc.json": "metadata/statedb/couchdb/indexes/indexSizeSortDoc.json", 37 } 38 39 filesWithIndices = map[string]string{ 40 "../chaincode/marbles/cmdwithindexspecs/META-INF/statedb/couchdb/indexes/indexSizeSortDoc.json": "metadata/statedb/couchdb/indexes/indexSizeSortDoc.json", 41 "../chaincode/marbles/cmdwithindexspecs/META-INF/statedb/couchdb/indexes/indexColorSortDoc.json": "metadata/statedb/couchdb/indexes/indexColorSortDoc.json", 42 } 43 ) 44 45 var _ = Describe("CouchDB indexes", func() { 46 var ( 47 testDir string 48 client *docker.Client 49 network *nwo.Network 50 orderer *nwo.Orderer 51 process ifrit.Process 52 53 couchAddr string 54 couchDB *runner.CouchDB 55 couchProcess ifrit.Process 56 57 legacyChaincode nwo.Chaincode 58 newlifecycleChaincode nwo.Chaincode 59 ) 60 61 BeforeEach(func() { 62 var err error 63 testDir, err = ioutil.TempDir("", "ledger") 64 Expect(err).NotTo(HaveOccurred()) 65 client, err = docker.NewClientFromEnv() 66 Expect(err).NotTo(HaveOccurred()) 67 68 network = nwo.New(nwo.FullSolo(), testDir, client, StartPort(), components) 69 70 cwd, err := os.Getwd() 71 Expect(err).NotTo(HaveOccurred()) 72 network.ExternalBuilders = append(network.ExternalBuilders, fabricconfig.ExternalBuilder{ 73 Path: filepath.Join(cwd, "..", "externalbuilders", "golang"), 74 Name: "external-golang", 75 EnvironmentWhitelist: []string{"GOPATH", "GOCACHE", "GOPROXY", "HOME", "PATH"}, 76 }) 77 78 network.GenerateConfigTree() 79 80 // configure only one of four peers (Org1, peer0) to use couchdb. 81 // Note that we do not support a channel with mixed DBs. 82 // However, for testing, it would be fine to use couchdb for one 83 // peer and sending all the couchdb related test queries to this peer 84 couchDB = &runner.CouchDB{} 85 couchProcess = ifrit.Invoke(couchDB) 86 Eventually(couchProcess.Ready(), runner.DefaultStartTimeout).Should(BeClosed()) 87 Consistently(couchProcess.Wait()).ShouldNot(Receive()) 88 couchAddr = couchDB.Address() 89 peer := network.Peer("Org1", "peer0") 90 core := network.ReadPeerConfig(peer) 91 core.Ledger.State.StateDatabase = "CouchDB" 92 core.Ledger.State.CouchDBConfig.CouchDBAddress = couchAddr 93 network.WritePeerConfig(peer, core) 94 95 // start the network 96 network.Bootstrap() 97 networkRunner := network.NetworkGroupRunner() 98 process = ifrit.Invoke(networkRunner) 99 Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed()) 100 orderer = network.Orderer("orderer") 101 network.CreateAndJoinChannel(orderer, "testchannel") 102 network.UpdateChannelAnchors(orderer, "testchannel") 103 network.VerifyMembership(network.PeersWithChannel("testchannel"), "testchannel") 104 105 legacyChaincode = nwo.Chaincode{ 106 Name: "marbles", 107 Version: "0.0", 108 Path: chaincodePathWithIndex, 109 Ctor: `{"Args":[]}`, 110 Policy: `OR ('Org1MSP.member','Org2MSP.member')`, 111 PackageFile: filepath.Join(testDir, "marbles_legacy.tar.gz"), 112 } 113 114 newlifecycleChaincode = nwo.Chaincode{ 115 Name: "marbles", 116 Version: "0.0", 117 Path: components.Build(chaincodePathWithIndex), 118 Lang: "binary", 119 CodeFiles: filesWithIndex, 120 PackageFile: filepath.Join(testDir, "marbles.tar.gz"), 121 SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member')`, 122 Sequence: "1", 123 Label: "marbles", 124 } 125 }) 126 127 AfterEach(func() { 128 process.Signal(syscall.SIGTERM) 129 Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive()) 130 couchProcess.Signal(syscall.SIGTERM) 131 Eventually(couchProcess.Wait(), network.EventuallyTimeout).Should(Receive()) 132 network.Cleanup() 133 os.RemoveAll(testDir) 134 }) 135 136 When("chaincode is installed and instantiated via legacy lifecycle", func() { 137 It("creates indexes", func() { 138 nwo.PackageChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0")) 139 nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0")) 140 nwo.InstantiateChaincodeLegacy(network, "testchannel", orderer, legacyChaincode, network.Peer("Org1", "peer0"), network.Peers...) 141 initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_indexed") 142 verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 143 }) 144 }) 145 146 When("chaincode is deployed via new lifecycle (using the docker chaincode build) ", func() { 147 BeforeEach(func() { 148 newlifecycleChaincode.Path = chaincodePathWithIndex 149 newlifecycleChaincode.Lang = "golang" 150 }) 151 152 It("creates indexes", func() { 153 nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0")) 154 nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode, network.Peers...) 155 initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_indexed") 156 verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 157 }) 158 }) 159 160 When("chaincode is defined and installed via new lifecycle and then upgraded with an additional index", func() { 161 It("creates indexes", func() { 162 nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0")) 163 nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode, network.Peers...) 164 initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_indexed") 165 verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 166 verifyColorIndexDoesNotExist(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 167 168 By("upgrading the chaincode to include an additional index") 169 newlifecycleChaincode.Sequence = "2" 170 newlifecycleChaincode.CodeFiles = filesWithIndices 171 newlifecycleChaincode.PackageFile = filepath.Join(testDir, "marbles-two-indexes.tar.gz") 172 newlifecycleChaincode.Label = "marbles-two-indexes" 173 174 nwo.PackageChaincodeBinary(newlifecycleChaincode) 175 nwo.InstallChaincode(network, newlifecycleChaincode, network.Peers...) 176 nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, newlifecycleChaincode, network.Peers...) 177 nwo.CheckCommitReadinessUntilReady(network, "testchannel", newlifecycleChaincode, network.PeerOrgs(), network.Peers...) 178 nwo.CommitChaincode(network, "testchannel", orderer, newlifecycleChaincode, network.Peers[0], network.Peers...) 179 180 verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 181 verifyColorIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 182 }) 183 }) 184 185 When("chaincode is installed and instantiated via legacy lifecycle and then defined and installed via new lifecycle", func() { 186 BeforeEach(func() { 187 legacyChaincode.Path = chaincodePathWithNoIndex 188 newlifecycleChaincode.CodeFiles = filesWithIndex 189 }) 190 191 It("creates indexes from the new lifecycle package", func() { 192 By("instantiating and installing legacy chaincode") 193 nwo.PackageChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0")) 194 nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0")) 195 nwo.InstantiateChaincodeLegacy(network, "testchannel", orderer, legacyChaincode, network.Peer("Org1", "peer0"), network.Peers...) 196 initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_not_indexed") 197 verifySizeIndexDoesNotExist(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 198 199 By("installing and defining chaincode using new lifecycle") 200 nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0")) 201 nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode) 202 initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_indexed") 203 verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 204 }) 205 }) 206 207 When("chaincode is installed and instantiated via legacy lifecycle with an external builder", func() { 208 BeforeEach(func() { 209 // This covers the legacy lifecycle + external builder scenario 210 legacyChaincode.Path = chaincodePathWithIndexes 211 legacyChaincode.Name = "marbles-external" 212 }) 213 214 It("creates indexes from the new lifecycle package", func() { 215 peer := network.Peer("Org1", "peer0") 216 217 By("installing with the external chaincode builder") 218 nwo.PackageChaincodeLegacy(network, legacyChaincode, peer) 219 nwo.InstallChaincodeLegacy(network, legacyChaincode, peer) 220 nwo.InstantiateChaincodeLegacy(network, "testchannel", orderer, legacyChaincode, peer, peer) 221 initMarble(network, "testchannel", orderer, peer, legacyChaincode.Name, "marble_indexed") 222 verifySizeIndexExists(network, "testchannel", orderer, peer, legacyChaincode.Name) 223 }) 224 }) 225 226 When("chaincode is instantiated via legacy lifecycle, then defined and installed via new lifecycle and, finally installed via legacy lifecycle", func() { 227 BeforeEach(func() { 228 legacyChaincode.Path = chaincodePathWithIndex 229 newlifecycleChaincode.CodeFiles = nil 230 }) 231 232 It("does not create indexes upon final installation of legacy chaincode", func() { 233 By("instantiating legacy chaincode") 234 // lscc requires the chaincode to be installed before a instantiate transaction can be simulated 235 // doing so in Org1.peer1 so that chaincode is not installed on "Org1.peer0" i.e., only instantiated 236 // via legacy lifecycle 237 nwo.PackageChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer1")) 238 nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer1")) 239 nwo.InstantiateChaincodeLegacy(network, "testchannel", orderer, legacyChaincode, network.Peer("Org1", "peer1"), network.Peers...) 240 241 By("installing and defining chaincode using new lifecycle") 242 nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0")) 243 nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode) 244 initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_not_indexed") 245 246 By("installing legacy chaincode on Org1.peer0") 247 nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0")) 248 249 By("verifying that the index should not have been created on (Org1, peer0) - though the legacy package contains indexes") 250 verifySizeIndexDoesNotExist(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 251 }) 252 }) 253 254 When("chaincode is installed using legacy lifecycle, then defined and installed using new lifecycle", func() { 255 BeforeEach(func() { 256 legacyChaincode.Path = chaincodePathWithIndex 257 newlifecycleChaincode.CodeFiles = nil 258 }) 259 260 It("does not use legacy package to create indexes", func() { 261 By("installing legacy chaincode (with an index included)") 262 nwo.PackageChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0")) 263 nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0")) 264 265 By("installing and defining chaincode (without an index included) using new lifecycle") 266 nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0")) 267 nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode) 268 initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_not_indexed") 269 270 By("verifying that the index should not have been created - though the legacy package contains indexes") 271 verifySizeIndexDoesNotExist(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles") 272 }) 273 }) 274 }) 275 276 func initMarble(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName, marbleName string) { 277 By("invoking initMarble function of the chaincode") 278 sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{ 279 ChannelID: channel, 280 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 281 Name: ccName, 282 Ctor: prepareChaincodeInvokeArgs("initMarble", marbleName, "blue", "35", "tom"), 283 PeerAddresses: []string{ 284 n.PeerAddress(peer, nwo.ListenPort), 285 }, 286 WaitForEvent: true, 287 }) 288 Expect(err).NotTo(HaveOccurred()) 289 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 290 Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful.")) 291 } 292 293 func prepareChaincodeInvokeArgs(args ...string) string { 294 m, err := json.Marshal(map[string][]string{ 295 "Args": args, 296 }) 297 Expect(err).NotTo(HaveOccurred()) 298 return string(m) 299 } 300 301 func verifySizeIndexExists(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string) { 302 verifySizeIndexPresence(n, channel, orderer, peer, ccName, true) 303 } 304 305 func verifySizeIndexDoesNotExist(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string) { 306 verifySizeIndexPresence(n, channel, orderer, peer, ccName, false) 307 } 308 309 func verifySizeIndexPresence(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string, expectIndexPresent bool) { 310 query := `{ 311 "selector":{ 312 "docType":{ 313 "$eq":"marble" 314 }, 315 "owner":{ 316 "$eq":"tom" 317 }, 318 "size":{ 319 "$gt":0 320 } 321 }, 322 "fields":["docType","owner","size"], 323 "sort":[{"size":"desc"}], 324 "use_index":"_design/indexSizeSortDoc" 325 }` 326 verifyIndexPresence(n, channel, orderer, peer, ccName, expectIndexPresent, query) 327 } 328 329 func verifyColorIndexExists(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string) { 330 verifyColorIndexPresence(n, channel, orderer, peer, ccName, true) 331 } 332 333 func verifyColorIndexDoesNotExist(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string) { 334 verifyColorIndexPresence(n, channel, orderer, peer, ccName, false) 335 } 336 337 func verifyColorIndexPresence(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string, expectIndexPresent bool) { 338 query := `{ 339 "selector":{ 340 "docType":{ 341 "$eq":"marble" 342 }, 343 "owner":{ 344 "$eq":"tom" 345 }, 346 "color":{ 347 "$eq":"blue" 348 } 349 }, 350 "fields":["docType","owner","size"], 351 "sort":[{"color":"desc"}], 352 "use_index":"_design/indexColorSortDoc" 353 }` 354 verifyIndexPresence(n, channel, orderer, peer, ccName, expectIndexPresent, query) 355 } 356 357 func verifyIndexPresence(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string, expectIndexPresent bool, indexQuery string) { 358 By("invoking queryMarbles function with a user constructed query that requires an index due to a sort") 359 sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{ 360 ChannelID: channel, 361 Name: ccName, 362 Ctor: prepareChaincodeInvokeArgs("queryMarbles", indexQuery), 363 }) 364 Expect(err).NotTo(HaveOccurred()) 365 if expectIndexPresent { 366 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 367 Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful.")) 368 } else { 369 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 370 Expect(sess.Err).To(gbytes.Say("Error:no_usable_index")) 371 } 372 }