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