From ae0339057e0eddf28a0282c5fac45ea5d3063bbd Mon Sep 17 00:00:00 2001 From: David Enyeart Date: Mon, 15 Jan 2018 05:45:21 -0500 Subject: [PATCH] [FAB-7134] Deploy couchdb indexes upon cc install This change set will automatically deploy any couchdb indexes that are defined in the chaincode package upon chaincode installation on the peer, to any joined channel's state database where the chaincode is already instantiated. If the chaincode gets instantiated after install, the indexes will automatically get deployed to couchdb at instantiation/upgrade time (see FAB-7581 for that logic). Change-Id: Idb6e374d231d7d8bc0a87597b8f40c4635fd96c8 Signed-off-by: David Enyeart --- .../cc_statedb_artifacts_provider.go | 23 +++++++++++++------ core/ledger/cceventmgmt/defs.go | 2 +- core/scc/lscc/lscc.go | 22 ++++++++++++++++++ core/scc/lscc/lscc_test.go | 6 +++++ 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/core/common/ccprovider/cc_statedb_artifacts_provider.go b/core/common/ccprovider/cc_statedb_artifacts_provider.go index 69d5acd8252..f19a13efa77 100644 --- a/core/common/ccprovider/cc_statedb_artifacts_provider.go +++ b/core/common/ccprovider/cc_statedb_artifacts_provider.go @@ -21,7 +21,7 @@ const ( // ExtractStatedbArtifactsAsTarbytes extracts the statedb artifacts from the code package tar and create a statedb artifact tar. // The state db artifacts are expected to contain state db specific artifacts such as index specification in the case of couchdb. // This function is intented to be used during chaincode instantiate/upgrade so that statedb artifacts can be created. -func ExtractStatedbArtifactsAsTarbytes(ccname, ccversion string) (installed bool, statedbArtifactsTar []byte, err error) { +func ExtractStatedbArtifactsForChaincode(ccname, ccversion string) (installed bool, statedbArtifactsTar []byte, err error) { ccpackage, err := GetChaincodeFromFS(ccname, ccversion) if err != nil { // TODO for now, we assume that an error indicates that the chaincode is not installed on the peer. @@ -31,12 +31,21 @@ func ExtractStatedbArtifactsAsTarbytes(ccname, ccversion string) (installed bool return false, nil, nil } + statedbArtifactsTar, err = ExtractStatedbArtifactsFromCCPackage(ccpackage) + return true, statedbArtifactsTar, err +} + +// ExtractStatedbArtifactsFromCCPackage extracts the statedb artifacts from the code package tar and create a statedb artifact tar. +// The state db artifacts are expected to contain state db specific artifacts such as index specification in the case of couchdb. +// This function is called during chaincode instantiate/upgrade (from above), and from install, so that statedb artifacts can be created. +func ExtractStatedbArtifactsFromCCPackage(ccpackage CCPackage) (statedbArtifactsTar []byte, err error) { + cds := ccpackage.GetDepSpec() is := bytes.NewReader(cds.CodePackage) gr, err := gzip.NewReader(is) if err != nil { ccproviderLogger.Errorf("Failure opening codepackage gzip stream: %s", err) - return true, nil, err + return nil, err } tr := tar.NewReader(gr) statedbTarBuffer := bytes.NewBuffer(nil) @@ -52,7 +61,7 @@ func ExtractStatedbArtifactsAsTarbytes(ccname, ccversion string) (installed bool } if err != nil { - return true, nil, err + return nil, err } ccproviderLogger.Debugf("header.Name = %s", header.Name) if !strings.HasPrefix(header.Name, ccPackageStatedbDir) { @@ -60,17 +69,17 @@ func ExtractStatedbArtifactsAsTarbytes(ccname, ccversion string) (installed bool } if err = tw.WriteHeader(header); err != nil { ccproviderLogger.Error("Error adding header to statedb tar:", err, header.Name) - return true, nil, err + return nil, err } if _, err := io.Copy(tw, tr); err != nil { ccproviderLogger.Error("Error copying file to statedb tar:", err, header.Name) - return true, nil, err + return nil, err } ccproviderLogger.Debug("Wrote file to statedb tar:", header.Name) } if err = tw.Close(); err != nil { - return true, nil, err + return nil, err } ccproviderLogger.Debug("Created statedb artifact tar") - return true, statedbTarBuffer.Bytes(), nil + return statedbTarBuffer.Bytes(), nil } diff --git a/core/ledger/cceventmgmt/defs.go b/core/ledger/cceventmgmt/defs.go index db36156b524..408fa552c6d 100644 --- a/core/ledger/cceventmgmt/defs.go +++ b/core/ledger/cceventmgmt/defs.go @@ -50,5 +50,5 @@ func (p *chaincodeInfoProviderImpl) IsChaincodeDeployed(chainid string, chaincod // RetrieveChaincodeArtifacts implements function in the interface ChaincodeInfoProvider func (p *chaincodeInfoProviderImpl) RetrieveChaincodeArtifacts(chaincodeDefinition *ChaincodeDefinition) (installed bool, dbArtifactsTar []byte, err error) { - return ccprovider.ExtractStatedbArtifactsAsTarbytes(chaincodeDefinition.Name, chaincodeDefinition.Version) + return ccprovider.ExtractStatedbArtifactsForChaincode(chaincodeDefinition.Name, chaincodeDefinition.Version) } diff --git a/core/scc/lscc/lscc.go b/core/scc/lscc/lscc.go index 83aa78e466a..72866eb9c62 100644 --- a/core/scc/lscc/lscc.go +++ b/core/scc/lscc/lscc.go @@ -29,6 +29,7 @@ import ( "github.com/hyperledger/fabric/core/common/ccprovider" "github.com/hyperledger/fabric/core/common/privdata" "github.com/hyperledger/fabric/core/common/sysccprovider" + "github.com/hyperledger/fabric/core/ledger/cceventmgmt" "github.com/hyperledger/fabric/core/peer" "github.com/hyperledger/fabric/core/policy" "github.com/hyperledger/fabric/core/policyprovider" @@ -395,6 +396,27 @@ func (lscc *lifeCycleSysCC) executeInstall(stub shim.ChaincodeStubInterface, ccb return err } + // Get any statedb artifacts from the chaincode package, e.g. couchdb index definitions + statedbArtifactsTar, err := ccprovider.ExtractStatedbArtifactsFromCCPackage(ccpack) + if err != nil { + return err + } + + chaincodeDefinition := &cceventmgmt.ChaincodeDefinition{ + Name: ccpack.GetChaincodeData().Name, + Version: ccpack.GetChaincodeData().Version, + Hash: ccpack.GetId()} // Note - The chaincode 'id' is the hash of chaincode's (CodeHash || MetaDataHash), aka fingerprint + + // HandleChaincodeInstall will apply any statedb artifacts (e.g. couchdb indexes) to + // any channel's statedb where the chaincode is already instantiated + // Note - this step is done prior to PutChaincodeToLocalStorage() since this step is idempotent and harmless until endorsements start, + // that is, if there are errors deploying the indexes the chaincode install can safely be re-attempted later. + err = cceventmgmt.GetMgr().HandleChaincodeInstall(chaincodeDefinition, statedbArtifactsTar) + if err != nil { + return err + } + + // Finally, if everything is good above, install the chaincode to local peer file system so that endorsements can start if err = lscc.support.PutChaincodeToLocalStorage(ccpack); err != nil { return err } diff --git a/core/scc/lscc/lscc_test.go b/core/scc/lscc/lscc_test.go index b70a8534a75..97bdd2dd720 100644 --- a/core/scc/lscc/lscc_test.go +++ b/core/scc/lscc/lscc_test.go @@ -27,6 +27,7 @@ import ( "github.com/hyperledger/fabric/core/common/ccprovider" "github.com/hyperledger/fabric/core/common/sysccprovider" cutil "github.com/hyperledger/fabric/core/container/util" + "github.com/hyperledger/fabric/core/ledger/cceventmgmt" "github.com/hyperledger/fabric/core/mocks/scc/lscc" "github.com/hyperledger/fabric/core/policy" policymocks "github.com/hyperledger/fabric/core/policy/mocks" @@ -84,6 +85,11 @@ func constructDeploymentSpec(name string, path string, version string, initArgs //TestInstall tests the install function with various inputs func TestInstall(t *testing.T) { + + // Initialize cceventmgmt Mgr + // TODO cceventmgmt singleton should be refactored out of peer in the future. See CR 16549 for details. + cceventmgmt.Initialize() + scc := &lifeCycleSysCC{support: &lscc.MockSupport{}} stub := shim.NewMockStub("lscc", scc) res := stub.MockInit("1", nil)