github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/core/container/externalbuilder/externalbuilder_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 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "strings" 15 16 "github.com/hyperledger/fabric/common/flogging" 17 "github.com/hyperledger/fabric/core/container/ccintf" 18 "github.com/hyperledger/fabric/core/container/externalbuilder" 19 "github.com/hyperledger/fabric/core/peer" 20 . "github.com/onsi/ginkgo" 21 . "github.com/onsi/gomega" 22 "go.uber.org/zap" 23 "go.uber.org/zap/zapcore" 24 ) 25 26 var _ = Describe("externalbuilder", func() { 27 var ( 28 codePackage *os.File 29 logger *flogging.FabricLogger 30 md []byte 31 ) 32 33 BeforeEach(func() { 34 var err error 35 codePackage, err = os.Open("testdata/normal_archive.tar.gz") 36 Expect(err).NotTo(HaveOccurred()) 37 38 md = []byte(`{"some":"fake-metadata"}`) 39 40 enc := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{MessageKey: "msg"}) 41 core := zapcore.NewCore(enc, zapcore.AddSync(GinkgoWriter), zap.NewAtomicLevel()) 42 logger = flogging.NewFabricLogger(zap.New(core).Named("logger")) 43 }) 44 45 AfterEach(func() { 46 if codePackage != nil { 47 codePackage.Close() 48 } 49 }) 50 51 Describe("NewBuildContext", func() { 52 It("creates a new context, including temporary locations", func() { 53 buildContext, err := externalbuilder.NewBuildContext("fake-package-id", md, codePackage) 54 Expect(err).NotTo(HaveOccurred()) 55 defer buildContext.Cleanup() 56 57 Expect(buildContext.ScratchDir).NotTo(BeEmpty()) 58 Expect(buildContext.ScratchDir).To(BeADirectory()) 59 60 Expect(buildContext.SourceDir).NotTo(BeEmpty()) 61 Expect(buildContext.SourceDir).To(BeADirectory()) 62 63 Expect(buildContext.ReleaseDir).NotTo(BeEmpty()) 64 Expect(buildContext.ReleaseDir).To(BeADirectory()) 65 66 Expect(buildContext.BldDir).NotTo(BeEmpty()) 67 Expect(buildContext.BldDir).To(BeADirectory()) 68 69 Expect(filepath.Join(buildContext.SourceDir, "a/test.file")).To(BeARegularFile()) 70 }) 71 72 Context("when the archive cannot be extracted", func() { 73 It("returns an error", func() { 74 codePackage, err := os.Open("testdata/archive_with_absolute.tar.gz") 75 Expect(err).NotTo(HaveOccurred()) 76 defer codePackage.Close() 77 _, err = externalbuilder.NewBuildContext("fake-package-id", md, codePackage) 78 Expect(err).To(MatchError(ContainSubstring("could not untar source package"))) 79 }) 80 }) 81 82 Context("when package id contains inappropriate chars", func() { 83 It("replaces them with dash", func() { 84 buildContext, err := externalbuilder.NewBuildContext("i&am/pkg:id", md, codePackage) 85 Expect(err).NotTo(HaveOccurred()) 86 Expect(buildContext.ScratchDir).To(ContainSubstring("fabric-i-am-pkg-id")) 87 }) 88 }) 89 }) 90 91 Describe("Detector", func() { 92 var ( 93 durablePath string 94 detector *externalbuilder.Detector 95 ) 96 97 BeforeEach(func() { 98 var err error 99 durablePath, err = ioutil.TempDir("", "detect-test") 100 Expect(err).NotTo(HaveOccurred()) 101 102 detector = &externalbuilder.Detector{ 103 Builders: externalbuilder.CreateBuilders([]peer.ExternalBuilder{ 104 {Path: "bad1", Name: "bad1"}, 105 {Path: "testdata/goodbuilder", Name: "goodbuilder"}, 106 {Path: "bad2", Name: "bad2"}, 107 }), 108 DurablePath: durablePath, 109 } 110 }) 111 112 AfterEach(func() { 113 if durablePath != "" { 114 err := os.RemoveAll(durablePath) 115 Expect(err).NotTo(HaveOccurred()) 116 } 117 }) 118 119 Describe("Build", func() { 120 It("iterates over all detectors and chooses the one that matches", func() { 121 instance, err := detector.Build("fake-package-id", md, codePackage) 122 Expect(err).NotTo(HaveOccurred()) 123 Expect(instance.Builder.Name).To(Equal("goodbuilder")) 124 }) 125 126 Context("when no builder can be found", func() { 127 BeforeEach(func() { 128 detector.Builders = externalbuilder.CreateBuilders([]peer.ExternalBuilder{ 129 {Path: "bad1", Name: "bad1"}, 130 }) 131 }) 132 133 It("returns a nil instance", func() { 134 i, err := detector.Build("fake-package-id", md, codePackage) 135 Expect(err).NotTo(HaveOccurred()) 136 Expect(i).To(BeNil()) 137 }) 138 }) 139 140 It("persists the build output", func() { 141 _, err := detector.Build("fake-package-id", md, codePackage) 142 Expect(err).NotTo(HaveOccurred()) 143 144 Expect(filepath.Join(durablePath, "fake-package-id", "bld")).To(BeADirectory()) 145 Expect(filepath.Join(durablePath, "fake-package-id", "release")).To(BeADirectory()) 146 Expect(filepath.Join(durablePath, "fake-package-id", "build-info.json")).To(BeARegularFile()) 147 }) 148 149 Context("when the durable path cannot be created", func() { 150 BeforeEach(func() { 151 detector.DurablePath = "path/to/nowhere" 152 }) 153 154 It("wraps and returns the error", func() { 155 _, err := detector.Build("fake-package-id", md, codePackage) 156 Expect(err).To(MatchError("could not create dir 'path/to/nowhere/fake-package-id' to persist build output: mkdir path/to/nowhere/fake-package-id: no such file or directory")) 157 }) 158 }) 159 }) 160 161 Describe("CachedBuild", func() { 162 var existingInstance *externalbuilder.Instance 163 164 BeforeEach(func() { 165 var err error 166 existingInstance, err = detector.Build("fake-package-id", md, codePackage) 167 Expect(err).NotTo(HaveOccurred()) 168 169 // ensure the builder will fail if invoked 170 detector.Builders[0].Location = "bad-path" 171 }) 172 173 It("returns the existing built instance", func() { 174 newInstance, err := detector.Build("fake-package-id", md, codePackage) 175 Expect(err).NotTo(HaveOccurred()) 176 Expect(existingInstance).To(Equal(newInstance)) 177 }) 178 179 When("the build-info is missing", func() { 180 BeforeEach(func() { 181 err := os.RemoveAll(filepath.Join(durablePath, "fake-package-id", "build-info.json")) 182 Expect(err).NotTo(HaveOccurred()) 183 }) 184 185 It("returns an error", func() { 186 _, err := detector.Build("fake-package-id", md, codePackage) 187 Expect(err).To(MatchError(ContainSubstring("existing build could not be restored: could not read '"))) 188 }) 189 }) 190 191 When("the build-info is corrupted", func() { 192 BeforeEach(func() { 193 err := ioutil.WriteFile(filepath.Join(durablePath, "fake-package-id", "build-info.json"), []byte("{corrupted"), 0600) 194 Expect(err).NotTo(HaveOccurred()) 195 }) 196 197 It("returns an error", func() { 198 _, err := detector.Build("fake-package-id", md, codePackage) 199 Expect(err).To(MatchError(ContainSubstring("invalid character 'c' looking for beginning of object key string"))) 200 }) 201 }) 202 203 When("the builder is no longer available", func() { 204 BeforeEach(func() { 205 detector.Builders = detector.Builders[:1] 206 }) 207 208 It("returns an error", func() { 209 _, err := detector.Build("fake-package-id", md, codePackage) 210 Expect(err).To(MatchError("existing build could not be restored: chaincode 'fake-package-id' was already built with builder 'goodbuilder', but that builder is no longer available")) 211 }) 212 }) 213 }) 214 }) 215 216 Describe("Builders", func() { 217 var ( 218 builder *externalbuilder.Builder 219 buildContext *externalbuilder.BuildContext 220 ) 221 222 BeforeEach(func() { 223 builder = &externalbuilder.Builder{ 224 Location: "testdata/goodbuilder", 225 Name: "goodbuilder", 226 Logger: logger, 227 } 228 229 var err error 230 buildContext, err = externalbuilder.NewBuildContext("fake-package-id", md, codePackage) 231 Expect(err).NotTo(HaveOccurred()) 232 }) 233 234 AfterEach(func() { 235 buildContext.Cleanup() 236 }) 237 238 Describe("Detect", func() { 239 It("detects when the package is handled by the external builder", func() { 240 result := builder.Detect(buildContext) 241 Expect(result).To(BeTrue()) 242 }) 243 244 Context("when the detector exits with a non-zero status", func() { 245 BeforeEach(func() { 246 builder.Location = "testdata/failbuilder" 247 }) 248 249 It("returns false", func() { 250 result := builder.Detect(buildContext) 251 Expect(result).To(BeFalse()) 252 }) 253 }) 254 }) 255 256 Describe("Build", func() { 257 It("builds the package by invoking external builder", func() { 258 err := builder.Build(buildContext) 259 Expect(err).NotTo(HaveOccurred()) 260 }) 261 262 Context("when the builder exits with a non-zero status", func() { 263 BeforeEach(func() { 264 builder.Location = "testdata/failbuilder" 265 builder.Name = "failbuilder" 266 }) 267 268 It("returns an error", func() { 269 err := builder.Build(buildContext) 270 Expect(err).To(MatchError("external builder 'failbuilder' failed: exit status 1")) 271 }) 272 }) 273 }) 274 275 Describe("Release", func() { 276 It("releases the package by invoking external builder", func() { 277 err := builder.Release(buildContext) 278 Expect(err).NotTo(HaveOccurred()) 279 }) 280 281 When("the release binary is not in the builder", func() { 282 BeforeEach(func() { 283 builder.Location = "bad-builder-location" 284 }) 285 286 It("returns no error as release is optional", func() { 287 err := builder.Release(buildContext) 288 Expect(err).NotTo(HaveOccurred()) 289 }) 290 }) 291 292 When("the builder exits with a non-zero status", func() { 293 BeforeEach(func() { 294 builder.Location = "testdata/failbuilder" 295 builder.Name = "failbuilder" 296 }) 297 298 It("returns an error", func() { 299 err := builder.Release(buildContext) 300 Expect(err).To(MatchError("builder 'failbuilder' release failed: exit status 1")) 301 }) 302 }) 303 }) 304 305 Describe("Run", func() { 306 var ( 307 fakeConnection *ccintf.PeerConnection 308 bldDir string 309 ) 310 311 BeforeEach(func() { 312 var err error 313 bldDir, err = ioutil.TempDir("", "run-test") 314 Expect(err).NotTo(HaveOccurred()) 315 316 fakeConnection = &ccintf.PeerConnection{ 317 Address: "fake-peer-address", 318 TLSConfig: &ccintf.TLSConfig{ 319 ClientCert: []byte("fake-client-cert"), 320 ClientKey: []byte("fake-client-key"), 321 RootCert: []byte("fake-root-cert"), 322 }, 323 } 324 }) 325 326 AfterEach(func() { 327 if bldDir != "" { 328 err := os.RemoveAll(bldDir) 329 Expect(err).NotTo(HaveOccurred()) 330 } 331 }) 332 333 It("runs the package by invoking external builder", func() { 334 sess, err := builder.Run("test-ccid", bldDir, fakeConnection) 335 Expect(err).NotTo(HaveOccurred()) 336 337 errCh := make(chan error) 338 go func() { errCh <- sess.Wait() }() 339 Eventually(errCh).Should(Receive(BeNil())) 340 }) 341 }) 342 343 Describe("NewCommand", func() { 344 It("only propagates expected variables", func() { 345 var expectedEnv []string 346 for _, key := range externalbuilder.DefaultEnvWhitelist { 347 if val, ok := os.LookupEnv(key); ok { 348 expectedEnv = append(expectedEnv, fmt.Sprintf("%s=%s", key, val)) 349 } 350 } 351 352 cmd := builder.NewCommand("/usr/bin/env") 353 Expect(cmd.Env).To(ConsistOf(expectedEnv)) 354 355 output, err := cmd.CombinedOutput() 356 Expect(err).NotTo(HaveOccurred()) 357 env := strings.Split(strings.TrimSuffix(string(output), "\n"), "\n") 358 Expect(env).To(ConsistOf(expectedEnv)) 359 }) 360 }) 361 }) 362 363 Describe("SanitizeCCIDPath", func() { 364 It("forbids the set of forbidden windows characters", func() { 365 sanitizedPath := externalbuilder.SanitizeCCIDPath(`<>:"/\|?*&`) 366 Expect(sanitizedPath).To(Equal("----------")) 367 }) 368 }) 369 })