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