github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/container/container.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package container
     8  
     9  import (
    10  	"io"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/hechain20/hechain/common/flogging"
    15  	"github.com/hechain20/hechain/core/chaincode/persistence"
    16  	"github.com/hechain20/hechain/core/container/ccintf"
    17  
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  var vmLogger = flogging.MustGetLogger("container")
    22  
    23  //go:generate counterfeiter -o mock/docker_builder.go --fake-name DockerBuilder . DockerBuilder
    24  
    25  // DockerBuilder is what is exposed by the dockercontroller
    26  type DockerBuilder interface {
    27  	Build(ccid string, metadata *persistence.ChaincodePackageMetadata, codePackageStream io.Reader) (Instance, error)
    28  }
    29  
    30  //go:generate counterfeiter -o mock/external_builder.go --fake-name ExternalBuilder . ExternalBuilder
    31  
    32  // ExternalBuilder is what is exposed by the dockercontroller
    33  type ExternalBuilder interface {
    34  	Build(ccid string, metadata []byte, codePackageStream io.Reader) (Instance, error)
    35  }
    36  
    37  //go:generate counterfeiter -o mock/instance.go --fake-name Instance . Instance
    38  
    39  // Instance represents a built chaincode instance, because of the docker legacy, calling this a
    40  // built 'container' would be very misleading, and going forward with the external launcher
    41  // 'image' also seemed inappropriate.  So, the vague 'Instance' is used here.
    42  type Instance interface {
    43  	Start(peerConnection *ccintf.PeerConnection) error
    44  	ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error)
    45  	Stop() error
    46  	Wait() (int, error)
    47  }
    48  
    49  type UninitializedInstance struct{}
    50  
    51  func (UninitializedInstance) Start(peerConnection *ccintf.PeerConnection) error {
    52  	return errors.Errorf("instance has not yet been built, cannot be started")
    53  }
    54  
    55  func (UninitializedInstance) ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) {
    56  	return nil, errors.Errorf("instance has not yet been built, cannot get chaincode server info")
    57  }
    58  
    59  func (UninitializedInstance) Stop() error {
    60  	return errors.Errorf("instance has not yet been built, cannot be stopped")
    61  }
    62  
    63  func (UninitializedInstance) Wait() (int, error) {
    64  	return 0, errors.Errorf("instance has not yet been built, cannot wait")
    65  }
    66  
    67  //go:generate counterfeiter -o mock/package_provider.go --fake-name PackageProvider . PackageProvider
    68  
    69  // PackageProvider gets chaincode packages from the filesystem.
    70  type PackageProvider interface {
    71  	GetChaincodePackage(packageID string) (md *persistence.ChaincodePackageMetadata, mdBytes []byte, codeStream io.ReadCloser, err error)
    72  }
    73  
    74  type Router struct {
    75  	ExternalBuilder ExternalBuilder
    76  	DockerBuilder   DockerBuilder
    77  	containers      map[string]Instance
    78  	PackageProvider PackageProvider
    79  	mutex           sync.Mutex
    80  }
    81  
    82  func (r *Router) getInstance(ccid string) Instance {
    83  	r.mutex.Lock()
    84  	defer r.mutex.Unlock()
    85  
    86  	// Note, to resolve the locking problem which existed in the previous code, we never delete
    87  	// references from the map.  In this way, it is safe to release the lock and operate
    88  	// on the returned reference
    89  	vm, ok := r.containers[ccid]
    90  	if !ok {
    91  		return UninitializedInstance{}
    92  	}
    93  
    94  	return vm
    95  }
    96  
    97  func (r *Router) Build(ccid string) error {
    98  	var instance Instance
    99  
   100  	if r.ExternalBuilder != nil {
   101  		// for now, the package ID we retrieve from the FS is always the ccid
   102  		// the chaincode uses for registration
   103  		_, mdBytes, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid)
   104  		if err != nil {
   105  			return errors.WithMessage(err, "failed to get chaincode package for external build")
   106  		}
   107  		defer codeStream.Close()
   108  
   109  		instance, err = r.ExternalBuilder.Build(ccid, mdBytes, codeStream)
   110  		if err != nil {
   111  			return errors.WithMessage(err, "external builder failed")
   112  		}
   113  	}
   114  
   115  	if instance == nil {
   116  		if r.DockerBuilder == nil {
   117  			return errors.New("no DockerBuilder, cannot build")
   118  		}
   119  		metadata, _, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid)
   120  		if err != nil {
   121  			return errors.WithMessage(err, "failed to get chaincode package for docker build")
   122  		}
   123  		defer codeStream.Close()
   124  
   125  		instance, err = r.DockerBuilder.Build(ccid, metadata, codeStream)
   126  		if err != nil {
   127  			return errors.WithMessage(err, "docker build failed")
   128  		}
   129  	}
   130  
   131  	r.mutex.Lock()
   132  	defer r.mutex.Unlock()
   133  	if r.containers == nil {
   134  		r.containers = map[string]Instance{}
   135  	}
   136  	r.containers[ccid] = instance
   137  
   138  	return nil
   139  }
   140  
   141  func (r *Router) ChaincodeServerInfo(ccid string) (*ccintf.ChaincodeServerInfo, error) {
   142  	return r.getInstance(ccid).ChaincodeServerInfo()
   143  }
   144  
   145  func (r *Router) Start(ccid string, peerConnection *ccintf.PeerConnection) error {
   146  	return r.getInstance(ccid).Start(peerConnection)
   147  }
   148  
   149  func (r *Router) Stop(ccid string) error {
   150  	return r.getInstance(ccid).Stop()
   151  }
   152  
   153  func (r *Router) Wait(ccid string) (int, error) {
   154  	return r.getInstance(ccid).Wait()
   155  }
   156  
   157  func (r *Router) Shutdown(timeout time.Duration) {
   158  	var wg sync.WaitGroup
   159  	for ccid := range r.containers {
   160  		wg.Add(1)
   161  		go func(ccid string) {
   162  			defer wg.Done()
   163  			if err := r.Stop(ccid); err != nil {
   164  				vmLogger.Warnw("failed to stop chaincode", "ccid", ccid, "error", err)
   165  			}
   166  		}(ccid)
   167  	}
   168  
   169  	done := make(chan struct{})
   170  	go func() {
   171  		wg.Wait()
   172  		close(done)
   173  	}()
   174  
   175  	select {
   176  	case <-time.After(timeout):
   177  		vmLogger.Warning("timeout while stopping external chaincodes")
   178  	case <-done:
   179  	}
   180  }