github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/runtime_launcher.go (about) 1 /* 2 Copyright hechain. 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/hechain20/hechain/core/chaincode/accesscontrol" 14 "github.com/hechain20/hechain/core/chaincode/extcc" 15 "github.com/hechain20/hechain/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 } 150 151 func (r *RuntimeLauncher) Stop(ccid string) error { 152 err := r.Runtime.Stop(ccid) 153 if err != nil { 154 return errors.WithMessagef(err, "failed to stop chaincode %s", ccid) 155 } 156 157 return nil 158 }