github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/container/externalbuilder/instance.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package externalbuilder 8 9 import ( 10 "encoding/json" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "syscall" 16 "time" 17 18 "github.com/hechain20/hechain/core/container/ccintf" 19 "github.com/hechain20/hechain/internal/pkg/comm" 20 "github.com/pkg/errors" 21 ) 22 23 const ( 24 DialTimeout = 3 * time.Second 25 CCServerReleaseDir = "chaincode/server" 26 ) 27 28 type Instance struct { 29 PackageID string 30 BldDir string 31 ReleaseDir string 32 Builder *Builder 33 Session *Session 34 TermTimeout time.Duration 35 } 36 37 // Duration used for the DialTimeout property 38 type Duration time.Duration 39 40 func (d Duration) MarshalJSON() ([]byte, error) { 41 return json.Marshal(time.Duration(d).String()) 42 } 43 44 func (d *Duration) UnmarshalJSON(b []byte) error { 45 var v interface{} 46 if err := json.Unmarshal(b, &v); err != nil { 47 return err 48 } 49 50 switch value := v.(type) { 51 case float64: 52 *d = Duration(time.Duration(value)) 53 case string: 54 dur, err := time.ParseDuration(value) 55 if err != nil { 56 return err 57 } 58 *d = Duration(dur) 59 default: 60 return errors.New("invalid duration") 61 } 62 63 return nil 64 } 65 66 // ChaincodeServerUserData holds "connection.json" information 67 type ChaincodeServerUserData struct { 68 Address string `json:"address"` 69 DialTimeout Duration `json:"dial_timeout"` 70 TLSRequired bool `json:"tls_required"` 71 ClientAuthRequired bool `json:"client_auth_required"` 72 ClientKey string `json:"client_key"` // PEM encoded client key 73 ClientCert string `json:"client_cert"` // PEM encoded client certificate 74 RootCert string `json:"root_cert"` // PEM encoded peer chaincode certificate 75 76 } 77 78 func (c *ChaincodeServerUserData) ChaincodeServerInfo(cryptoDir string) (*ccintf.ChaincodeServerInfo, error) { 79 if c.Address == "" { 80 return nil, errors.New("chaincode address not provided") 81 } 82 connInfo := &ccintf.ChaincodeServerInfo{Address: c.Address} 83 84 connInfo.ClientConfig.DialTimeout = time.Duration(c.DialTimeout) 85 if connInfo.ClientConfig.DialTimeout == 0 { 86 connInfo.ClientConfig.DialTimeout = DialTimeout 87 } 88 89 // we can expose this if necessary 90 connInfo.ClientConfig.KaOpts = comm.DefaultKeepaliveOptions 91 92 if !c.TLSRequired { 93 return connInfo, nil 94 } 95 if c.ClientAuthRequired && c.ClientKey == "" { 96 return nil, errors.New("chaincode tls key not provided") 97 } 98 if c.ClientAuthRequired && c.ClientCert == "" { 99 return nil, errors.New("chaincode tls cert not provided") 100 } 101 if c.RootCert == "" { 102 return nil, errors.New("chaincode tls root cert not provided") 103 } 104 105 connInfo.ClientConfig.SecOpts.UseTLS = true 106 107 if c.ClientAuthRequired { 108 connInfo.ClientConfig.SecOpts.RequireClientCert = true 109 connInfo.ClientConfig.SecOpts.Certificate = []byte(c.ClientCert) 110 connInfo.ClientConfig.SecOpts.Key = []byte(c.ClientKey) 111 } 112 113 connInfo.ClientConfig.SecOpts.ServerRootCAs = [][]byte{[]byte(c.RootCert)} 114 115 return connInfo, nil 116 } 117 118 func (i *Instance) ChaincodeServerReleaseDir() string { 119 return filepath.Join(i.ReleaseDir, CCServerReleaseDir) 120 } 121 122 func (i *Instance) ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) { 123 ccinfoPath := filepath.Join(i.ChaincodeServerReleaseDir(), "connection.json") 124 125 _, err := os.Stat(ccinfoPath) 126 127 if os.IsNotExist(err) { 128 return nil, nil 129 } 130 131 if err != nil { 132 return nil, errors.WithMessage(err, "connection information not provided") 133 } 134 b, err := ioutil.ReadFile(ccinfoPath) 135 if err != nil { 136 return nil, errors.WithMessagef(err, "could not read '%s' for chaincode info", ccinfoPath) 137 } 138 ccdata := &ChaincodeServerUserData{} 139 err = json.Unmarshal(b, &ccdata) 140 if err != nil { 141 return nil, errors.WithMessagef(err, "malformed chaincode info at '%s'", ccinfoPath) 142 } 143 144 return ccdata.ChaincodeServerInfo(i.ChaincodeServerReleaseDir()) 145 } 146 147 func (i *Instance) Start(peerConnection *ccintf.PeerConnection) error { 148 sess, err := i.Builder.Run(i.PackageID, i.BldDir, peerConnection) 149 if err != nil { 150 return errors.WithMessage(err, "could not execute run") 151 } 152 i.Session = sess 153 return nil 154 } 155 156 // Stop signals the process to terminate with SIGTERM. If the process doesn't 157 // terminate within TermTimeout, the process is killed with SIGKILL. 158 func (i *Instance) Stop() error { 159 if i.Session == nil { 160 return errors.Errorf("instance has not been started") 161 } 162 163 done := make(chan struct{}) 164 go func() { i.Wait(); close(done) }() 165 166 i.Session.Signal(syscall.SIGTERM) 167 select { 168 case <-time.After(i.TermTimeout): 169 i.Session.Signal(syscall.SIGKILL) 170 case <-done: 171 return nil 172 } 173 174 select { 175 case <-time.After(5 * time.Second): 176 return errors.Errorf("failed to stop instance '%s'", i.PackageID) 177 case <-done: 178 return nil 179 } 180 } 181 182 func (i *Instance) Wait() (int, error) { 183 if i.Session == nil { 184 return -1, errors.Errorf("instance was not successfully started") 185 } 186 187 err := i.Session.Wait() 188 err = errors.Wrapf(err, "builder '%s' run failed", i.Builder.Name) 189 if exitErr, ok := errors.Cause(err).(*exec.ExitError); ok { 190 return exitErr.ExitCode(), err 191 } 192 return 0, err 193 }