Skip to content

Commit

Permalink
[FAB-7134] Deploy couchdb indexes upon cc install
Browse files Browse the repository at this point in the history
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 <enyeart@us.ibm.com>
  • Loading branch information
denyeart committed Jan 16, 2018
1 parent 66d785b commit ae03390
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 8 deletions.
23 changes: 16 additions & 7 deletions core/common/ccprovider/cc_statedb_artifacts_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand All @@ -52,25 +61,25 @@ 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) {
continue
}
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
}
2 changes: 1 addition & 1 deletion core/ledger/cceventmgmt/defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
22 changes: 22 additions & 0 deletions core/scc/lscc/lscc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down
6 changes: 6 additions & 0 deletions core/scc/lscc/lscc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit ae03390

Please sign in to comment.