github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+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/comm" 23 "github.com/hyperledger/fabric/core/container/ccintf" 24 "github.com/hyperledger/fabric/core/container/externalbuilder" 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 }, 44 } 45 }) 46 47 Describe("ChaincodeServerInfo", func() { 48 BeforeEach(func() { 49 var err error 50 instance.ReleaseDir, err = ioutil.TempDir("", "cc-conn-test") 51 Expect(err).NotTo(HaveOccurred()) 52 53 err = os.MkdirAll(filepath.Join(instance.ReleaseDir, "chaincode", "server"), 0755) 54 Expect(err).NotTo(HaveOccurred()) 55 //initiaze with a well-formed, all fields set, connection.json file 56 ccdata := `{"address": "ccaddress:12345", "tls_required": true, "dial_timeout": "10s", "client_auth_required": true, "key_path": "key.pem", "cert_path": "cert.pem", "root_cert_path": "root.pem"}` 57 err = ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json"), []byte(ccdata), 0600) 58 Expect(err).NotTo(HaveOccurred()) 59 err = ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "key.pem"), []byte("fake-key"), 0600) 60 Expect(err).NotTo(HaveOccurred()) 61 err = ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "cert.pem"), []byte("fake-cert"), 0600) 62 Expect(err).NotTo(HaveOccurred()) 63 err = ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "root.pem"), []byte("fake-root-cert"), 0600) 64 Expect(err).NotTo(HaveOccurred()) 65 }) 66 67 AfterEach(func() { 68 os.RemoveAll(instance.ReleaseDir) 69 }) 70 71 It("returns chaincode connection", func() { 72 ccinfo, err := instance.ChaincodeServerInfo() 73 Expect(err).NotTo(HaveOccurred()) 74 Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{ 75 Address: "ccaddress:12345", 76 ClientConfig: comm.ClientConfig{ 77 SecOpts: comm.SecureOptions{ 78 UseTLS: true, 79 RequireClientCert: true, 80 Certificate: []byte("fake-cert"), 81 Key: []byte("fake-key"), 82 ServerRootCAs: [][]byte{[]byte("fake-root-cert")}, 83 }, 84 KaOpts: comm.DefaultKeepaliveOptions, 85 Timeout: 10 * time.Second, 86 }, 87 })) 88 }) 89 90 When("connection.json is not provided", func() { 91 BeforeEach(func() { 92 err := os.Remove(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json")) 93 Expect(err).NotTo(HaveOccurred()) 94 }) 95 96 It("returns nil server info", func() { 97 ccinfo, err := instance.ChaincodeServerInfo() 98 Expect(err).NotTo(HaveOccurred()) 99 Expect(ccinfo).To(BeNil()) 100 }) 101 }) 102 103 When("chaincode info is badly formed", func() { 104 BeforeEach(func() { 105 ccdata := `{"badly formed chaincode"}` 106 err := ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json"), []byte(ccdata), 0600) 107 Expect(err).NotTo(HaveOccurred()) 108 }) 109 110 It("returns a malformed chaincode error", func() { 111 _, err := instance.ChaincodeServerInfo() 112 Expect(err).To(MatchError(ContainSubstring("malformed chaincode info"))) 113 }) 114 }) 115 }) 116 117 Describe("ChaincodeServerUserData", func() { 118 var ( 119 ccuserdata *externalbuilder.ChaincodeServerUserData 120 releaseDir string 121 ) 122 123 BeforeEach(func() { 124 var err error 125 releaseDir, err = ioutil.TempDir("", "cc-conn-test") 126 Expect(err).NotTo(HaveOccurred()) 127 128 err = os.MkdirAll(filepath.Join(releaseDir, "chaincode", "server"), 0755) 129 Expect(err).NotTo(HaveOccurred()) 130 err = ioutil.WriteFile(filepath.Join(releaseDir, "key.pem"), []byte("fake-key"), 0600) 131 Expect(err).NotTo(HaveOccurred()) 132 err = ioutil.WriteFile(filepath.Join(releaseDir, "cert.pem"), []byte("fake-cert"), 0600) 133 Expect(err).NotTo(HaveOccurred()) 134 err = ioutil.WriteFile(filepath.Join(releaseDir, "root.pem"), []byte("fake-root-cert"), 0600) 135 Expect(err).NotTo(HaveOccurred()) 136 137 ccuserdata = &externalbuilder.ChaincodeServerUserData{ 138 Address: "ccaddress:12345", 139 DialTimeout: externalbuilder.Duration{10 * time.Second}, 140 TlsRequired: true, 141 ClientAuthRequired: true, 142 KeyPath: "key.pem", 143 CertPath: "cert.pem", 144 RootCertPath: "root.pem", 145 } 146 }) 147 AfterEach(func() { 148 os.RemoveAll(releaseDir) 149 }) 150 When("chaincode does not provide all info", func() { 151 Context("tls is not provided", func() { 152 It("returns TLS without client auth information", func() { 153 //"tls" missing 154 ccuserdata.TlsRequired = false 155 156 ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir) 157 Expect(err).NotTo(HaveOccurred()) 158 Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{ 159 Address: "ccaddress:12345", 160 ClientConfig: comm.ClientConfig{ 161 Timeout: 10 * time.Second, 162 KaOpts: comm.DefaultKeepaliveOptions, 163 }, 164 })) 165 }) 166 }) 167 Context("client auth is not provided", func() { 168 It("returns TLS without client auth information", func() { 169 //"client_auth_required" missing 170 ccuserdata.ClientAuthRequired = false 171 172 ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir) 173 Expect(err).NotTo(HaveOccurred()) 174 Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{ 175 Address: "ccaddress:12345", 176 ClientConfig: comm.ClientConfig{ 177 SecOpts: comm.SecureOptions{ 178 UseTLS: true, 179 ServerRootCAs: [][]byte{[]byte("fake-root-cert")}, 180 }, 181 KaOpts: comm.DefaultKeepaliveOptions, 182 Timeout: 10 * time.Second, 183 }, 184 })) 185 }) 186 }) 187 Context("dial timeout not provided", func() { 188 It("returns default dial timeout without dialtimeout", func() { 189 //"dial_timeout" missing 190 ccuserdata.DialTimeout = externalbuilder.Duration{} 191 192 ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir) 193 Expect(err).NotTo(HaveOccurred()) 194 Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{ 195 Address: "ccaddress:12345", 196 ClientConfig: comm.ClientConfig{ 197 SecOpts: comm.SecureOptions{ 198 UseTLS: true, 199 RequireClientCert: true, 200 Certificate: []byte("fake-cert"), 201 Key: []byte("fake-key"), 202 ServerRootCAs: [][]byte{[]byte("fake-root-cert")}, 203 }, 204 KaOpts: comm.DefaultKeepaliveOptions, 205 Timeout: 3 * time.Second, 206 }, 207 })) 208 }) 209 }) 210 Context("address is not provided", func() { 211 It("returns missing address error", func() { 212 //"address" missing 213 ccuserdata.Address = "" 214 215 _, err := ccuserdata.ChaincodeServerInfo(releaseDir) 216 Expect(err).To(MatchError("chaincode address not provided")) 217 }) 218 }) 219 Context("key is not provided", func() { 220 It("returns missing key error", func() { 221 //"key" missing 222 ccuserdata.KeyPath = "" 223 224 _, err := ccuserdata.ChaincodeServerInfo(releaseDir) 225 Expect(err).To(MatchError("chaincode tls key not provided")) 226 }) 227 }) 228 Context("cert is not provided", func() { 229 It("returns missing key error", func() { 230 //"cert" missing 231 ccuserdata.CertPath = "" 232 233 _, err := ccuserdata.ChaincodeServerInfo(releaseDir) 234 Expect(err).To(MatchError("chaincode tls cert not provided")) 235 }) 236 }) 237 Context("root cert is not provided", func() { 238 It("returns missing root cert error", func() { 239 //"root" missing 240 ccuserdata.RootCertPath = "" 241 242 _, err := ccuserdata.ChaincodeServerInfo(releaseDir) 243 Expect(err).To(MatchError("chaincode tls root cert not provided")) 244 }) 245 }) 246 Context("cert file is missing", func() { 247 It("returns missing cert file error", func() { 248 //cert file is missing 249 err := os.Remove(filepath.Join(releaseDir, "cert.pem")) 250 Expect(err).NotTo(HaveOccurred()) 251 252 _, err = ccuserdata.ChaincodeServerInfo(releaseDir) 253 Expect(err).To(MatchError(ContainSubstring("error reading cert file"))) 254 }) 255 }) 256 Context("key file is missing", func() { 257 It("returns missing key file error", func() { 258 //key file is missing 259 err := os.Remove(filepath.Join(releaseDir, "key.pem")) 260 Expect(err).NotTo(HaveOccurred()) 261 262 _, err = ccuserdata.ChaincodeServerInfo(releaseDir) 263 Expect(err).To(MatchError(ContainSubstring("error reading key file"))) 264 }) 265 }) 266 Context("root cert file is missing", func() { 267 It("returns missing root cert file error", func() { 268 //key file is missing 269 err := os.Remove(filepath.Join(releaseDir, "root.pem")) 270 Expect(err).NotTo(HaveOccurred()) 271 272 _, err = ccuserdata.ChaincodeServerInfo(releaseDir) 273 Expect(err).To(MatchError(ContainSubstring("error reading root cert file"))) 274 }) 275 }) 276 }) 277 }) 278 279 Describe("Start", func() { 280 It("invokes the builder's run command and sets the run status", func() { 281 err := instance.Start(&ccintf.PeerConnection{ 282 Address: "fake-peer-address", 283 TLSConfig: &ccintf.TLSConfig{ 284 ClientCert: []byte("fake-client-cert"), 285 ClientKey: []byte("fake-client-key"), 286 RootCert: []byte("fake-root-cert"), 287 }, 288 }) 289 Expect(err).NotTo(HaveOccurred()) 290 Expect(instance.Session).NotTo(BeNil()) 291 292 errCh := make(chan error) 293 go func() { errCh <- instance.Session.Wait() }() 294 Eventually(errCh).Should(Receive(BeNil())) 295 }) 296 }) 297 298 Describe("Stop", func() { 299 It("terminates the process", func() { 300 cmd := exec.Command("sleep", "90") 301 sess, err := externalbuilder.Start(logger, cmd) 302 Expect(err).NotTo(HaveOccurred()) 303 instance.Session = sess 304 instance.TermTimeout = time.Minute 305 306 errCh := make(chan error) 307 go func() { errCh <- instance.Session.Wait() }() 308 Consistently(errCh).ShouldNot(Receive()) 309 310 err = instance.Stop() 311 Expect(err).ToNot(HaveOccurred()) 312 Eventually(errCh).Should(Receive(MatchError("signal: terminated"))) 313 }) 314 315 Context("when the process doesn't respond to SIGTERM within TermTimeout", func() { 316 It("kills the process with malice", func() { 317 cmd := exec.Command("testdata/ignoreterm.sh") 318 sess, err := externalbuilder.Start(logger, cmd) 319 Expect(err).NotTo(HaveOccurred()) 320 321 instance.Session = sess 322 instance.TermTimeout = time.Second 323 324 errCh := make(chan error) 325 go func() { errCh <- instance.Session.Wait() }() 326 Consistently(errCh).ShouldNot(Receive()) 327 328 err = instance.Stop() 329 Expect(err).ToNot(HaveOccurred()) 330 Eventually(errCh).Should(Receive(MatchError("signal: killed"))) 331 }) 332 }) 333 334 Context("when the instance session has not been started", func() { 335 It("returns an error", func() { 336 instance.Session = nil 337 err := instance.Stop() 338 Expect(err).To(MatchError("instance has not been started")) 339 }) 340 }) 341 }) 342 343 Describe("Wait", func() { 344 BeforeEach(func() { 345 err := instance.Start(&ccintf.PeerConnection{ 346 Address: "fake-peer-address", 347 TLSConfig: &ccintf.TLSConfig{ 348 ClientCert: []byte("fake-client-cert"), 349 ClientKey: []byte("fake-client-key"), 350 RootCert: []byte("fake-root-cert"), 351 }, 352 }) 353 Expect(err).NotTo(HaveOccurred()) 354 }) 355 356 It("returns the exit status of the run", func() { 357 code, err := instance.Wait() 358 Expect(err).NotTo(HaveOccurred()) 359 Expect(code).To(Equal(0)) 360 }) 361 362 Context("when run exits with a non-zero status", func() { 363 BeforeEach(func() { 364 instance.Builder.Location = "testdata/failbuilder" 365 instance.Builder.Name = "failbuilder" 366 err := instance.Start(&ccintf.PeerConnection{ 367 Address: "fake-peer-address", 368 }) 369 Expect(err).NotTo(HaveOccurred()) 370 }) 371 372 It("returns the exit status of the run and accompanying error", func() { 373 code, err := instance.Wait() 374 Expect(err).To(MatchError("builder 'failbuilder' run failed: exit status 1")) 375 Expect(code).To(Equal(1)) 376 }) 377 }) 378 379 Context("when the instance session has not been started", func() { 380 It("returns an error", func() { 381 instance.Session = nil 382 exitCode, err := instance.Wait() 383 Expect(err).To(MatchError("instance was not successfully started")) 384 Expect(exitCode).To(Equal(-1)) 385 }) 386 }) 387 }) 388 })