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