github.com/ahlemtn/fabric@v2.1.1+incompatible/core/container/externalbuilder/instance_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package externalbuilder_test 8 9 import ( 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "time" 15 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 "go.uber.org/zap" 19 "go.uber.org/zap/zapcore" 20 21 "github.com/hyperledger/fabric/common/flogging" 22 "github.com/hyperledger/fabric/core/container/ccintf" 23 "github.com/hyperledger/fabric/core/container/externalbuilder" 24 "github.com/hyperledger/fabric/internal/pkg/comm" 25 ) 26 27 var _ = Describe("Instance", func() { 28 var ( 29 logger *flogging.FabricLogger 30 instance *externalbuilder.Instance 31 ) 32 33 BeforeEach(func() { 34 enc := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{MessageKey: "msg"}) 35 core := zapcore.NewCore(enc, zapcore.AddSync(GinkgoWriter), zap.NewAtomicLevel()) 36 logger = flogging.NewFabricLogger(zap.New(core).Named("logger")) 37 38 instance = &externalbuilder.Instance{ 39 PackageID: "test-ccid", 40 Builder: &externalbuilder.Builder{ 41 Location: "testdata/goodbuilder", 42 Logger: logger, 43 MSPID: "mspid", 44 }, 45 } 46 }) 47 48 Describe("ChaincodeServerInfo", func() { 49 BeforeEach(func() { 50 var err error 51 instance.ReleaseDir, err = ioutil.TempDir("", "cc-conn-test") 52 Expect(err).NotTo(HaveOccurred()) 53 54 err = os.MkdirAll(filepath.Join(instance.ReleaseDir, "chaincode", "server"), 0755) 55 Expect(err).NotTo(HaveOccurred()) 56 // initialize with a well-formed, all fields set, connection.json file 57 ccdata := `{"address": "ccaddress:12345", "tls_required": true, "dial_timeout": "10s", "client_auth_required": true, "client_key": "fake-key", "client_cert": "fake-cert", "root_cert": "fake-root-cert"}` 58 err = ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json"), []byte(ccdata), 0600) 59 Expect(err).NotTo(HaveOccurred()) 60 }) 61 62 AfterEach(func() { 63 os.RemoveAll(instance.ReleaseDir) 64 }) 65 66 It("returns chaincode connection", func() { 67 ccinfo, err := instance.ChaincodeServerInfo() 68 Expect(err).NotTo(HaveOccurred()) 69 Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{ 70 Address: "ccaddress:12345", 71 ClientConfig: comm.ClientConfig{ 72 SecOpts: comm.SecureOptions{ 73 UseTLS: true, 74 RequireClientCert: true, 75 Certificate: []byte("fake-cert"), 76 Key: []byte("fake-key"), 77 ServerRootCAs: [][]byte{[]byte("fake-root-cert")}, 78 }, 79 KaOpts: comm.DefaultKeepaliveOptions, 80 Timeout: 10 * time.Second, 81 }, 82 })) 83 }) 84 85 When("connection.json is not provided", func() { 86 BeforeEach(func() { 87 err := os.Remove(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json")) 88 Expect(err).NotTo(HaveOccurred()) 89 }) 90 91 It("returns nil server info", func() { 92 ccinfo, err := instance.ChaincodeServerInfo() 93 Expect(err).NotTo(HaveOccurred()) 94 Expect(ccinfo).To(BeNil()) 95 }) 96 }) 97 98 When("chaincode info is badly formed", func() { 99 BeforeEach(func() { 100 ccdata := `{"badly formed chaincode"}` 101 err := ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json"), []byte(ccdata), 0600) 102 Expect(err).NotTo(HaveOccurred()) 103 }) 104 105 It("returns a malformed chaincode error", func() { 106 _, err := instance.ChaincodeServerInfo() 107 Expect(err).To(MatchError(ContainSubstring("malformed chaincode info"))) 108 }) 109 }) 110 }) 111 112 Describe("ChaincodeServerUserData", func() { 113 var ( 114 ccuserdata *externalbuilder.ChaincodeServerUserData 115 releaseDir string 116 ) 117 118 BeforeEach(func() { 119 var err error 120 releaseDir, err = ioutil.TempDir("", "cc-conn-test") 121 Expect(err).NotTo(HaveOccurred()) 122 123 err = os.MkdirAll(filepath.Join(releaseDir, "chaincode", "server"), 0755) 124 Expect(err).NotTo(HaveOccurred()) 125 126 ccuserdata = &externalbuilder.ChaincodeServerUserData{ 127 Address: "ccaddress:12345", 128 DialTimeout: externalbuilder.Duration{10 * time.Second}, 129 TLSRequired: true, 130 ClientAuthRequired: true, 131 ClientKey: "fake-key", 132 ClientCert: "fake-cert", 133 RootCert: "fake-root-cert", 134 } 135 }) 136 137 AfterEach(func() { 138 os.RemoveAll(releaseDir) 139 }) 140 141 When("chaincode does not provide all info", func() { 142 Context("tls is not provided", func() { 143 It("returns TLS without client auth information", func() { 144 ccuserdata.TLSRequired = false 145 146 ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir) 147 Expect(err).NotTo(HaveOccurred()) 148 Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{ 149 Address: "ccaddress:12345", 150 ClientConfig: comm.ClientConfig{ 151 Timeout: 10 * time.Second, 152 KaOpts: comm.DefaultKeepaliveOptions, 153 }, 154 })) 155 }) 156 }) 157 158 Context("client auth is not provided", func() { 159 It("returns TLS without client auth information", func() { 160 ccuserdata.ClientAuthRequired = false 161 162 ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir) 163 Expect(err).NotTo(HaveOccurred()) 164 Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{ 165 Address: "ccaddress:12345", 166 ClientConfig: comm.ClientConfig{ 167 SecOpts: comm.SecureOptions{ 168 UseTLS: true, 169 ServerRootCAs: [][]byte{[]byte("fake-root-cert")}, 170 }, 171 KaOpts: comm.DefaultKeepaliveOptions, 172 Timeout: 10 * time.Second, 173 }, 174 })) 175 }) 176 }) 177 178 Context("dial timeout not provided", func() { 179 It("returns default dial timeout without dialtimeout", func() { 180 ccuserdata.DialTimeout = externalbuilder.Duration{} 181 182 ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir) 183 Expect(err).NotTo(HaveOccurred()) 184 Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{ 185 Address: "ccaddress:12345", 186 ClientConfig: comm.ClientConfig{ 187 SecOpts: comm.SecureOptions{ 188 UseTLS: true, 189 RequireClientCert: true, 190 Certificate: []byte("fake-cert"), 191 Key: []byte("fake-key"), 192 ServerRootCAs: [][]byte{[]byte("fake-root-cert")}, 193 }, 194 KaOpts: comm.DefaultKeepaliveOptions, 195 Timeout: 3 * time.Second, 196 }, 197 })) 198 }) 199 }) 200 201 Context("address is not provided", func() { 202 It("returns missing address error", func() { 203 ccuserdata.Address = "" 204 205 _, err := ccuserdata.ChaincodeServerInfo(releaseDir) 206 Expect(err).To(MatchError("chaincode address not provided")) 207 }) 208 }) 209 210 Context("key is not provided", func() { 211 It("returns missing key error", func() { 212 ccuserdata.ClientKey = "" 213 214 _, err := ccuserdata.ChaincodeServerInfo(releaseDir) 215 Expect(err).To(MatchError("chaincode tls key not provided")) 216 }) 217 }) 218 219 Context("cert is not provided", func() { 220 It("returns missing key error", func() { 221 ccuserdata.ClientCert = "" 222 223 _, err := ccuserdata.ChaincodeServerInfo(releaseDir) 224 Expect(err).To(MatchError("chaincode tls cert not provided")) 225 }) 226 }) 227 228 Context("root cert is not provided", func() { 229 It("returns missing root cert error", func() { 230 ccuserdata.RootCert = "" 231 232 _, err := ccuserdata.ChaincodeServerInfo(releaseDir) 233 Expect(err).To(MatchError("chaincode tls root cert not provided")) 234 }) 235 }) 236 }) 237 }) 238 239 Describe("Start", func() { 240 It("invokes the builder's run command and sets the run status", func() { 241 err := instance.Start(&ccintf.PeerConnection{ 242 Address: "fake-peer-address", 243 TLSConfig: &ccintf.TLSConfig{ 244 ClientCert: []byte("fake-client-cert"), 245 ClientKey: []byte("fake-client-key"), 246 RootCert: []byte("fake-root-cert"), 247 }, 248 }) 249 Expect(err).NotTo(HaveOccurred()) 250 Expect(instance.Session).NotTo(BeNil()) 251 252 errCh := make(chan error) 253 go func() { errCh <- instance.Session.Wait() }() 254 Eventually(errCh).Should(Receive(BeNil())) 255 }) 256 }) 257 258 Describe("Stop", func() { 259 It("terminates the process", func() { 260 cmd := exec.Command("sleep", "90") 261 sess, err := externalbuilder.Start(logger, cmd) 262 Expect(err).NotTo(HaveOccurred()) 263 instance.Session = sess 264 instance.TermTimeout = time.Minute 265 266 errCh := make(chan error) 267 go func() { errCh <- instance.Session.Wait() }() 268 Consistently(errCh).ShouldNot(Receive()) 269 270 err = instance.Stop() 271 Expect(err).ToNot(HaveOccurred()) 272 Eventually(errCh).Should(Receive(MatchError("signal: terminated"))) 273 }) 274 275 Context("when the process doesn't respond to SIGTERM within TermTimeout", func() { 276 It("kills the process with malice", func() { 277 cmd := exec.Command("testdata/ignoreterm.sh") 278 sess, err := externalbuilder.Start(logger, cmd) 279 Expect(err).NotTo(HaveOccurred()) 280 281 instance.Session = sess 282 instance.TermTimeout = time.Second 283 284 errCh := make(chan error) 285 go func() { errCh <- instance.Session.Wait() }() 286 Consistently(errCh).ShouldNot(Receive()) 287 288 err = instance.Stop() 289 Expect(err).ToNot(HaveOccurred()) 290 Eventually(errCh).Should(Receive(MatchError("signal: killed"))) 291 }) 292 }) 293 294 Context("when the instance session has not been started", func() { 295 It("returns an error", func() { 296 instance.Session = nil 297 err := instance.Stop() 298 Expect(err).To(MatchError("instance has not been started")) 299 }) 300 }) 301 }) 302 303 Describe("Wait", func() { 304 BeforeEach(func() { 305 err := instance.Start(&ccintf.PeerConnection{ 306 Address: "fake-peer-address", 307 TLSConfig: &ccintf.TLSConfig{ 308 ClientCert: []byte("fake-client-cert"), 309 ClientKey: []byte("fake-client-key"), 310 RootCert: []byte("fake-root-cert"), 311 }, 312 }) 313 Expect(err).NotTo(HaveOccurred()) 314 }) 315 316 It("returns the exit status of the run", func() { 317 code, err := instance.Wait() 318 Expect(err).NotTo(HaveOccurred()) 319 Expect(code).To(Equal(0)) 320 }) 321 322 Context("when run exits with a non-zero status", func() { 323 BeforeEach(func() { 324 instance.Builder.Location = "testdata/failbuilder" 325 instance.Builder.Name = "failbuilder" 326 err := instance.Start(&ccintf.PeerConnection{ 327 Address: "fake-peer-address", 328 }) 329 Expect(err).NotTo(HaveOccurred()) 330 }) 331 332 It("returns the exit status of the run and accompanying error", func() { 333 code, err := instance.Wait() 334 Expect(err).To(MatchError("builder 'failbuilder' run failed: exit status 1")) 335 Expect(code).To(Equal(1)) 336 }) 337 }) 338 339 Context("when the instance session has not been started", func() { 340 It("returns an error", func() { 341 instance.Session = nil 342 exitCode, err := instance.Wait() 343 Expect(err).To(MatchError("instance was not successfully started")) 344 Expect(exitCode).To(Equal(-1)) 345 }) 346 }) 347 }) 348 })