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  })