github.com/ewagmig/fabric@v2.1.1+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/docker_builder.go --fake-name DockerBuilder . DockerBuilder
    23  
    24  // DockerBuilder is what is exposed by the dockercontroller
    25  type DockerBuilder interface {
    26  	Build(ccid string, metadata *persistence.ChaincodePackageMetadata, codePackageStream io.Reader) (Instance, error)
    27  }
    28  
    29  //go:generate counterfeiter -o mock/external_builder.go --fake-name ExternalBuilder . ExternalBuilder
    30  
    31  // ExternalBuilder is what is exposed by the dockercontroller
    32  type ExternalBuilder interface {
    33  	Build(ccid string, metadata []byte, codePackageStream io.Reader) (Instance, error)
    34  }
    35  
    36  //go:generate counterfeiter -o mock/instance.go --fake-name Instance . Instance
    37  
    38  // Instance represents a built chaincode instance, because of the docker legacy, calling this a
    39  // built 'container' would be very misleading, and going forward with the external launcher
    40  // 'image' also seemed inappropriate.  So, the vague 'Instance' is used here.
    41  type Instance interface {
    42  	Start(peerConnection *ccintf.PeerConnection) error
    43  	ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error)
    44  	Stop() error
    45  	Wait() (int, error)
    46  }
    47  
    48  type UninitializedInstance struct{}
    49  
    50  func (UninitializedInstance) Start(peerConnection *ccintf.PeerConnection) error {
    51  	return errors.Errorf("instance has not yet been built, cannot be started")
    52  }
    53  
    54  func (UninitializedInstance) ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) {
    55  	return nil, errors.Errorf("instance has not yet been built, cannot get chaincode server info")
    56  }
    57  
    58  func (UninitializedInstance) Stop() error {
    59  	return errors.Errorf("instance has not yet been built, cannot be stopped")
    60  }
    61  
    62  func (UninitializedInstance) Wait() (int, error) {
    63  	return 0, errors.Errorf("instance has not yet been built, cannot wait")
    64  }
    65  
    66  //go:generate counterfeiter -o mock/package_provider.go --fake-name PackageProvider . PackageProvider
    67  
    68  // PackageProvider gets chaincode packages from the filesystem.
    69  type PackageProvider interface {
    70  	GetChaincodePackage(packageID string) (md *persistence.ChaincodePackageMetadata, mdBytes []byte, codeStream io.ReadCloser, err error)
    71  }
    72  
    73  type Router struct {
    74  	ExternalBuilder ExternalBuilder
    75  	DockerBuilder   DockerBuilder
    76  	containers      map[string]Instance
    77  	PackageProvider PackageProvider
    78  	mutex           sync.Mutex
    79  }
    80  
    81  func (r *Router) getInstance(ccid string) Instance {
    82  	r.mutex.Lock()
    83  	defer r.mutex.Unlock()
    84  
    85  	// Note, to resolve the locking problem which existed in the previous code, we never delete
    86  	// references from the map.  In this way, it is safe to release the lock and operate
    87  	// on the returned reference
    88  	vm, ok := r.containers[ccid]
    89  	if !ok {
    90  		return UninitializedInstance{}
    91  	}
    92  
    93  	return vm
    94  }
    95  
    96  func (r *Router) Build(ccid string) error {
    97  	var instance Instance
    98  
    99  	if r.ExternalBuilder != nil {
   100  		// for now, the package ID we retrieve from the FS is always the ccid
   101  		// the chaincode uses for registration
   102  		_, mdBytes, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid)
   103  		if err != nil {
   104  			return errors.WithMessage(err, "failed to get chaincode package for external build")
   105  		}
   106  		defer codeStream.Close()
   107  
   108  		instance, err = r.ExternalBuilder.Build(ccid, mdBytes, codeStream)
   109  		if err != nil {
   110  			return errors.WithMessage(err, "external builder failed")
   111  		}
   112  	}
   113  
   114  	if instance == nil {
   115  		if r.DockerBuilder == nil {
   116  			return errors.New("no DockerBuilder, cannot build")
   117  		}
   118  		metadata, _, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid)
   119  		if err != nil {
   120  			return errors.WithMessage(err, "failed to get chaincode package for docker build")
   121  		}
   122  		defer codeStream.Close()
   123  
   124  		instance, err = r.DockerBuilder.Build(ccid, metadata, codeStream)
   125  		if err != nil {
   126  			return errors.WithMessage(err, "docker build failed")
   127  		}
   128  	}
   129  
   130  	r.mutex.Lock()
   131  	defer r.mutex.Unlock()
   132  	if r.containers == nil {
   133  		r.containers = map[string]Instance{}
   134  	}
   135  	r.containers[ccid] = instance
   136  
   137  	return nil
   138  }
   139  
   140  func (r *Router) ChaincodeServerInfo(ccid string) (*ccintf.ChaincodeServerInfo, error) {
   141  	return r.getInstance(ccid).ChaincodeServerInfo()
   142  }
   143  
   144  func (r *Router) Start(ccid string, peerConnection *ccintf.PeerConnection) error {
   145  	return r.getInstance(ccid).Start(peerConnection)
   146  }
   147  
   148  func (r *Router) Stop(ccid string) error {
   149  	return r.getInstance(ccid).Stop()
   150  }
   151  
   152  func (r *Router) Wait(ccid string) (int, error) {
   153  	return r.getInstance(ccid).Wait()
   154  }