Skip to content

Commit

Permalink
FAB-17001 Delete chaincode package on build fail
Browse files Browse the repository at this point in the history
The new lifecycle has no existing contract to honor with respect to
installing chaincodes which are not buildable at install time, so it is
odd that we adhere to that contract from the previous LSCC case.

This CR changes the new lifecycle to delete the chaincode from the
filesystem if the build is unsuccessful, under the assumption that
future attempts to build this chaincode will similarly fail, and the
package will simply be taking up space on the filesystem.

Signed-off-by: Jason Yellick <[email protected]>
Change-Id: I7d12a767dbaf6c9d0b497139c4a99793b4d29a16
  • Loading branch information
Jason Yellick committed Nov 8, 2019
1 parent 6707428 commit 8ff17e1
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 4 deletions.
10 changes: 6 additions & 4 deletions core/chaincode/lifecycle/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ type ChaincodeStore interface {
Save(label string, ccInstallPkg []byte) (string, error)
ListInstalledChaincodes() ([]chaincode.InstalledChaincode, error)
Load(packageID string) (ccInstallPkg []byte, err error)
Delete(packageID string) error
}

type PackageParser interface {
Expand Down Expand Up @@ -495,20 +496,21 @@ func (ef *ExternalFunctions) InstallChaincode(chaincodeInstallPackage []byte) (*
return nil, errors.WithMessage(err, "could not save cc install package")
}

if ef.InstallListener != nil {
ef.InstallListener.HandleChaincodeInstalled(pkg.Metadata, packageID)
}

buildStatus, ok := ef.BuildRegistry.BuildStatus(packageID)
if !ok {
err := ef.ChaincodeBuilder.Build(packageID)
buildStatus.Notify(err)
}
<-buildStatus.Done()
if err := buildStatus.Err(); err != nil {
ef.Resources.ChaincodeStore.Delete(packageID)
return nil, errors.WithMessage(err, "could not build chaincode")
}

if ef.InstallListener != nil {
ef.InstallListener.HandleChaincodeInstalled(pkg.Metadata, packageID)
}

logger.Infof("successfully installed chaincode with package ID '%s'", packageID)

return &chaincode.InstalledChaincode{
Expand Down
6 changes: 6 additions & 0 deletions core/chaincode/lifecycle/lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ var _ = Describe("ExternalFunctions", func() {
_, err := ef.InstallChaincode([]byte("cc-package"))
Expect(err).To(MatchError("could not build chaincode: fake-build-error"))
})

It("deletes the chaincode from disk", func() {
ef.InstallChaincode([]byte("cc-package"))
Expect(fakeCCStore.DeleteCallCount()).To(Equal(1))
Expect(fakeCCStore.DeleteArgsForCall(0)).To(Equal("fake-hash"))
})
})

When("the chaincode is already being built", func() {
Expand Down
73 changes: 73 additions & 0 deletions core/chaincode/lifecycle/mock/chaincode_store.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions core/chaincode/persistence/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@ func (s *Store) Load(packageID string) ([]byte, error) {
return ccInstallPkg, nil
}

// Delete deletes a persisted chaincode. Note, there is no locking,
// so this should only be performed if the chaincode has already
// been marked built.
func (s *Store) Delete(packageID string) error {
ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID))
return s.ReadWriter.Remove(ccInstallPkgPath)
}

// CodePackageNotFoundErr is the error returned when a code package cannot
// be found in the persistence store
type CodePackageNotFoundErr struct {
Expand Down
34 changes: 34 additions & 0 deletions core/chaincode/persistence/persistence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,40 @@ var _ = Describe("Persistence", func() {
})
})

Describe("Delete", func() {
var (
mockReadWriter *mock.IOReadWriter
store *persistence.Store
)

BeforeEach(func() {
mockReadWriter = &mock.IOReadWriter{}
store = &persistence.Store{
ReadWriter: mockReadWriter,
Path: "foo",
}
})

It("removes the chaincode from the filesystem", func() {
err := store.Delete("hash")
Expect(err).NotTo(HaveOccurred())

Expect(mockReadWriter.RemoveCallCount()).To(Equal(1))
Expect(mockReadWriter.RemoveArgsForCall(0)).To(Equal("foo/hash.tar.gz"))
})

When("remove returns an error", func() {
BeforeEach(func() {
mockReadWriter.RemoveReturns(fmt.Errorf("fake-remove-error"))
})

It("returns the error", func() {
err := store.Delete("hash")
Expect(err).To(MatchError("fake-remove-error"))
})
})
})

Describe("Load", func() {
var (
mockReadWriter *mock.IOReadWriter
Expand Down

0 comments on commit 8ff17e1

Please sign in to comment.