github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/container/container.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package container
     8  
     9  import (
    10  	"io"
    11  	"sync"
    12  
    13  	"github.com/hyperledger/fabric/common/flogging"
    14  	"github.com/hyperledger/fabric/core/chaincode/persistence"
    15  	"github.com/hyperledger/fabric/core/container/ccintf"
    16  
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  var vmLogger = flogging.MustGetLogger("container")
    21  
    22  //go:generate counterfeiter -o mock/vm.go --fake-name VM . VM
    23  
    24  //VM is an abstract virtual image for supporting arbitrary virual machines
    25  type VM interface {
    26  	Build(ccid string, metadata *persistence.ChaincodePackageMetadata, codePackageStream io.Reader) (Instance, error)
    27  }
    28  
    29  //go:generate counterfeiter -o mock/instance.go --fake-name Instance . Instance
    30  
    31  // Instance represents a built chaincode instance, because of the docker legacy, calling this a
    32  // built 'container' would be very misleading, and going forward with the external launcher
    33  // 'image' also seemed inappropriate.  So, the vague 'Instance' is used here.
    34  type Instance interface {
    35  	Start(peerConnection *ccintf.PeerConnection) error
    36  	ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error)
    37  	Stop() error
    38  	Wait() (int, error)
    39  }
    40  
    41  type UninitializedInstance struct{}
    42  
    43  func (UninitializedInstance) Start(peerConnection *ccintf.PeerConnection) error {
    44  	return errors.Errorf("instance has not yet been built, cannot be started")
    45  }
    46  
    47  func (UninitializedInstance) ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) {
    48  	return nil, errors.Errorf("instance has not yet been built, cannot get chaincode server info")
    49  }
    50  
    51  func (UninitializedInstance) Stop() error {
    52  	return errors.Errorf("instance has not yet been built, cannot be stopped")
    53  }
    54  
    55  func (UninitializedInstance) Wait() (int, error) {
    56  	return 0, errors.Errorf("instance has not yet been built, cannot wait")
    57  }
    58  
    59  //go:generate counterfeiter -o mock/package_provider.go --fake-name PackageProvider . PackageProvider
    60  
    61  // PackageProvider gets chaincode packages from the filesystem.
    62  type PackageProvider interface {
    63  	GetChaincodePackage(packageID string) (*persistence.ChaincodePackageMetadata, io.ReadCloser, error)
    64  }
    65  
    66  type Router struct {
    67  	ExternalVM      VM
    68  	DockerVM        VM
    69  	containers      map[string]Instance
    70  	PackageProvider PackageProvider
    71  	mutex           sync.Mutex
    72  }
    73  
    74  func (r *Router) getInstance(ccid string) Instance {
    75  	r.mutex.Lock()
    76  	defer r.mutex.Unlock()
    77  
    78  	// Note, to resolve the locking problem which existed in the previous code, we never delete
    79  	// references from the map.  In this way, it is safe to release the lock and operate
    80  	// on the returned reference
    81  	vm, ok := r.containers[ccid]
    82  	if !ok {
    83  		return UninitializedInstance{}
    84  	}
    85  
    86  	return vm
    87  }
    88  
    89  func (r *Router) Build(ccid string) error {
    90  	var instance Instance
    91  
    92  	if r.ExternalVM != nil {
    93  		// for now, the package ID we retrieve from the FS is always the ccid
    94  		// the chaincode uses for registration
    95  		metadata, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid)
    96  		if err != nil {
    97  			return errors.WithMessage(err, "failed to get chaincode package for external build")
    98  		}
    99  		defer codeStream.Close()
   100  
   101  		instance, err = r.ExternalVM.Build(ccid, metadata, codeStream)
   102  		if err != nil {
   103  			return errors.WithMessage(err, "external builder failed")
   104  		}
   105  	}
   106  
   107  	if instance == nil {
   108  		metadata, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid)
   109  		if err != nil {
   110  			return errors.WithMessage(err, "failed to get chaincode package for docker build")
   111  		}
   112  		defer codeStream.Close()
   113  
   114  		instance, err = r.DockerVM.Build(ccid, metadata, codeStream)
   115  		if err != nil {
   116  			return errors.WithMessage(err, "docker build failed")
   117  		}
   118  	}
   119  
   120  	r.mutex.Lock()
   121  	defer r.mutex.Unlock()
   122  	if r.containers == nil {
   123  		r.containers = map[string]Instance{}
   124  	}
   125  	r.containers[ccid] = instance
   126  
   127  	return nil
   128  }
   129  
   130  func (r *Router) ChaincodeServerInfo(ccid string) (*ccintf.ChaincodeServerInfo, error) {
   131  	return r.getInstance(ccid).ChaincodeServerInfo()
   132  }
   133  
   134  func (r *Router) Start(ccid string, peerConnection *ccintf.PeerConnection) error {
   135  	return r.getInstance(ccid).Start(peerConnection)
   136  }
   137  
   138  func (r *Router) Stop(ccid string) error {
   139  	return r.getInstance(ccid).Stop()
   140  }
   141  
   142  func (r *Router) Wait(ccid string) (int, error) {
   143  	return r.getInstance(ccid).Wait()
   144  }