github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/core/chaincode/runtime_launcher.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package chaincode
     8  
     9  import (
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/hyperledger/fabric/core/chaincode/accesscontrol"
    14  	"github.com/hyperledger/fabric/core/chaincode/extcc"
    15  	"github.com/hyperledger/fabric/core/container/ccintf"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // LaunchRegistry tracks launching chaincode instances.
    20  type LaunchRegistry interface {
    21  	Launching(ccid string) (launchState *LaunchState, started bool)
    22  	Deregister(ccid string) error
    23  }
    24  
    25  // ConnectionHandler handles the `Chaincode` client connection
    26  type ConnectionHandler interface {
    27  	Stream(ccid string, ccinfo *ccintf.ChaincodeServerInfo, sHandler extcc.StreamHandler) error
    28  }
    29  
    30  // RuntimeLauncher is responsible for launching chaincode runtimes.
    31  type RuntimeLauncher struct {
    32  	Runtime           Runtime
    33  	Registry          LaunchRegistry
    34  	StartupTimeout    time.Duration
    35  	Metrics           *LaunchMetrics
    36  	PeerAddress       string
    37  	CACert            []byte
    38  	CertGenerator     CertGenerator
    39  	ConnectionHandler ConnectionHandler
    40  }
    41  
    42  // CertGenerator generates client certificates for chaincode.
    43  type CertGenerator interface {
    44  	// Generate returns a certificate and private key and associates
    45  	// the hash of the certificates with the given chaincode name
    46  	Generate(ccName string) (*accesscontrol.CertAndPrivKeyPair, error)
    47  }
    48  
    49  func (r *RuntimeLauncher) ChaincodeClientInfo(ccid string) (*ccintf.PeerConnection, error) {
    50  	var tlsConfig *ccintf.TLSConfig
    51  	if r.CertGenerator != nil {
    52  		certKeyPair, err := r.CertGenerator.Generate(string(ccid))
    53  		if err != nil {
    54  			return nil, errors.WithMessagef(err, "failed to generate TLS certificates for %s", ccid)
    55  		}
    56  
    57  		tlsConfig = &ccintf.TLSConfig{
    58  			ClientCert: certKeyPair.Cert,
    59  			ClientKey:  certKeyPair.Key,
    60  			RootCert:   r.CACert,
    61  		}
    62  	}
    63  
    64  	return &ccintf.PeerConnection{
    65  		Address:   r.PeerAddress,
    66  		TLSConfig: tlsConfig,
    67  	}, nil
    68  }
    69  
    70  func (r *RuntimeLauncher) Launch(ccid string, streamHandler extcc.StreamHandler) error {
    71  	var startFailCh chan error
    72  	var timeoutCh <-chan time.Time
    73  
    74  	startTime := time.Now()
    75  	launchState, alreadyStarted := r.Registry.Launching(ccid)
    76  	if !alreadyStarted {
    77  		startFailCh = make(chan error, 1)
    78  		timeoutCh = time.NewTimer(r.StartupTimeout).C
    79  
    80  		go func() {
    81  			// go through the build process to obtain connecion information
    82  			ccservinfo, err := r.Runtime.Build(ccid)
    83  			if err != nil {
    84  				startFailCh <- errors.WithMessage(err, "error building chaincode")
    85  				return
    86  			}
    87  
    88  			// chaincode server model indicated... proceed to connect to CC
    89  			if ccservinfo != nil {
    90  				if err = r.ConnectionHandler.Stream(ccid, ccservinfo, streamHandler); err != nil {
    91  					startFailCh <- errors.WithMessagef(err, "connection to %s failed", ccid)
    92  					return
    93  				}
    94  
    95  				launchState.Notify(errors.Errorf("connection to %s terminated", ccid))
    96  				return
    97  			}
    98  
    99  			// default peer-as-server model... compute connection information for CC callback
   100  			// and proceed to launch chaincode
   101  			ccinfo, err := r.ChaincodeClientInfo(ccid)
   102  			if err != nil {
   103  				startFailCh <- errors.WithMessage(err, "could not get connection info")
   104  				return
   105  			}
   106  			if ccinfo == nil {
   107  				startFailCh <- errors.New("could not get connection info")
   108  				return
   109  			}
   110  			if err = r.Runtime.Start(ccid, ccinfo); err != nil {
   111  				startFailCh <- errors.WithMessage(err, "error starting container")
   112  				return
   113  			}
   114  			exitCode, err := r.Runtime.Wait(ccid)
   115  			if err != nil {
   116  				launchState.Notify(errors.Wrap(err, "failed to wait on container exit"))
   117  			}
   118  			launchState.Notify(errors.Errorf("container exited with %d", exitCode))
   119  		}()
   120  	}
   121  
   122  	var err error
   123  	select {
   124  	case <-launchState.Done():
   125  		err = errors.WithMessage(launchState.Err(), "chaincode registration failed")
   126  	case err = <-startFailCh:
   127  		launchState.Notify(err)
   128  		r.Metrics.LaunchFailures.With("chaincode", ccid).Add(1)
   129  	case <-timeoutCh:
   130  		err = errors.Errorf("timeout expired while starting chaincode %s for transaction", ccid)
   131  		launchState.Notify(err)
   132  		r.Metrics.LaunchTimeouts.With("chaincode", ccid).Add(1)
   133  	}
   134  
   135  	success := true
   136  	if err != nil && !alreadyStarted {
   137  		success = false
   138  		chaincodeLogger.Debugf("stopping due to error while launching: %+v", err)
   139  		defer r.Registry.Deregister(ccid)
   140  	}
   141  
   142  	r.Metrics.LaunchDuration.With(
   143  		"chaincode", ccid,
   144  		"success", strconv.FormatBool(success),
   145  	).Observe(time.Since(startTime).Seconds())
   146  
   147  	chaincodeLogger.Debug("launch complete")
   148  	return err
   149  }