Skip to content

Commit

Permalink
[FAB-7570] chaincode metadata retrieval for SD
Browse files Browse the repository at this point in the history
This change set adds a method:
Metadata(channel string, cc string) *chaincode.Metadata
To core/cclifecycle, to be used by service discovery
when it needs to fetch the endorsement policy for chaincodes
that are deployed but not installed on the peer.

Change-Id: Id55ae085de9500dc8a2088f85cfa6062f436e354
Signed-off-by: yacovm <[email protected]>
  • Loading branch information
yacovm committed Apr 3, 2018
1 parent 3e6e421 commit 3c91127
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 7 deletions.
57 changes: 50 additions & 7 deletions core/cclifecycle/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ var (
// Lifecycle manages information regarding chaincode lifecycle
type Lifecycle struct {
sync.RWMutex
listeners []LifeCycleChangeListener
installedCCs []chaincode.InstalledChaincode
deployedCCsByChannel map[string]*chaincode.MetadataMapping
listeners []LifeCycleChangeListener
installedCCs []chaincode.InstalledChaincode
deployedCCsByChannel map[string]*chaincode.MetadataMapping
queryCreatorsByChannel map[string]QueryCreator
}

// LifeCycleChangeListener runs whenever there is a change to the metadata
Expand Down Expand Up @@ -75,14 +76,48 @@ func NewLifeCycle(installedChaincodes Enumerator) (*Lifecycle, error) {
}

lc := &Lifecycle{
installedCCs: installedCCs,
deployedCCsByChannel: make(map[string]*chaincode.MetadataMapping),
installedCCs: installedCCs,
deployedCCsByChannel: make(map[string]*chaincode.MetadataMapping),
queryCreatorsByChannel: make(map[string]QueryCreator),
}

return lc, nil
}

// Metadata returns the metadata of the chaincode on the given channel,
// or nil if not found or an error occurred at retrieving it
func (lc *Lifecycle) Metadata(channel string, cc string) *chaincode.Metadata {
newQuery := lc.queryCreatorsByChannel[channel]
if newQuery == nil {
logger.Warning("Requested Metadata for non-existent channel", channel)
return nil
}
if md, found := lc.deployedCCsByChannel[channel].Lookup(cc); found {
logger.Debug("Returning metadata for channel", channel, ", chaincode", cc, ":", md)
return &md
}
query, err := newQuery()
if err != nil {
logger.Error("Failed obtaining new query for channel", channel, ":", err)
return nil
}
md, err := DeployedChaincodes(query, AcceptAll, cc)
if err != nil {
logger.Error("Failed querying LSCC for channel", channel, ":", err)
return nil
}
if len(md) == 0 {
logger.Info("Chaincode", cc, "isn't defined in channel", channel)
return nil
}

return &md[0]
}

func (lc *Lifecycle) initMetadataForChannel(channel string, newQuery QueryCreator) error {
if lc.isChannelMetadataInitialized(channel) {
return nil
}
// Create a new metadata mapping for the channel
query, err := newQuery()
if err != nil {
Expand All @@ -92,15 +127,23 @@ func (lc *Lifecycle) initMetadataForChannel(channel string, newQuery QueryCreato
if err != nil {
return errors.WithStack(err)
}
lc.createMetadataForChannel(channel)
lc.createMetadataForChannel(channel, newQuery)
lc.loadMetadataForChannel(channel, ccs)
return nil
}

func (lc *Lifecycle) createMetadataForChannel(channel string) {
func (lc *Lifecycle) createMetadataForChannel(channel string, newQuery QueryCreator) {
lc.Lock()
defer lc.Unlock()
lc.deployedCCsByChannel[channel] = chaincode.NewMetadataMapping()
lc.queryCreatorsByChannel[channel] = newQuery
}

func (lc *Lifecycle) isChannelMetadataInitialized(channel string) bool {
lc.RLock()
defer lc.RUnlock()
_, exists := lc.deployedCCsByChannel[channel]
return exists
}

func (lc *Lifecycle) loadMetadataForChannel(channel string, ccs chaincode.MetadataSet) {
Expand Down
77 changes: 77 additions & 0 deletions core/cclifecycle/lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,72 @@ var _ = Describe("LifeCycle", func() {
}, nil)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed accessing DB"))

// We also can't query the DB via Metadata()
md := lc.Metadata("mychannel", "cc5")
var nilMetadata *chaincode.Metadata
Expect(md).To(Equal(nilMetadata))
})
})

Describe("Channel metadata retrieval", func() {
var query *mocks.Query
var nilMetadata *chaincode.Metadata

BeforeEach(func() {
query = &mocks.Query{}
query.GetStateReturnsOnCall(0, cc1Bytes, nil)
query.GetStateReturnsOnCall(1, nil, nil)
_, err = lc.NewChannelSubscription("mychannel", func() (cc.Query, error) {
return query, nil
})
})

Context("When the chaincode is installed and deployed", func() {
It("does not attempt to query LSCC for chaincodes that are installed, instead - it fetches from memory", func() {
md := lc.Metadata("mychannel", "cc1")
Expect(*md).To(Equal(metadataAtStartup))
})
})

Context("When the query fails", func() {
JustBeforeEach(func() {
query.GetStateReturnsOnCall(2, nil, errors.New("failed querying lscc"))
})
It("does not return the metadata if the query fails", func() {
md := lc.Metadata("mychannel", "cc5")
Expect(md).To(Equal(nilMetadata))
})
})

Context("When a non existent channel is queried for", func() {
It("does not return the metadata", func() {
md := lc.Metadata("mychannel5", "cc1")
Expect(md).To(Equal(nilMetadata))
})
})

Context("When the chaincode isn't deployed or installed", func() {
JustBeforeEach(func() {
query.GetStateReturnsOnCall(2, nil, nil)
})
It("returns empty metadata for chaincodes that are not installed and not deployed", func() {
md := lc.Metadata("mychannel", "cc5")
Expect(md).To(Equal(nilMetadata))
})
})

Context("When the chaincode is deployed but not installed", func() {
JustBeforeEach(func() {
query.GetStateReturnsOnCall(2, cc2Bytes, nil)
})
It("returns empty metadata for chaincodes that are not installed and not deployed", func() {
md := lc.Metadata("mychannel", "cc2")
Expect(*md).To(Equal(chaincode.Metadata{
Name: "cc2",
Version: "1.0",
}))
})
})
})

Expand Down Expand Up @@ -280,4 +346,15 @@ var _ = Describe("LifeCycle", func() {
}))
})
})

Context("when a subscription to the same channel is made once again", func() {
It("doesn't load data from the state DB", func() {
_, err = lc.NewChannelSubscription("mychannel", newQuery)
Expect(err).NotTo(HaveOccurred())
currentInvocationCount := query.GetStateCallCount()
_, err = lc.NewChannelSubscription("mychannel", newQuery)
Expect(err).NotTo(HaveOccurred())
Expect(query.GetStateCallCount()).To(Equal(currentInvocationCount))
})
})
})
7 changes: 7 additions & 0 deletions core/cclifecycle/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ type DirEnumerator func(string) ([]os.FileInfo, error)
// ChaincodeExtractor extracts chaincode from a given path
type ChaincodeExtractor func(ccname string, ccversion string, path string) (ccprovider.CCPackage, error)

var (
// AcceptAll returns a predicate that accepts all Metadata
AcceptAll ChaincodePredicate = func(cc chaincode.Metadata) bool {
return true
}
)

// InstalledCCs retrieves the installed chaincodes
func InstalledCCs(dir string, ls DirEnumerator, ccFromPath ChaincodeExtractor) ([]chaincode.InstalledChaincode, error) {
var chaincodes []chaincode.InstalledChaincode
Expand Down

0 comments on commit 3c91127

Please sign in to comment.