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