zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/storage/gc/gc_internal_test.go (about)

     1  package gc
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"os"
     9  	"path"
    10  	"testing"
    11  	"time"
    12  
    13  	godigest "github.com/opencontainers/go-digest"
    14  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    15  	. "github.com/smartystreets/goconvey/convey"
    16  
    17  	"zotregistry.dev/zot/pkg/api/config"
    18  	zcommon "zotregistry.dev/zot/pkg/common"
    19  	"zotregistry.dev/zot/pkg/extensions/monitoring"
    20  	zlog "zotregistry.dev/zot/pkg/log"
    21  	"zotregistry.dev/zot/pkg/meta/types"
    22  	"zotregistry.dev/zot/pkg/storage"
    23  	"zotregistry.dev/zot/pkg/storage/cache"
    24  	common "zotregistry.dev/zot/pkg/storage/common"
    25  	storageConstants "zotregistry.dev/zot/pkg/storage/constants"
    26  	"zotregistry.dev/zot/pkg/storage/local"
    27  	. "zotregistry.dev/zot/pkg/test/image-utils"
    28  	"zotregistry.dev/zot/pkg/test/mocks"
    29  )
    30  
    31  var (
    32  	errGC    = errors.New("gc error")
    33  	repoName = "test" //nolint: gochecknoglobals
    34  )
    35  
    36  func TestGarbageCollectManifestErrors(t *testing.T) {
    37  	Convey("Make imagestore and upload manifest", t, func(c C) {
    38  		dir := t.TempDir()
    39  
    40  		log := zlog.NewLogger("debug", "")
    41  		audit := zlog.NewAuditLogger("debug", "")
    42  
    43  		metrics := monitoring.NewMetricsServer(false, log)
    44  
    45  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
    46  			RootDir:     dir,
    47  			Name:        "cache",
    48  			UseRelPaths: true,
    49  		}, log)
    50  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
    51  
    52  		gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, Options{
    53  			Delay: storageConstants.DefaultGCDelay,
    54  			ImageRetention: config.ImageRetention{
    55  				Delay: storageConstants.DefaultRetentionDelay,
    56  				Policies: []config.RetentionPolicy{
    57  					{
    58  						Repositories:    []string{"**"},
    59  						DeleteReferrers: true,
    60  					},
    61  				},
    62  			},
    63  		}, audit, log)
    64  
    65  		Convey("trigger repo not found in addImageIndexBlobsToReferences()", func() {
    66  			err := gc.addIndexBlobsToReferences(repoName, ispec.Index{
    67  				Manifests: []ispec.Descriptor{
    68  					{
    69  						Digest:    godigest.FromString("miss"),
    70  						MediaType: ispec.MediaTypeImageIndex,
    71  					},
    72  				},
    73  			}, map[string]bool{})
    74  			So(err, ShouldNotBeNil)
    75  		})
    76  
    77  		Convey("trigger repo not found in addImageManifestBlobsToReferences()", func() {
    78  			err := gc.addIndexBlobsToReferences(repoName, ispec.Index{
    79  				Manifests: []ispec.Descriptor{
    80  					{
    81  						Digest:    godigest.FromString("miss"),
    82  						MediaType: ispec.MediaTypeImageManifest,
    83  					},
    84  				},
    85  			}, map[string]bool{})
    86  			So(err, ShouldNotBeNil)
    87  		})
    88  
    89  		content := []byte("this is a blob")
    90  		digest := godigest.FromBytes(content)
    91  		So(digest, ShouldNotBeNil)
    92  
    93  		_, blen, err := imgStore.FullBlobUpload(repoName, bytes.NewReader(content), digest)
    94  		So(err, ShouldBeNil)
    95  		So(blen, ShouldEqual, len(content))
    96  
    97  		cblob, cdigest := GetRandomImageConfig()
    98  		_, clen, err := imgStore.FullBlobUpload(repoName, bytes.NewReader(cblob), cdigest)
    99  		So(err, ShouldBeNil)
   100  		So(clen, ShouldEqual, len(cblob))
   101  
   102  		manifest := ispec.Manifest{
   103  			Config: ispec.Descriptor{
   104  				MediaType: ispec.MediaTypeImageConfig,
   105  				Digest:    cdigest,
   106  				Size:      int64(len(cblob)),
   107  			},
   108  			Layers: []ispec.Descriptor{
   109  				{
   110  					MediaType: ispec.MediaTypeImageLayer,
   111  					Digest:    digest,
   112  					Size:      int64(len(content)),
   113  				},
   114  			},
   115  		}
   116  
   117  		manifest.SchemaVersion = 2
   118  
   119  		body, err := json.Marshal(manifest)
   120  		So(err, ShouldBeNil)
   121  
   122  		manifestDigest := godigest.FromBytes(body)
   123  
   124  		_, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, body)
   125  		So(err, ShouldBeNil)
   126  
   127  		Convey("trigger GetIndex error in GetReferencedBlobs", func() {
   128  			index, err := common.GetIndex(imgStore, repoName, log)
   129  			So(err, ShouldBeNil)
   130  
   131  			err = os.Chmod(path.Join(imgStore.RootDir(), repoName), 0o000)
   132  			So(err, ShouldBeNil)
   133  
   134  			defer func() {
   135  				err := os.Chmod(path.Join(imgStore.RootDir(), repoName), 0o755)
   136  				So(err, ShouldBeNil)
   137  			}()
   138  
   139  			err = gc.addIndexBlobsToReferences(repoName, index, map[string]bool{})
   140  			So(err, ShouldNotBeNil)
   141  		})
   142  
   143  		Convey("trigger GetImageManifest error in AddIndexBlobsToReferences", func() {
   144  			index, err := common.GetIndex(imgStore, repoName, log)
   145  			So(err, ShouldBeNil)
   146  
   147  			err = os.Chmod(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", manifestDigest.Encoded()), 0o000)
   148  			So(err, ShouldBeNil)
   149  
   150  			defer func() {
   151  				err := os.Chmod(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", manifestDigest.Encoded()), 0o755)
   152  				So(err, ShouldBeNil)
   153  			}()
   154  
   155  			err = gc.addIndexBlobsToReferences(repoName, index, map[string]bool{})
   156  			So(err, ShouldNotBeNil)
   157  		})
   158  	})
   159  }
   160  
   161  func TestGarbageCollectIndexErrors(t *testing.T) {
   162  	Convey("Make imagestore and upload manifest", t, func(c C) {
   163  		dir := t.TempDir()
   164  
   165  		log := zlog.NewLogger("debug", "")
   166  		audit := zlog.NewAuditLogger("debug", "")
   167  
   168  		metrics := monitoring.NewMetricsServer(false, log)
   169  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   170  			RootDir:     dir,
   171  			Name:        "cache",
   172  			UseRelPaths: true,
   173  		}, log)
   174  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
   175  
   176  		gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, Options{
   177  			Delay: storageConstants.DefaultGCDelay,
   178  			ImageRetention: config.ImageRetention{
   179  				Delay: storageConstants.DefaultRetentionDelay,
   180  				Policies: []config.RetentionPolicy{
   181  					{
   182  						Repositories:    []string{"**"},
   183  						DeleteReferrers: true,
   184  					},
   185  				},
   186  			},
   187  		}, audit, log)
   188  
   189  		content := []byte("this is a blob")
   190  		bdgst := godigest.FromBytes(content)
   191  		So(bdgst, ShouldNotBeNil)
   192  
   193  		_, bsize, err := imgStore.FullBlobUpload(repoName, bytes.NewReader(content), bdgst)
   194  		So(err, ShouldBeNil)
   195  		So(bsize, ShouldEqual, len(content))
   196  
   197  		var index ispec.Index
   198  		index.SchemaVersion = 2
   199  		index.MediaType = ispec.MediaTypeImageIndex
   200  
   201  		var digest godigest.Digest
   202  		for i := 0; i < 4; i++ {
   203  			// upload image config blob
   204  			upload, err := imgStore.NewBlobUpload(repoName)
   205  			So(err, ShouldBeNil)
   206  			So(upload, ShouldNotBeEmpty)
   207  
   208  			cblob, cdigest := GetRandomImageConfig()
   209  			buf := bytes.NewBuffer(cblob)
   210  			buflen := buf.Len()
   211  			blob, err := imgStore.PutBlobChunkStreamed(repoName, upload, buf)
   212  			So(err, ShouldBeNil)
   213  			So(blob, ShouldEqual, buflen)
   214  
   215  			err = imgStore.FinishBlobUpload(repoName, upload, buf, cdigest)
   216  			So(err, ShouldBeNil)
   217  			So(blob, ShouldEqual, buflen)
   218  
   219  			// create a manifest
   220  			manifest := ispec.Manifest{
   221  				Config: ispec.Descriptor{
   222  					MediaType: ispec.MediaTypeImageConfig,
   223  					Digest:    cdigest,
   224  					Size:      int64(len(cblob)),
   225  				},
   226  				Layers: []ispec.Descriptor{
   227  					{
   228  						MediaType: ispec.MediaTypeImageLayer,
   229  						Digest:    bdgst,
   230  						Size:      bsize,
   231  					},
   232  				},
   233  			}
   234  			manifest.SchemaVersion = 2
   235  			content, err = json.Marshal(manifest)
   236  			So(err, ShouldBeNil)
   237  			digest = godigest.FromBytes(content)
   238  			So(digest, ShouldNotBeNil)
   239  			_, _, err = imgStore.PutImageManifest(repoName, digest.String(), ispec.MediaTypeImageManifest, content)
   240  			So(err, ShouldBeNil)
   241  
   242  			index.Manifests = append(index.Manifests, ispec.Descriptor{
   243  				Digest:    digest,
   244  				MediaType: ispec.MediaTypeImageManifest,
   245  				Size:      int64(len(content)),
   246  			})
   247  		}
   248  
   249  		// upload index image
   250  		indexContent, err := json.Marshal(index)
   251  		So(err, ShouldBeNil)
   252  		indexDigest := godigest.FromBytes(indexContent)
   253  		So(indexDigest, ShouldNotBeNil)
   254  
   255  		_, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageIndex, indexContent)
   256  		So(err, ShouldBeNil)
   257  
   258  		index, err = common.GetIndex(imgStore, repoName, log)
   259  		So(err, ShouldBeNil)
   260  
   261  		err = gc.addIndexBlobsToReferences(repoName, index, map[string]bool{})
   262  		So(err, ShouldBeNil)
   263  
   264  		Convey("trigger GetImageIndex error in GetReferencedBlobsInImageIndex", func() {
   265  			err := os.Chmod(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", indexDigest.Encoded()), 0o000)
   266  			So(err, ShouldBeNil)
   267  
   268  			defer func() {
   269  				err := os.Chmod(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", indexDigest.Encoded()), 0o755)
   270  				So(err, ShouldBeNil)
   271  			}()
   272  
   273  			err = gc.addIndexBlobsToReferences(repoName, index, map[string]bool{})
   274  			So(err, ShouldNotBeNil)
   275  		})
   276  	})
   277  }
   278  
   279  func TestGarbageCollectWithMockedImageStore(t *testing.T) {
   280  	trueVal := true
   281  
   282  	ctx := context.Background()
   283  
   284  	Convey("Cover gc error paths", t, func(c C) {
   285  		log := zlog.NewLogger("debug", "")
   286  		audit := zlog.NewAuditLogger("debug", "")
   287  
   288  		gcOptions := Options{
   289  			Delay: storageConstants.DefaultGCDelay,
   290  			ImageRetention: config.ImageRetention{
   291  				Delay: storageConstants.DefaultRetentionDelay,
   292  				Policies: []config.RetentionPolicy{
   293  					{
   294  						Repositories:    []string{"**"},
   295  						DeleteReferrers: true,
   296  					},
   297  				},
   298  			},
   299  		}
   300  
   301  		Convey("Error on GetIndex in gc.cleanRepo()", func() {
   302  			gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{
   303  				GetRepoMetaFn: func(ctx context.Context, repo string) (types.RepoMeta, error) {
   304  					return types.RepoMeta{}, errGC
   305  				},
   306  			}, gcOptions, audit, log)
   307  
   308  			err := gc.cleanRepo(ctx, repoName)
   309  			So(err, ShouldNotBeNil)
   310  		})
   311  
   312  		Convey("Error on GetIndex in gc.removeUnreferencedBlobs()", func() {
   313  			gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{
   314  				GetRepoMetaFn: func(ctx context.Context, repo string) (types.RepoMeta, error) {
   315  					return types.RepoMeta{}, errGC
   316  				},
   317  			}, gcOptions, audit, log)
   318  
   319  			err := gc.removeUnreferencedBlobs("repo", time.Hour, log)
   320  			So(err, ShouldNotBeNil)
   321  		})
   322  
   323  		Convey("Error on gc.removeManifest()", func() {
   324  			gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{
   325  				GetRepoMetaFn: func(ctx context.Context, repo string) (types.RepoMeta, error) {
   326  					return types.RepoMeta{}, errGC
   327  				},
   328  			}, gcOptions, audit, log)
   329  
   330  			_, err := gc.removeManifest("", &ispec.Index{}, ispec.DescriptorEmptyJSON, "tag", "", "")
   331  			So(err, ShouldNotBeNil)
   332  		})
   333  
   334  		Convey("Error on metaDB in gc.cleanRepo()", func() {
   335  			gcOptions := Options{
   336  				Delay: storageConstants.DefaultGCDelay,
   337  				ImageRetention: config.ImageRetention{
   338  					Delay: storageConstants.DefaultRetentionDelay,
   339  					Policies: []config.RetentionPolicy{
   340  						{
   341  							Repositories: []string{"**"},
   342  							KeepTags: []config.KeepTagsPolicy{
   343  								{
   344  									Patterns: []string{".*"},
   345  								},
   346  							},
   347  						},
   348  					},
   349  				},
   350  			}
   351  
   352  			gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{
   353  				GetRepoMetaFn: func(ctx context.Context, repo string) (types.RepoMeta, error) {
   354  					return types.RepoMeta{}, errGC
   355  				},
   356  			}, gcOptions, audit, log)
   357  
   358  			err := gc.removeTagsPerRetentionPolicy(ctx, "name", &ispec.Index{})
   359  			So(err, ShouldNotBeNil)
   360  		})
   361  
   362  		Convey("Error on context done in removeTags...", func() {
   363  			gcOptions := Options{
   364  				Delay: storageConstants.DefaultGCDelay,
   365  				ImageRetention: config.ImageRetention{
   366  					Delay: storageConstants.DefaultRetentionDelay,
   367  					Policies: []config.RetentionPolicy{
   368  						{
   369  							Repositories: []string{"**"},
   370  							KeepTags: []config.KeepTagsPolicy{
   371  								{
   372  									Patterns: []string{".*"},
   373  								},
   374  							},
   375  						},
   376  					},
   377  				},
   378  			}
   379  
   380  			gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{}, gcOptions, audit, log)
   381  
   382  			ctx, cancel := context.WithCancel(ctx)
   383  			cancel()
   384  
   385  			err := gc.removeTagsPerRetentionPolicy(ctx, "name", &ispec.Index{
   386  				Manifests: []ispec.Descriptor{
   387  					{
   388  						MediaType: ispec.MediaTypeImageManifest,
   389  						Digest:    godigest.FromBytes([]byte("digest")),
   390  					},
   391  				},
   392  			})
   393  			So(err, ShouldNotBeNil)
   394  		})
   395  
   396  		Convey("Error on PutIndexContent in gc.cleanRepo()", func() {
   397  			returnedIndexJSON := ispec.Index{}
   398  
   399  			returnedIndexJSONBuf, err := json.Marshal(returnedIndexJSON)
   400  			So(err, ShouldBeNil)
   401  
   402  			imgStore := mocks.MockedImageStore{
   403  				PutIndexContentFn: func(repo string, index ispec.Index) error {
   404  					return errGC
   405  				},
   406  				GetIndexContentFn: func(repo string) ([]byte, error) {
   407  					return returnedIndexJSONBuf, nil
   408  				},
   409  			}
   410  
   411  			gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log)
   412  
   413  			err = gc.cleanRepo(ctx, repoName)
   414  			So(err, ShouldNotBeNil)
   415  		})
   416  
   417  		Convey("Error on gc.cleanBlobs() in gc.cleanRepo()", func() {
   418  			returnedIndexJSON := ispec.Index{}
   419  
   420  			returnedIndexJSONBuf, err := json.Marshal(returnedIndexJSON)
   421  			So(err, ShouldBeNil)
   422  
   423  			imgStore := mocks.MockedImageStore{
   424  				PutIndexContentFn: func(repo string, index ispec.Index) error {
   425  					return nil
   426  				},
   427  				GetIndexContentFn: func(repo string) ([]byte, error) {
   428  					return returnedIndexJSONBuf, nil
   429  				},
   430  				GetAllBlobsFn: func(repo string) ([]string, error) {
   431  					return []string{}, errGC
   432  				},
   433  			}
   434  
   435  			gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log)
   436  
   437  			err = gc.cleanRepo(ctx, repoName)
   438  			So(err, ShouldNotBeNil)
   439  		})
   440  
   441  		Convey("False on imgStore.DirExists() in gc.cleanRepo()", func() {
   442  			imgStore := mocks.MockedImageStore{
   443  				DirExistsFn: func(d string) bool {
   444  					return false
   445  				},
   446  			}
   447  
   448  			gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log)
   449  
   450  			err := gc.cleanRepo(ctx, repoName)
   451  			So(err, ShouldNotBeNil)
   452  		})
   453  
   454  		Convey("Error on gc.identifyManifestsReferencedInIndex in gc.cleanManifests() with multiarch image", func() {
   455  			indexImageDigest := godigest.FromBytes([]byte("digest"))
   456  
   457  			returnedIndexImage := ispec.Index{
   458  				Subject: &ispec.DescriptorEmptyJSON,
   459  				Manifests: []ispec.Descriptor{
   460  					{
   461  						MediaType: ispec.MediaTypeImageIndex,
   462  						Digest:    godigest.FromBytes([]byte("digest2")),
   463  					},
   464  				},
   465  			}
   466  
   467  			returnedIndexImageBuf, err := json.Marshal(returnedIndexImage)
   468  			So(err, ShouldBeNil)
   469  
   470  			imgStore := mocks.MockedImageStore{
   471  				GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
   472  					if digest == indexImageDigest {
   473  						return returnedIndexImageBuf, nil
   474  					} else {
   475  						return nil, errGC
   476  					}
   477  				},
   478  			}
   479  
   480  			gcOptions.ImageRetention = config.ImageRetention{
   481  				Policies: []config.RetentionPolicy{
   482  					{
   483  						Repositories:   []string{"**"},
   484  						DeleteUntagged: &trueVal,
   485  					},
   486  				},
   487  			}
   488  			gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log)
   489  
   490  			err = gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{
   491  				Manifests: []ispec.Descriptor{
   492  					{
   493  						MediaType: ispec.MediaTypeImageIndex,
   494  						Digest:    indexImageDigest,
   495  					},
   496  				},
   497  			})
   498  			So(err, ShouldNotBeNil)
   499  		})
   500  
   501  		Convey("Error on gc.identifyManifestsReferencedInIndex in gc.cleanManifests() with image", func() {
   502  			imgStore := mocks.MockedImageStore{
   503  				GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
   504  					return nil, errGC
   505  				},
   506  			}
   507  
   508  			gcOptions.ImageRetention = config.ImageRetention{
   509  				Policies: []config.RetentionPolicy{
   510  					{
   511  						Repositories:   []string{"**"},
   512  						DeleteUntagged: &trueVal,
   513  					},
   514  				},
   515  			}
   516  
   517  			gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log)
   518  
   519  			err := gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{
   520  				Manifests: []ispec.Descriptor{
   521  					{
   522  						MediaType: ispec.MediaTypeImageManifest,
   523  						Digest:    godigest.FromBytes([]byte("digest")),
   524  					},
   525  				},
   526  			})
   527  			So(err, ShouldNotBeNil)
   528  		})
   529  
   530  		Convey("Error on context done in removeManifests...", func() {
   531  			imgStore := mocks.MockedImageStore{}
   532  
   533  			gcOptions.ImageRetention = config.ImageRetention{
   534  				Policies: []config.RetentionPolicy{
   535  					{
   536  						Repositories:   []string{"**"},
   537  						DeleteUntagged: &trueVal,
   538  					},
   539  				},
   540  			}
   541  
   542  			gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log)
   543  
   544  			ctx, cancel := context.WithCancel(ctx)
   545  			cancel()
   546  
   547  			err := gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{
   548  				Manifests: []ispec.Descriptor{
   549  					{
   550  						MediaType: ispec.MediaTypeImageManifest,
   551  						Digest:    godigest.FromBytes([]byte("digest")),
   552  					},
   553  				},
   554  			})
   555  			So(err, ShouldNotBeNil)
   556  		})
   557  
   558  		Convey("Error on gc.gcManifest() in gc.cleanManifests() with image", func() {
   559  			returnedImage := ispec.Manifest{
   560  				MediaType: ispec.MediaTypeImageManifest,
   561  			}
   562  
   563  			returnedImageBuf, err := json.Marshal(returnedImage)
   564  			So(err, ShouldBeNil)
   565  
   566  			imgStore := mocks.MockedImageStore{
   567  				GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
   568  					return returnedImageBuf, nil
   569  				},
   570  			}
   571  
   572  			metaDB := mocks.MetaDBMock{
   573  				RemoveRepoReferenceFn: func(repo, reference string, manifestDigest godigest.Digest) error {
   574  					return errGC
   575  				},
   576  			}
   577  
   578  			gcOptions.ImageRetention = config.ImageRetention{
   579  				Policies: []config.RetentionPolicy{
   580  					{
   581  						Repositories:   []string{"**"},
   582  						DeleteUntagged: &trueVal,
   583  					},
   584  				},
   585  			}
   586  			gc := NewGarbageCollect(imgStore, metaDB, gcOptions, audit, log)
   587  
   588  			err = gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{
   589  				Manifests: []ispec.Descriptor{
   590  					{
   591  						MediaType: ispec.MediaTypeImageManifest,
   592  						Digest:    godigest.FromBytes([]byte("digest")),
   593  					},
   594  				},
   595  			})
   596  			So(err, ShouldNotBeNil)
   597  		})
   598  		Convey("Error on gc.gcManifest() in gc.cleanManifests() with signature", func() {
   599  			returnedImage := ispec.Manifest{
   600  				MediaType:    ispec.MediaTypeImageManifest,
   601  				ArtifactType: zcommon.NotationSignature,
   602  			}
   603  
   604  			returnedImageBuf, err := json.Marshal(returnedImage)
   605  			So(err, ShouldBeNil)
   606  
   607  			imgStore := mocks.MockedImageStore{
   608  				GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
   609  					return returnedImageBuf, nil
   610  				},
   611  			}
   612  
   613  			metaDB := mocks.MetaDBMock{
   614  				DeleteSignatureFn: func(repo string, signedManifestDigest godigest.Digest, sm types.SignatureMetadata) error {
   615  					return errGC
   616  				},
   617  			}
   618  
   619  			gcOptions.ImageRetention = config.ImageRetention{}
   620  			gc := NewGarbageCollect(imgStore, metaDB, gcOptions, audit, log)
   621  
   622  			desc := ispec.Descriptor{
   623  				MediaType: ispec.MediaTypeImageManifest,
   624  				Digest:    godigest.FromBytes([]byte("digest")),
   625  			}
   626  
   627  			index := &ispec.Index{
   628  				Manifests: []ispec.Descriptor{desc},
   629  			}
   630  			_, err = gc.removeManifest(repoName, index, desc, desc.Digest.String(), storage.NotationType,
   631  				godigest.FromBytes([]byte("digest2")))
   632  
   633  			So(err, ShouldNotBeNil)
   634  		})
   635  
   636  		Convey("Error on gc.gcReferrer() in gc.cleanManifests() with image index", func() {
   637  			manifestDesc := ispec.Descriptor{
   638  				MediaType: ispec.MediaTypeImageIndex,
   639  				Digest:    godigest.FromBytes([]byte("digest")),
   640  			}
   641  
   642  			returnedIndexImage := ispec.Index{
   643  				MediaType: ispec.MediaTypeImageIndex,
   644  				Subject: &ispec.Descriptor{
   645  					Digest: godigest.FromBytes([]byte("digest2")),
   646  				},
   647  				Manifests: []ispec.Descriptor{
   648  					manifestDesc,
   649  				},
   650  			}
   651  
   652  			returnedIndexImageBuf, err := json.Marshal(returnedIndexImage)
   653  			So(err, ShouldBeNil)
   654  
   655  			imgStore := mocks.MockedImageStore{
   656  				GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
   657  					return returnedIndexImageBuf, nil
   658  				},
   659  				StatBlobFn: func(repo string, digest godigest.Digest) (bool, int64, time.Time, error) {
   660  					return false, -1, time.Time{}, errGC
   661  				},
   662  			}
   663  
   664  			gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log)
   665  
   666  			err = gc.removeManifestsPerRepoPolicy(ctx, repoName, &returnedIndexImage)
   667  			So(err, ShouldNotBeNil)
   668  		})
   669  
   670  		Convey("Error on gc.gcReferrer() in gc.cleanManifests() with image", func() {
   671  			manifestDesc := ispec.Descriptor{
   672  				MediaType: ispec.MediaTypeImageManifest,
   673  				Digest:    godigest.FromBytes([]byte("digest")),
   674  			}
   675  
   676  			returnedImage := ispec.Manifest{
   677  				Subject: &ispec.Descriptor{
   678  					Digest: godigest.FromBytes([]byte("digest2")),
   679  				},
   680  				MediaType: ispec.MediaTypeImageManifest,
   681  			}
   682  
   683  			returnedImageBuf, err := json.Marshal(returnedImage)
   684  			So(err, ShouldBeNil)
   685  
   686  			imgStore := mocks.MockedImageStore{
   687  				GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
   688  					return returnedImageBuf, nil
   689  				},
   690  				StatBlobFn: func(repo string, digest godigest.Digest) (bool, int64, time.Time, error) {
   691  					return false, -1, time.Time{}, errGC
   692  				},
   693  			}
   694  
   695  			gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log)
   696  
   697  			err = gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{
   698  				Manifests: []ispec.Descriptor{
   699  					manifestDesc,
   700  				},
   701  			})
   702  			So(err, ShouldNotBeNil)
   703  		})
   704  	})
   705  }