github.com/ewagmig/fabric@v2.1.1+incompatible/core/chaincode/lifecycle/custodian.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lifecycle 8 9 import ( 10 "sync" 11 12 "github.com/hyperledger/fabric/core/container" 13 ) 14 15 //go:generate counterfeiter -o mock/chaincode_launcher.go --fake-name ChaincodeLauncher . ChaincodeLauncher 16 type ChaincodeLauncher interface { 17 Launch(ccid string) error 18 Stop(ccid string) error 19 } 20 21 // ChaincodeCustodian is responsible for enqueuing builds and launches 22 // of chaincodes as they become available and stops when chaincodes 23 // are no longer referenced by an active chaincode definition. 24 type ChaincodeCustodian struct { 25 cond *sync.Cond 26 mutex sync.Mutex 27 choreQueue []*chaincodeChore 28 halt bool 29 } 30 31 // chaincodeChore represents a unit of work to be performed by the worker 32 // routine. It identifies the chaincode the work is associated with. If 33 // the work is to launch, then runnable is true (and stoppable is false). 34 // If the work is to stop, then stoppable is true (and runnable is false). 35 // If the work is simply to build, then runnable and stoppable are false. 36 type chaincodeChore struct { 37 chaincodeID string 38 runnable bool 39 stoppable bool 40 } 41 42 // NewChaincodeCustodian creates an instance of a chaincode custodian. It is the 43 // instantiator's responsibility to spawn a go routine to service the Work routine 44 // along with the appropriate dependencies. 45 func NewChaincodeCustodian() *ChaincodeCustodian { 46 cc := &ChaincodeCustodian{} 47 cc.cond = sync.NewCond(&cc.mutex) 48 return cc 49 } 50 51 func (cc *ChaincodeCustodian) NotifyInstalled(chaincodeID string) { 52 cc.mutex.Lock() 53 defer cc.mutex.Unlock() 54 cc.choreQueue = append(cc.choreQueue, &chaincodeChore{ 55 chaincodeID: chaincodeID, 56 }) 57 cc.cond.Signal() 58 } 59 60 func (cc *ChaincodeCustodian) NotifyInstalledAndRunnable(chaincodeID string) { 61 cc.mutex.Lock() 62 defer cc.mutex.Unlock() 63 cc.choreQueue = append(cc.choreQueue, &chaincodeChore{ 64 chaincodeID: chaincodeID, 65 runnable: true, 66 }) 67 cc.cond.Signal() 68 } 69 70 func (cc *ChaincodeCustodian) NotifyStoppable(chaincodeID string) { 71 cc.mutex.Lock() 72 defer cc.mutex.Unlock() 73 cc.choreQueue = append(cc.choreQueue, &chaincodeChore{ 74 chaincodeID: chaincodeID, 75 stoppable: true, 76 }) 77 cc.cond.Signal() 78 } 79 80 func (cc *ChaincodeCustodian) Close() { 81 cc.mutex.Lock() 82 defer cc.mutex.Unlock() 83 cc.halt = true 84 cc.cond.Signal() 85 } 86 87 func (cc *ChaincodeCustodian) Work(buildRegistry *container.BuildRegistry, builder ChaincodeBuilder, launcher ChaincodeLauncher) { 88 for { 89 cc.mutex.Lock() 90 if len(cc.choreQueue) == 0 && !cc.halt { 91 cc.cond.Wait() 92 } 93 if cc.halt { 94 cc.mutex.Unlock() 95 return 96 } 97 chore := cc.choreQueue[0] 98 cc.choreQueue = cc.choreQueue[1:] 99 cc.mutex.Unlock() 100 101 if chore.runnable { 102 if err := launcher.Launch(chore.chaincodeID); err != nil { 103 logger.Warningf("could not launch chaincode '%s': %s", chore.chaincodeID, err) 104 } 105 continue 106 } 107 108 if chore.stoppable { 109 if err := launcher.Stop(chore.chaincodeID); err != nil { 110 logger.Warningf("could not stop chaincode '%s': %s", chore.chaincodeID, err) 111 } 112 continue 113 } 114 115 buildStatus, ok := buildRegistry.BuildStatus(chore.chaincodeID) 116 if ok { 117 logger.Debugf("skipping build of chaincode '%s' as it is already in progress", chore.chaincodeID) 118 continue 119 } 120 err := builder.Build(chore.chaincodeID) 121 if err != nil { 122 logger.Warningf("could not build chaincode '%s': %s", chore.chaincodeID, err) 123 } 124 buildStatus.Notify(err) 125 } 126 }