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