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  }