github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/chaincode/persistence/chaincode_package_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package persistence_test 8 9 import ( 10 "io/ioutil" 11 12 pb "github.com/hyperledger/fabric-protos-go/peer" 13 "github.com/hyperledger/fabric/core/chaincode/persistence" 14 "github.com/hyperledger/fabric/core/chaincode/persistence/mock" 15 . "github.com/onsi/ginkgo" 16 . "github.com/onsi/gomega" 17 "github.com/pkg/errors" 18 tm "github.com/stretchr/testify/mock" 19 ) 20 21 var _ = Describe("FallbackPackageLocator", func() { 22 var ( 23 cpl *persistence.ChaincodePackageLocator 24 fakeLegacyLocator *mock.LegacyCCPackageLocator 25 fpl *persistence.FallbackPackageLocator 26 ) 27 28 BeforeEach(func() { 29 cpl = &persistence.ChaincodePackageLocator{ 30 ChaincodeDir: "testdata", 31 } 32 fakeLegacyLocator = &mock.LegacyCCPackageLocator{} 33 fpl = &persistence.FallbackPackageLocator{ 34 ChaincodePackageLocator: cpl, 35 LegacyCCPackageLocator: fakeLegacyLocator, 36 } 37 }) 38 39 Describe("GetChaincodePackage", func() { 40 It("gets the chaincode package metadata and stream", func() { 41 md, stream, err := fpl.GetChaincodePackage("good-package") 42 Expect(err).NotTo(HaveOccurred()) 43 defer stream.Close() 44 Expect(md).To(Equal(&persistence.ChaincodePackageMetadata{ 45 Type: "Fake-Type", 46 Path: "Fake-Path", 47 Label: "Real-Label", 48 })) 49 code, err := ioutil.ReadAll(stream) 50 Expect(err).NotTo(HaveOccurred()) 51 Expect(code).To(Equal([]byte("package"))) 52 Expect(fakeLegacyLocator.GetChaincodeDepSpecCallCount()).To(Equal(0)) 53 }) 54 55 Context("when the package has bad metadata", func() { 56 It("wraps and returns the error", func() { 57 _, _, err := fpl.GetChaincodePackage("bad-metadata") 58 Expect(err).To(MatchError(ContainSubstring("error retrieving chaincode package metadata 'bad-metadata'"))) 59 }) 60 }) 61 62 Context("when the package has bad code", func() { 63 It("wraps and returns the error", func() { 64 _, _, err := fpl.GetChaincodePackage("missing-codepackage") 65 Expect(err).To(MatchError(ContainSubstring("error retrieving chaincode package code 'missing-codepackage'"))) 66 }) 67 }) 68 69 Context("when the package is not in the new package store", func() { 70 BeforeEach(func() { 71 fakeLegacyLocator.GetChaincodeDepSpecReturns( 72 &pb.ChaincodeDeploymentSpec{ 73 ChaincodeSpec: &pb.ChaincodeSpec{ 74 ChaincodeId: &pb.ChaincodeID{ 75 Path: "legacy-path", 76 }, 77 Type: pb.ChaincodeSpec_GOLANG, 78 }, 79 CodePackage: []byte("legacy-code"), 80 }, 81 nil) 82 }) 83 84 It("falls back to the legacy retriever", func() { 85 md, stream, err := fpl.GetChaincodePackage("legacy-package") 86 Expect(err).NotTo(HaveOccurred()) 87 defer stream.Close() 88 Expect(md).To(Equal(&persistence.ChaincodePackageMetadata{ 89 Path: "legacy-path", 90 Type: "GOLANG", 91 })) 92 code, err := ioutil.ReadAll(stream) 93 Expect(err).NotTo(HaveOccurred()) 94 Expect(code).To(Equal([]byte("legacy-code"))) 95 }) 96 97 Context("when the legacy provider returns an error", func() { 98 BeforeEach(func() { 99 fakeLegacyLocator.GetChaincodeDepSpecReturns(nil, errors.Errorf("fake-error")) 100 }) 101 102 It("wraps and returns the error", func() { 103 _, _, err := fpl.GetChaincodePackage("legacy-package") 104 Expect(err).To(MatchError("could not get legacy chaincode package 'legacy-package': fake-error")) 105 }) 106 }) 107 }) 108 }) 109 }) 110 111 var _ = Describe("ChaincodePackageParser", func() { 112 var ( 113 mockMetaProvider *mock.MetadataProvider 114 ccpp persistence.ChaincodePackageParser 115 ) 116 117 BeforeEach(func() { 118 mockMetaProvider = &mock.MetadataProvider{} 119 mockMetaProvider.On("GetDBArtifacts", tm.Anything).Return([]byte("DB artefacts"), nil) 120 121 ccpp.MetadataProvider = mockMetaProvider 122 }) 123 124 Describe("ParseChaincodePackage", func() { 125 It("parses a chaincode package", func() { 126 data, err := ioutil.ReadFile("testdata/good-package.tar.gz") 127 Expect(err).NotTo(HaveOccurred()) 128 129 ccPackage, err := ccpp.Parse(data) 130 Expect(err).NotTo(HaveOccurred()) 131 Expect(ccPackage.Metadata).To(Equal(&persistence.ChaincodePackageMetadata{ 132 Type: "Fake-Type", 133 Path: "Fake-Path", 134 Label: "Real-Label", 135 })) 136 Expect(ccPackage.DBArtifacts).To(Equal([]byte("DB artefacts"))) 137 }) 138 139 Context("when the data is not gzipped", func() { 140 It("fails", func() { 141 _, err := ccpp.Parse([]byte("bad-data")) 142 Expect(err).To(MatchError("error reading as gzip stream: unexpected EOF")) 143 }) 144 }) 145 146 Context("when the retrieval of the DB metadata fails", func() { 147 BeforeEach(func() { 148 mockMetaProvider = &mock.MetadataProvider{} 149 mockMetaProvider.On("GetDBArtifacts", tm.Anything).Return(nil, errors.New("not good")) 150 151 ccpp.MetadataProvider = mockMetaProvider 152 }) 153 154 It("fails", func() { 155 data, err := ioutil.ReadFile("testdata/good-package.tar.gz") 156 Expect(err).NotTo(HaveOccurred()) 157 158 ccPackage, err := ccpp.Parse(data) 159 Expect(ccPackage).To(BeNil()) 160 Expect(err).To(MatchError(ContainSubstring("error retrieving DB artifacts from code package"))) 161 }) 162 }) 163 164 Context("when the chaincode package metadata is missing", func() { 165 It("fails", func() { 166 data, err := ioutil.ReadFile("testdata/missing-metadata.tar.gz") 167 Expect(err).NotTo(HaveOccurred()) 168 169 _, err = ccpp.Parse(data) 170 Expect(err).To(MatchError("did not find any package metadata (missing metadata.json)")) 171 }) 172 }) 173 174 Context("when the chaincode package metadata is corrupt", func() { 175 It("fails", func() { 176 data, err := ioutil.ReadFile("testdata/bad-metadata.tar.gz") 177 Expect(err).NotTo(HaveOccurred()) 178 179 _, err = ccpp.Parse(data) 180 Expect(err).To(MatchError("could not unmarshal metadata.json as json: invalid character '\\n' in string literal")) 181 }) 182 }) 183 184 Context("when the label is empty or missing", func() { 185 It("fails", func() { 186 data, err := ioutil.ReadFile("testdata/empty-label.tar.gz") 187 Expect(err).NotTo(HaveOccurred()) 188 189 _, err = ccpp.Parse(data) 190 Expect(err.Error()).To(ContainSubstring("invalid label ''. Label must be non-empty, can only consist of alphanumerics, symbols from '.+-_', and can only begin with alphanumerics")) 191 }) 192 }) 193 194 Context("when the label contains forbidden characters", func() { 195 It("fails", func() { 196 data, err := ioutil.ReadFile("testdata/bad-label.tar.gz") 197 Expect(err).NotTo(HaveOccurred()) 198 199 _, err = ccpp.Parse(data) 200 Expect(err.Error()).To(ContainSubstring("invalid label 'Bad-Label!'. Label must be non-empty, can only consist of alphanumerics, symbols from '.+-_', and can only begin with alphanumerics")) 201 }) 202 }) 203 204 Context("when the tar file is corrupted", func() { 205 It("fails", func() { 206 data, err := ioutil.ReadFile("testdata/corrupted-package.tar.gz") 207 Expect(err).NotTo(HaveOccurred()) 208 209 _, err = ccpp.Parse(data) 210 Expect(err).To(MatchError("could not read Chaincode-Package-Metadata.json from tar: unexpected EOF")) 211 }) 212 }) 213 214 Context("when the tar has non-regular files", func() { 215 It("fails", func() { 216 data, err := ioutil.ReadFile("testdata/non-regular-file.tar.gz") 217 Expect(err).NotTo(HaveOccurred()) 218 219 _, err = ccpp.Parse(data) 220 Expect(err).To(MatchError("tar entry code.tar.gz is not a regular file, type 50")) 221 }) 222 }) 223 224 Context("when the tar has a corrupt header entry", func() { 225 It("fails", func() { 226 data, err := ioutil.ReadFile("testdata/corrupted-header.tar.gz") 227 Expect(err).NotTo(HaveOccurred()) 228 229 _, err = ccpp.Parse(data) 230 Expect(err).To(MatchError("error inspecting next tar header: flate: corrupt input before offset 86")) 231 }) 232 }) 233 234 Context("when the tar has too many entries", func() { 235 It("logs a warning but otherwise allows it", func() { 236 data, err := ioutil.ReadFile("testdata/too-many-files.tar.gz") 237 Expect(err).NotTo(HaveOccurred()) 238 239 _, err = ccpp.Parse(data) 240 Expect(err).NotTo(HaveOccurred()) 241 }) 242 }) 243 244 Context("when the tar is missing a code-package", func() { 245 It("fails", func() { 246 data, err := ioutil.ReadFile("testdata/missing-codepackage.tar.gz") 247 Expect(err).NotTo(HaveOccurred()) 248 249 _, err = ccpp.Parse(data) 250 Expect(err).To(MatchError("did not find a code package inside the package")) 251 }) 252 }) 253 }) 254 }) 255 256 var _ = Describe("ChaincodePackageLocator", func() { 257 var ( 258 locator *persistence.ChaincodePackageLocator 259 ) 260 261 BeforeEach(func() { 262 locator = &persistence.ChaincodePackageLocator{ 263 ChaincodeDir: "/fake-dir", 264 } 265 }) 266 267 Describe("ChaincodePackageStreamer", func() { 268 It("creates a ChaincodePackageStreamer for the given packageID", func() { 269 streamer := locator.ChaincodePackageStreamer("test-package") 270 Expect(streamer).To(Equal(&persistence.ChaincodePackageStreamer{ 271 PackagePath: "/fake-dir/test-package.tar.gz", 272 })) 273 }) 274 }) 275 }) 276 277 var _ = Describe("ChaincodePackageStreamer", func() { 278 var ( 279 streamer *persistence.ChaincodePackageStreamer 280 ) 281 282 BeforeEach(func() { 283 streamer = &persistence.ChaincodePackageStreamer{ 284 PackagePath: "testdata/good-package.tar.gz", 285 } 286 }) 287 288 Describe("Metadata", func() { 289 It("reads the metadata from the package", func() { 290 md, err := streamer.Metadata() 291 Expect(err).NotTo(HaveOccurred()) 292 Expect(md).To(Equal(&persistence.ChaincodePackageMetadata{ 293 Type: "Fake-Type", 294 Path: "Fake-Path", 295 Label: "Real-Label", 296 })) 297 }) 298 299 Context("when the metadata file cannot be found", func() { 300 BeforeEach(func() { 301 streamer.PackagePath = "testdata/missing-metadata.tar.gz" 302 }) 303 304 It("wraps and returns the error", func() { 305 _, err := streamer.Metadata() 306 Expect(err).To(MatchError("could not get metadata file: did not find file 'metadata.json' in package")) 307 }) 308 }) 309 310 Context("when the metadata file cannot be parsed", func() { 311 BeforeEach(func() { 312 streamer.PackagePath = "testdata/bad-metadata.tar.gz" 313 }) 314 315 It("wraps and returns the error", func() { 316 _, err := streamer.Metadata() 317 Expect(err).To(MatchError("could not parse metadata file: invalid character '\\n' in string literal")) 318 }) 319 }) 320 }) 321 322 Describe("Code", func() { 323 It("reads a file from the package", func() { 324 code, err := streamer.Code() 325 Expect(err).NotTo(HaveOccurred()) 326 codeBytes, err := ioutil.ReadAll(code) 327 code.Close() 328 Expect(err).NotTo(HaveOccurred()) 329 Expect(codeBytes).To(Equal([]byte("package"))) 330 }) 331 332 Context("when the file cannot be found because the code is not a regular file", func() { 333 BeforeEach(func() { 334 streamer.PackagePath = "testdata/missing-codepackage.tar.gz" 335 }) 336 337 It("wraps and returns the error", func() { 338 _, err := streamer.Code() 339 Expect(err).To(MatchError("could not get code package: did not find file 'code.tar.gz' in package")) 340 }) 341 }) 342 }) 343 344 Describe("File", func() { 345 It("reads a file from the package", func() { 346 code, err := streamer.File("code.tar.gz") 347 Expect(err).NotTo(HaveOccurred()) 348 codeBytes, err := ioutil.ReadAll(code) 349 code.Close() 350 Expect(err).NotTo(HaveOccurred()) 351 Expect(codeBytes).To(Equal([]byte("package"))) 352 }) 353 354 Context("when the file is not a regular file", func() { 355 BeforeEach(func() { 356 streamer.PackagePath = "testdata/non-regular-file.tar.gz" 357 }) 358 359 It("wraps and returns the error", func() { 360 _, err := streamer.File("code.tar.gz") 361 Expect(err).To(MatchError("tar entry code.tar.gz is not a regular file, type 50")) 362 }) 363 }) 364 365 Context("when the code cannot be found because the archive is corrupt", func() { 366 BeforeEach(func() { 367 streamer.PackagePath = "testdata/bad-archive.tar.gz" 368 }) 369 370 It("wraps and returns the error", func() { 371 _, err := streamer.File("code.tar.gz") 372 Expect(err).To(MatchError("could not open chaincode package at 'testdata/bad-archive.tar.gz': open testdata/bad-archive.tar.gz: no such file or directory")) 373 }) 374 }) 375 376 Context("when the code cannot be found because the header is corrupt", func() { 377 BeforeEach(func() { 378 streamer.PackagePath = "testdata/corrupted-header.tar.gz" 379 }) 380 381 It("wraps and returns the error", func() { 382 _, err := streamer.File("code.tar.gz") 383 Expect(err).To(MatchError("error inspecting next tar header: flate: corrupt input before offset 86")) 384 }) 385 }) 386 387 Context("when the code cannot be found because the gzip is corrupt", func() { 388 BeforeEach(func() { 389 streamer.PackagePath = "testdata/corrupted-gzip.tar.gz" 390 }) 391 392 It("wraps and returns the error", func() { 393 _, err := streamer.File("code.tar.gz") 394 Expect(err).To(MatchError("error reading as gzip stream: unexpected EOF")) 395 }) 396 }) 397 }) 398 })