Commit 35fc4132 authored by Jonathan Levi (HACERA)'s avatar Jonathan Levi (HACERA) Committed by Gerrit Code Review
Browse files

Merge changes Ie8efa507,I18226576

* changes:
  FAB-11520 Add implementation for ChaincodeInstall
  FAB-11588 Have package-provider parse fs bytes
parents f4996fcf d360f225
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package lifecycle
import (
"github.com/hyperledger/fabric/core/chaincode/persistence"
"github.com/pkg/errors"
)
// ChaincodeStore provides a way to persist chaincodes
type ChaincodeStore interface {
Save(name, version string, ccInstallPkg []byte) (hash []byte, err error)
}
type PackageParser interface {
Parse(data []byte) (*persistence.ChaincodePackage, error)
}
// Lifecycle implements the lifecycle operations which are invoked
// by the SCC as well as internally
type Lifecycle struct {
ChaincodeStore ChaincodeStore
PackageParser PackageParser
}
// InstallChaincode installs a given chaincode to the peer's chaincode store.
// It returns the hash to reference the chaincode by or an error on failure.
func (l *Lifecycle) InstallChaincode(name, version string, chaincodeInstallPackage []byte) ([]byte, error) {
// Let's validate that the chaincodeInstallPackage is at least well formed before writing it
_, err := l.PackageParser.Parse(chaincodeInstallPackage)
if err != nil {
return nil, errors.WithMessage(err, "could not parse as a chaincode install package")
}
hash, err := l.ChaincodeStore.Save(name, version, chaincodeInstallPackage)
if err != nil {
return nil, errors.WithMessage(err, "could not save cc install package")
}
return hash, nil
}
......@@ -12,6 +12,7 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/hyperledger/fabric/core/chaincode/lifecycle"
"github.com/hyperledger/fabric/core/chaincode/shim"
)
......@@ -20,6 +21,16 @@ type chaincodeStub interface {
shim.ChaincodeStubInterface
}
//go:generate counterfeiter -o mock/chaincode_store.go --fake-name ChaincodeStore . chaincodeStore
type chaincodeStore interface {
lifecycle.ChaincodeStore
}
//go:generate counterfeiter -o mock/package_parser.go --fake-name PackageParser . packageParser
type packageParser interface {
lifecycle.PackageParser
}
func TestLifecycle(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Lifecycle Suite")
......
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package lifecycle_test
import (
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/hyperledger/fabric/core/chaincode/lifecycle"
"github.com/hyperledger/fabric/core/chaincode/lifecycle/mock"
)
var _ = Describe("Lifecycle", func() {
var (
l *lifecycle.Lifecycle
fakeCCStore *mock.ChaincodeStore
fakeParser *mock.PackageParser
)
BeforeEach(func() {
fakeCCStore = &mock.ChaincodeStore{}
fakeParser = &mock.PackageParser{}
l = &lifecycle.Lifecycle{
PackageParser: fakeParser,
ChaincodeStore: fakeCCStore,
}
})
Describe("InstallChaincode", func() {
BeforeEach(func() {
fakeCCStore.SaveReturns([]byte("fake-hash"), nil)
})
It("saves the chaincode", func() {
hash, err := l.InstallChaincode("name", "version", []byte("cc-package"))
Expect(err).NotTo(HaveOccurred())
Expect(hash).To(Equal([]byte("fake-hash")))
Expect(fakeParser.ParseCallCount()).To(Equal(1))
Expect(fakeParser.ParseArgsForCall(0)).To(Equal([]byte("cc-package")))
Expect(fakeCCStore.SaveCallCount()).To(Equal(1))
name, version, msg := fakeCCStore.SaveArgsForCall(0)
Expect(name).To(Equal("name"))
Expect(version).To(Equal("version"))
Expect(msg).To(Equal([]byte("cc-package")))
})
Context("when saving the chaincode fails", func() {
BeforeEach(func() {
fakeCCStore.SaveReturns(nil, fmt.Errorf("fake-error"))
})
It("wraps and returns the error", func() {
hash, err := l.InstallChaincode("name", "version", []byte("cc-package"))
Expect(hash).To(BeNil())
Expect(err).To(MatchError("could not save cc install package: fake-error"))
})
})
Context("when parsing the chaincode package fails", func() {
BeforeEach(func() {
fakeParser.ParseReturns(nil, fmt.Errorf("parse-error"))
})
It("wraps and returns the error", func() {
hash, err := l.InstallChaincode("name", "version", []byte("fake-package"))
Expect(hash).To(BeNil())
Expect(err).To(MatchError("could not parse as a chaincode install package: parse-error"))
})
})
})
})
// Code generated by counterfeiter. DO NOT EDIT.
package mock
import (
"sync"
)
type ChaincodeStore struct {
SaveStub func(name, version string, ccInstallPkg []byte) (hash []byte, err error)
saveMutex sync.RWMutex
saveArgsForCall []struct {
name string
version string
ccInstallPkg []byte
}
saveReturns struct {
result1 []byte
result2 error
}
saveReturnsOnCall map[int]struct {
result1 []byte
result2 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *ChaincodeStore) Save(name string, version string, ccInstallPkg []byte) (hash []byte, err error) {
var ccInstallPkgCopy []byte
if ccInstallPkg != nil {
ccInstallPkgCopy = make([]byte, len(ccInstallPkg))
copy(ccInstallPkgCopy, ccInstallPkg)
}
fake.saveMutex.Lock()
ret, specificReturn := fake.saveReturnsOnCall[len(fake.saveArgsForCall)]
fake.saveArgsForCall = append(fake.saveArgsForCall, struct {
name string
version string
ccInstallPkg []byte
}{name, version, ccInstallPkgCopy})
fake.recordInvocation("Save", []interface{}{name, version, ccInstallPkgCopy})
fake.saveMutex.Unlock()
if fake.SaveStub != nil {
return fake.SaveStub(name, version, ccInstallPkg)
}
if specificReturn {
return ret.result1, ret.result2
}
return fake.saveReturns.result1, fake.saveReturns.result2
}
func (fake *ChaincodeStore) SaveCallCount() int {
fake.saveMutex.RLock()
defer fake.saveMutex.RUnlock()
return len(fake.saveArgsForCall)
}
func (fake *ChaincodeStore) SaveArgsForCall(i int) (string, string, []byte) {
fake.saveMutex.RLock()
defer fake.saveMutex.RUnlock()
return fake.saveArgsForCall[i].name, fake.saveArgsForCall[i].version, fake.saveArgsForCall[i].ccInstallPkg
}
func (fake *ChaincodeStore) SaveReturns(result1 []byte, result2 error) {
fake.SaveStub = nil
fake.saveReturns = struct {
result1 []byte
result2 error
}{result1, result2}
}
func (fake *ChaincodeStore) SaveReturnsOnCall(i int, result1 []byte, result2 error) {
fake.SaveStub = nil
if fake.saveReturnsOnCall == nil {
fake.saveReturnsOnCall = make(map[int]struct {
result1 []byte
result2 error
})
}
fake.saveReturnsOnCall[i] = struct {
result1 []byte
result2 error
}{result1, result2}
}
func (fake *ChaincodeStore) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.saveMutex.RLock()
defer fake.saveMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *ChaincodeStore) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
// Code generated by counterfeiter. DO NOT EDIT.
package mock
import (
"sync"
"github.com/hyperledger/fabric/core/chaincode/persistence"
)
type PackageParser struct {
ParseStub func(data []byte) (*persistence.ChaincodePackage, error)
parseMutex sync.RWMutex
parseArgsForCall []struct {
data []byte
}
parseReturns struct {
result1 *persistence.ChaincodePackage
result2 error
}
parseReturnsOnCall map[int]struct {
result1 *persistence.ChaincodePackage
result2 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *PackageParser) Parse(data []byte) (*persistence.ChaincodePackage, error) {
var dataCopy []byte
if data != nil {
dataCopy = make([]byte, len(data))
copy(dataCopy, data)
}
fake.parseMutex.Lock()
ret, specificReturn := fake.parseReturnsOnCall[len(fake.parseArgsForCall)]
fake.parseArgsForCall = append(fake.parseArgsForCall, struct {
data []byte
}{dataCopy})
fake.recordInvocation("Parse", []interface{}{dataCopy})
fake.parseMutex.Unlock()
if fake.ParseStub != nil {
return fake.ParseStub(data)
}
if specificReturn {
return ret.result1, ret.result2
}
return fake.parseReturns.result1, fake.parseReturns.result2
}
func (fake *PackageParser) ParseCallCount() int {
fake.parseMutex.RLock()
defer fake.parseMutex.RUnlock()
return len(fake.parseArgsForCall)
}
func (fake *PackageParser) ParseArgsForCall(i int) []byte {
fake.parseMutex.RLock()
defer fake.parseMutex.RUnlock()
return fake.parseArgsForCall[i].data
}
func (fake *PackageParser) ParseReturns(result1 *persistence.ChaincodePackage, result2 error) {
fake.ParseStub = nil
fake.parseReturns = struct {
result1 *persistence.ChaincodePackage
result2 error
}{result1, result2}
}
func (fake *PackageParser) ParseReturnsOnCall(i int, result1 *persistence.ChaincodePackage, result2 error) {
fake.ParseStub = nil
if fake.parseReturnsOnCall == nil {
fake.parseReturnsOnCall = make(map[int]struct {
result1 *persistence.ChaincodePackage
result2 error
})
}
fake.parseReturnsOnCall[i] = struct {
result1 *persistence.ChaincodePackage
result2 error
}{result1, result2}
}
func (fake *PackageParser) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.parseMutex.RLock()
defer fake.parseMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *PackageParser) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
// Code generated by counterfeiter. DO NOT EDIT.
package mock
import (
"sync"
persistence_test "github.com/hyperledger/fabric/core/chaincode/persistence"
)
type PackageParser struct {
ParseStub func(data []byte) (*persistence_test.ChaincodePackage, error)
parseMutex sync.RWMutex
parseArgsForCall []struct {
data []byte
}
parseReturns struct {
result1 *persistence_test.ChaincodePackage
result2 error
}
parseReturnsOnCall map[int]struct {
result1 *persistence_test.ChaincodePackage
result2 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *PackageParser) Parse(data []byte) (*persistence_test.ChaincodePackage, error) {
var dataCopy []byte
if data != nil {
dataCopy = make([]byte, len(data))
copy(dataCopy, data)
}
fake.parseMutex.Lock()
ret, specificReturn := fake.parseReturnsOnCall[len(fake.parseArgsForCall)]
fake.parseArgsForCall = append(fake.parseArgsForCall, struct {
data []byte
}{dataCopy})
fake.recordInvocation("Parse", []interface{}{dataCopy})
fake.parseMutex.Unlock()
if fake.ParseStub != nil {
return fake.ParseStub(data)
}
if specificReturn {
return ret.result1, ret.result2
}
return fake.parseReturns.result1, fake.parseReturns.result2
}
func (fake *PackageParser) ParseCallCount() int {
fake.parseMutex.RLock()
defer fake.parseMutex.RUnlock()
return len(fake.parseArgsForCall)
}
func (fake *PackageParser) ParseArgsForCall(i int) []byte {
fake.parseMutex.RLock()
defer fake.parseMutex.RUnlock()
return fake.parseArgsForCall[i].data
}
func (fake *PackageParser) ParseReturns(result1 *persistence_test.ChaincodePackage, result2 error) {
fake.ParseStub = nil
fake.parseReturns = struct {
result1 *persistence_test.ChaincodePackage
result2 error
}{result1, result2}
}
func (fake *PackageParser) ParseReturnsOnCall(i int, result1 *persistence_test.ChaincodePackage, result2 error) {
fake.ParseStub = nil
if fake.parseReturnsOnCall == nil {
fake.parseReturnsOnCall = make(map[int]struct {
result1 *persistence_test.ChaincodePackage
result2 error
})
}
fake.parseReturnsOnCall[i] = struct {
result1 *persistence_test.ChaincodePackage
result2 error
}{result1, result2}
}
func (fake *PackageParser) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.parseMutex.RLock()
defer fake.parseMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *PackageParser) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
......@@ -30,10 +30,16 @@ type LegacyPackageProvider interface {
ListInstalledChaincodes(dir string, de ccprovider.DirEnumerator, ce ccprovider.ChaincodeExtractor) ([]chaincode.InstalledChaincode, error)
}
// PackageParser provides an implementation of chaincode package parsing
type PackageParser interface {
Parse(data []byte) (*ChaincodePackage, error)
}
// PackageProvider holds the necessary dependencies to obtain the code
// package bytes for a chaincode
type PackageProvider struct {
Store StorePackageProvider
Parser PackageParser
LegacyPP LegacyPackageProvider
}
......@@ -72,11 +78,17 @@ func (p *PackageProvider) getCodePackageFromStore(name, version string) ([]byte,
return nil, errors.WithMessage(err, "error retrieving hash")
}
codePackage, _, _, err := p.Store.Load(hash)
fsBytes, _, _, err := p.Store.Load(hash)
if err != nil {
return nil, errors.WithMessage(err, "error loading code package from ChaincodeInstallPackage")
}
return codePackage, nil
ccPackage, err := p.Parser.Parse(fsBytes)
if err != nil {
return nil, errors.WithMessage(err, "error parsing chaincode package")
}
return ccPackage.CodePackage, nil
}
// GetCodePackageFromLegacyPP gets the code packages bytes from the
......
......@@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0
package persistence_test
import (
"fmt"
"github.com/hyperledger/fabric/common/chaincode"
"github.com/hyperledger/fabric/core/chaincode/persistence"
"github.com/hyperledger/fabric/core/chaincode/persistence/mock"
......@@ -20,6 +22,7 @@ var _ = Describe("PackageProvider", func() {
var (
mockSPP *mock.StorePackageProvider
mockLPP *mock.LegacyPackageProvider
mockParser *mock.PackageParser
packageProvider *persistence.PackageProvider
)
......@@ -28,11 +31,17 @@ var _ = Describe("PackageProvider", func() {
mockSPP.RetrieveHashReturns([]byte("testcchash"), nil)
mockSPP.LoadReturns([]byte("storeCode"), "testcc", "1.0", nil)
mockParser = &mock.PackageParser{}
mockParser.ParseReturns(&persistence.ChaincodePackage{
CodePackage: []byte("parsedCode"),
}, nil)
mockLPP = &mock.LegacyPackageProvider{}
mockLPP.GetChaincodeCodePackageReturns([]byte("legacyCode"), nil)
packageProvider = &persistence.PackageProvider{
Store: mockSPP,
Parser: mockParser,
LegacyPP: mockLPP,
}
})
......@@ -40,7 +49,27 @@ var _ = Describe("PackageProvider", func() {
It("gets the code package successfully", func() {
pkgBytes, err := packageProvider.GetChaincodeCodePackage("testcc", "1.0")
Expect(err).NotTo(HaveOccurred())
Expect(pkgBytes).To(Equal([]byte("storeCode")))
Expect(mockSPP.RetrieveHashCallCount()).To(Equal(1))
ccName, ccVersion := mockSPP.RetrieveHashArgsForCall(0)
Expect(ccName).To(Equal("testcc"))
Expect(ccVersion).To(Equal("1.0"))
Expect(mockParser.ParseCallCount()).To(Equal(1))
Expect(mockParser.ParseArgsForCall(0)).To(Equal([]byte("storeCode")))
Expect(pkgBytes).To(Equal([]byte("parsedCode")))
})
Context("when parsing the code package fails", func() {
BeforeEach(func() {
mockParser.ParseReturns(nil, fmt.Errorf("fake-error"))
})
It("wraps and returns the error", func() {
_, err := packageProvider.GetChaincodeCodePackage("testcc", "1.0")
Expect(err).To(MatchError("error parsing chaincode package: fake-error"))
})
})
Context("when the code package is not available in the store package provider", func() {
......
......@@ -35,6 +35,11 @@ type legacyPackageProvider interface {
persistence.LegacyPackageProvider
}
//go:generate counterfeiter -o mock/package_parser.go -fake-name PackageParser . packageParser
type packageParser interface {
persistence.PackageParser
}
func TestPersistence(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Persistence Suite")
......
......@@ -39,7 +39,7 @@ func (x ConfidentialityLevel) String() string {
return proto.EnumName(ConfidentialityLevel_name, int32(x))
}
func (ConfidentialityLevel) EnumDescriptor() ([]byte, []int) {