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

     1  package gc_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/docker/distribution/registry/storage/driver/factory"
    12  	_ "github.com/docker/distribution/registry/storage/driver/s3-aws"
    13  	guuid "github.com/gofrs/uuid"
    14  	. "github.com/smartystreets/goconvey/convey"
    15  	"gopkg.in/resty.v1"
    16  
    17  	"zotregistry.dev/zot/pkg/api/config"
    18  	"zotregistry.dev/zot/pkg/extensions/monitoring"
    19  	zlog "zotregistry.dev/zot/pkg/log"
    20  	"zotregistry.dev/zot/pkg/meta"
    21  	"zotregistry.dev/zot/pkg/meta/boltdb"
    22  	"zotregistry.dev/zot/pkg/meta/dynamodb"
    23  	mTypes "zotregistry.dev/zot/pkg/meta/types"
    24  	"zotregistry.dev/zot/pkg/storage"
    25  	storageConstants "zotregistry.dev/zot/pkg/storage/constants"
    26  	"zotregistry.dev/zot/pkg/storage/gc"
    27  	"zotregistry.dev/zot/pkg/storage/local"
    28  	"zotregistry.dev/zot/pkg/storage/s3"
    29  	storageTypes "zotregistry.dev/zot/pkg/storage/types"
    30  	. "zotregistry.dev/zot/pkg/test/image-utils"
    31  	tskip "zotregistry.dev/zot/pkg/test/skip"
    32  )
    33  
    34  const (
    35  	region = "us-east-2"
    36  )
    37  
    38  //nolint:gochecknoglobals
    39  var testCases = []struct {
    40  	testCaseName string
    41  	storageType  string
    42  }{
    43  	{
    44  		testCaseName: "S3APIs",
    45  		storageType:  storageConstants.S3StorageDriverName,
    46  	},
    47  	{
    48  		testCaseName: "LocalAPIs",
    49  		storageType:  storageConstants.LocalStorageDriverName,
    50  	},
    51  }
    52  
    53  func TestGarbageCollectAndRetention(t *testing.T) {
    54  	log := zlog.NewLogger("info", "/dev/null")
    55  	audit := zlog.NewAuditLogger("debug", "/dev/null")
    56  
    57  	metrics := monitoring.NewMetricsServer(false, log)
    58  
    59  	trueVal := true
    60  
    61  	for _, testcase := range testCases {
    62  		testcase := testcase
    63  		t.Run(testcase.testCaseName, func(t *testing.T) {
    64  			var imgStore storageTypes.ImageStore
    65  
    66  			var metaDB mTypes.MetaDB
    67  
    68  			if testcase.storageType == storageConstants.S3StorageDriverName {
    69  				tskip.SkipDynamo(t)
    70  				tskip.SkipS3(t)
    71  
    72  				uuid, err := guuid.NewV4()
    73  				if err != nil {
    74  					panic(err)
    75  				}
    76  
    77  				rootDir := path.Join("/oci-repo-test", uuid.String())
    78  				cacheDir := t.TempDir()
    79  
    80  				bucket := "zot-storage-test"
    81  
    82  				storageDriverParams := map[string]interface{}{
    83  					"rootDir":        rootDir,
    84  					"name":           "s3",
    85  					"region":         region,
    86  					"bucket":         bucket,
    87  					"regionendpoint": os.Getenv("S3MOCK_ENDPOINT"),
    88  					"accesskey":      "minioadmin",
    89  					"secretkey":      "minioadmin",
    90  					"secure":         false,
    91  					"skipverify":     false,
    92  				}
    93  
    94  				storeName := fmt.Sprintf("%v", storageDriverParams["name"])
    95  
    96  				store, err := factory.Create(storeName, storageDriverParams)
    97  				if err != nil {
    98  					panic(err)
    99  				}
   100  
   101  				defer store.Delete(context.Background(), rootDir) //nolint: errcheck
   102  
   103  				// create bucket if it doesn't exists
   104  				_, err = resty.R().Put("http://" + os.Getenv("S3MOCK_ENDPOINT") + "/" + bucket)
   105  				if err != nil {
   106  					panic(err)
   107  				}
   108  
   109  				uuid, err = guuid.NewV4()
   110  				if err != nil {
   111  					panic(err)
   112  				}
   113  
   114  				params := dynamodb.DBDriverParameters{ //nolint:contextcheck
   115  					Endpoint:               os.Getenv("DYNAMODBMOCK_ENDPOINT"),
   116  					Region:                 region,
   117  					RepoMetaTablename:      "repo" + uuid.String(),
   118  					RepoBlobsInfoTablename: "repoblobsinfo" + uuid.String(),
   119  					ImageMetaTablename:     "imagemeta" + uuid.String(),
   120  					UserDataTablename:      "user" + uuid.String(),
   121  					APIKeyTablename:        "apiKey" + uuid.String(),
   122  					VersionTablename:       "version" + uuid.String(),
   123  				}
   124  
   125  				client, err := dynamodb.GetDynamoClient(params)
   126  				if err != nil {
   127  					panic(err)
   128  				}
   129  
   130  				metaDB, err = dynamodb.New(client, params, log)
   131  				if err != nil {
   132  					panic(err)
   133  				}
   134  
   135  				imgStore = s3.NewImageStore(rootDir, cacheDir, true, false, log, metrics, nil, store, nil)
   136  			} else {
   137  				// Create temporary directory
   138  				rootDir := t.TempDir()
   139  
   140  				// Create ImageStore
   141  				imgStore = local.NewImageStore(rootDir, false, false, log, metrics, nil, nil)
   142  
   143  				// init metaDB
   144  				params := boltdb.DBParameters{
   145  					RootDir: rootDir,
   146  				}
   147  
   148  				boltDriver, err := boltdb.GetBoltDriver(params)
   149  				if err != nil {
   150  					panic(err)
   151  				}
   152  
   153  				metaDB, err = boltdb.New(boltDriver, log)
   154  				if err != nil {
   155  					panic(err)
   156  				}
   157  			}
   158  
   159  			storeController := storage.StoreController{}
   160  			storeController.DefaultStore = imgStore
   161  
   162  			ctx := context.Background()
   163  
   164  			Convey("setup gc images", t, func() {
   165  				// for gc testing
   166  				// basic images
   167  				gcTest1 := CreateRandomImage()
   168  				err := WriteImageToFileSystem(gcTest1, "gc-test1", "0.0.1", storeController)
   169  				So(err, ShouldBeNil)
   170  
   171  				// also add same image(same digest) with another tag
   172  				err = WriteImageToFileSystem(gcTest1, "gc-test1", "0.0.2", storeController)
   173  				So(err, ShouldBeNil)
   174  
   175  				gcTest2 := CreateRandomImage()
   176  				err = WriteImageToFileSystem(gcTest2, "gc-test2", "0.0.1", storeController)
   177  				So(err, ShouldBeNil)
   178  
   179  				gcTest3 := CreateRandomImage()
   180  				err = WriteImageToFileSystem(gcTest3, "gc-test3", "0.0.1", storeController)
   181  				So(err, ShouldBeNil)
   182  
   183  				// referrers
   184  				ref1 := CreateRandomImageWith().Subject(gcTest1.DescriptorRef()).Build()
   185  				err = WriteImageToFileSystem(ref1, "gc-test1", ref1.DigestStr(), storeController)
   186  				So(err, ShouldBeNil)
   187  
   188  				ref2 := CreateRandomImageWith().Subject(gcTest2.DescriptorRef()).Build()
   189  				err = WriteImageToFileSystem(ref2, "gc-test2", ref2.DigestStr(), storeController)
   190  				So(err, ShouldBeNil)
   191  
   192  				ref3 := CreateRandomImageWith().Subject(gcTest3.DescriptorRef()).Build()
   193  				err = WriteImageToFileSystem(ref3, "gc-test3", ref3.DigestStr(), storeController)
   194  				So(err, ShouldBeNil)
   195  
   196  				// referrers pointing to referrers
   197  				refOfRef1 := CreateRandomImageWith().Subject(ref1.DescriptorRef()).Build()
   198  				err = WriteImageToFileSystem(refOfRef1, "gc-test1", refOfRef1.DigestStr(), storeController)
   199  				So(err, ShouldBeNil)
   200  
   201  				refOfRef2 := CreateRandomImageWith().Subject(ref2.DescriptorRef()).Build()
   202  				err = WriteImageToFileSystem(refOfRef2, "gc-test2", refOfRef2.DigestStr(), storeController)
   203  				So(err, ShouldBeNil)
   204  
   205  				refOfRef3 := CreateRandomImageWith().Subject(ref3.DescriptorRef()).Build()
   206  				err = WriteImageToFileSystem(refOfRef3, "gc-test3", refOfRef3.DigestStr(), storeController)
   207  				So(err, ShouldBeNil)
   208  
   209  				// untagged images
   210  				gcUntagged1 := CreateRandomImage()
   211  				err = WriteImageToFileSystem(gcUntagged1, "gc-test1", gcUntagged1.DigestStr(), storeController)
   212  				So(err, ShouldBeNil)
   213  
   214  				gcUntagged2 := CreateRandomImage()
   215  				err = WriteImageToFileSystem(gcUntagged2, "gc-test2", gcUntagged2.DigestStr(), storeController)
   216  				So(err, ShouldBeNil)
   217  
   218  				gcUntagged3 := CreateRandomImage()
   219  				err = WriteImageToFileSystem(gcUntagged3, "gc-test3", gcUntagged3.DigestStr(), storeController)
   220  				So(err, ShouldBeNil)
   221  
   222  				// for image retention testing
   223  				// old images
   224  				gcOld1 := CreateRandomImage()
   225  				err = WriteImageToFileSystem(gcOld1, "retention", "0.0.1", storeController)
   226  				So(err, ShouldBeNil)
   227  
   228  				gcOld2 := CreateRandomImage()
   229  				err = WriteImageToFileSystem(gcOld2, "retention", "0.0.2", storeController)
   230  				So(err, ShouldBeNil)
   231  
   232  				gcOld3 := CreateRandomImage()
   233  				err = WriteImageToFileSystem(gcOld3, "retention", "0.0.3", storeController)
   234  				So(err, ShouldBeNil)
   235  
   236  				// new images
   237  				gcNew1 := CreateRandomImage()
   238  				err = WriteImageToFileSystem(gcNew1, "retention", "0.0.4", storeController)
   239  				So(err, ShouldBeNil)
   240  
   241  				gcNew2 := CreateRandomImage()
   242  				err = WriteImageToFileSystem(gcNew2, "retention", "0.0.5", storeController)
   243  				So(err, ShouldBeNil)
   244  
   245  				gcNew3 := CreateRandomImage()
   246  				err = WriteImageToFileSystem(gcNew3, "retention", "0.0.6", storeController)
   247  				So(err, ShouldBeNil)
   248  
   249  				err = meta.ParseStorage(metaDB, storeController, log) //nolint: contextcheck
   250  				So(err, ShouldBeNil)
   251  
   252  				retentionMeta, err := metaDB.GetRepoMeta(ctx, "retention")
   253  				So(err, ShouldBeNil)
   254  
   255  				// update timestamps for image retention
   256  				gcOld1Stats := retentionMeta.Statistics[gcOld1.DigestStr()]
   257  				gcOld1Stats.PushTimestamp = time.Now().Add(-10 * 24 * time.Hour)
   258  				gcOld1Stats.LastPullTimestamp = time.Now().Add(-10 * 24 * time.Hour)
   259  
   260  				gcOld2Stats := retentionMeta.Statistics[gcOld2.DigestStr()]
   261  				gcOld2Stats.PushTimestamp = time.Now().Add(-11 * 24 * time.Hour)
   262  				gcOld2Stats.LastPullTimestamp = time.Now().Add(-11 * 24 * time.Hour)
   263  
   264  				gcOld3Stats := retentionMeta.Statistics[gcOld3.DigestStr()]
   265  				gcOld3Stats.PushTimestamp = time.Now().Add(-12 * 24 * time.Hour)
   266  				gcOld3Stats.LastPullTimestamp = time.Now().Add(-12 * 24 * time.Hour)
   267  
   268  				gcNew1Stats := retentionMeta.Statistics[gcNew1.DigestStr()]
   269  				gcNew1Stats.PushTimestamp = time.Now().Add(-1 * 24 * time.Hour)
   270  				gcNew1Stats.LastPullTimestamp = time.Now().Add(-1 * 24 * time.Hour)
   271  
   272  				gcNew2Stats := retentionMeta.Statistics[gcNew2.DigestStr()]
   273  				gcNew2Stats.PushTimestamp = time.Now().Add(-2 * 24 * time.Hour)
   274  				gcNew2Stats.LastPullTimestamp = time.Now().Add(-2 * 24 * time.Hour)
   275  
   276  				gcNew3Stats := retentionMeta.Statistics[gcNew3.DigestStr()]
   277  				gcNew3Stats.PushTimestamp = time.Now().Add(-3 * 24 * time.Hour)
   278  				gcNew3Stats.LastPullTimestamp = time.Now().Add(-2 * 24 * time.Hour)
   279  
   280  				retentionMeta.Statistics[gcOld1.DigestStr()] = gcOld1Stats
   281  				retentionMeta.Statistics[gcOld2.DigestStr()] = gcOld2Stats
   282  				retentionMeta.Statistics[gcOld3.DigestStr()] = gcOld3Stats
   283  
   284  				retentionMeta.Statistics[gcNew1.DigestStr()] = gcNew1Stats
   285  				retentionMeta.Statistics[gcNew2.DigestStr()] = gcNew2Stats
   286  				retentionMeta.Statistics[gcNew3.DigestStr()] = gcNew3Stats
   287  
   288  				// update repo meta
   289  				err = metaDB.SetRepoMeta("retention", retentionMeta)
   290  				So(err, ShouldBeNil)
   291  
   292  				Convey("should not gc anything", func() {
   293  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   294  						Delay: storageConstants.DefaultGCDelay,
   295  						ImageRetention: config.ImageRetention{
   296  							Delay: storageConstants.DefaultRetentionDelay,
   297  							Policies: []config.RetentionPolicy{
   298  								{
   299  									Repositories:    []string{"**"},
   300  									DeleteReferrers: true,
   301  									DeleteUntagged:  &trueVal,
   302  									KeepTags: []config.KeepTagsPolicy{
   303  										{},
   304  									},
   305  								},
   306  							},
   307  						},
   308  					}, audit, log)
   309  
   310  					err := gc.CleanRepo(ctx, "gc-test1")
   311  					So(err, ShouldBeNil)
   312  
   313  					err = gc.CleanRepo(ctx, "gc-test2")
   314  					So(err, ShouldBeNil)
   315  
   316  					err = gc.CleanRepo(ctx, "gc-test3")
   317  					So(err, ShouldBeNil)
   318  
   319  					err = gc.CleanRepo(ctx, "retention")
   320  					So(err, ShouldBeNil)
   321  
   322  					_, _, _, err = imgStore.GetImageManifest("gc-test1", gcTest1.DigestStr())
   323  					So(err, ShouldBeNil)
   324  
   325  					_, _, _, err = imgStore.GetImageManifest("gc-test1", gcUntagged1.DigestStr())
   326  					So(err, ShouldBeNil)
   327  
   328  					_, _, _, err = imgStore.GetImageManifest("gc-test1", ref1.DigestStr())
   329  					So(err, ShouldBeNil)
   330  
   331  					_, _, _, err = imgStore.GetImageManifest("gc-test1", refOfRef1.DigestStr())
   332  					So(err, ShouldBeNil)
   333  
   334  					_, _, _, err = imgStore.GetImageManifest("gc-test2", gcTest2.DigestStr())
   335  					So(err, ShouldBeNil)
   336  
   337  					_, _, _, err = imgStore.GetImageManifest("gc-test2", gcUntagged2.DigestStr())
   338  					So(err, ShouldBeNil)
   339  
   340  					_, _, _, err = imgStore.GetImageManifest("gc-test2", ref2.DigestStr())
   341  					So(err, ShouldBeNil)
   342  
   343  					_, _, _, err = imgStore.GetImageManifest("gc-test2", refOfRef2.DigestStr())
   344  					So(err, ShouldBeNil)
   345  
   346  					_, _, _, err = imgStore.GetImageManifest("gc-test3", gcTest3.DigestStr())
   347  					So(err, ShouldBeNil)
   348  
   349  					_, _, _, err = imgStore.GetImageManifest("gc-test3", gcUntagged3.DigestStr())
   350  					So(err, ShouldBeNil)
   351  
   352  					_, _, _, err = imgStore.GetImageManifest("gc-test3", ref3.DigestStr())
   353  					So(err, ShouldBeNil)
   354  
   355  					_, _, _, err = imgStore.GetImageManifest("gc-test3", refOfRef3.DigestStr())
   356  					So(err, ShouldBeNil)
   357  
   358  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.1")
   359  					So(err, ShouldBeNil)
   360  
   361  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.2")
   362  					So(err, ShouldBeNil)
   363  
   364  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.3")
   365  					So(err, ShouldBeNil)
   366  
   367  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.4")
   368  					So(err, ShouldBeNil)
   369  
   370  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.5")
   371  					So(err, ShouldBeNil)
   372  
   373  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.6")
   374  					So(err, ShouldBeNil)
   375  				})
   376  
   377  				Convey("gc untagged manifests", func() {
   378  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   379  						Delay: storageConstants.DefaultGCDelay,
   380  						ImageRetention: config.ImageRetention{
   381  							Delay: 1 * time.Millisecond,
   382  							Policies: []config.RetentionPolicy{
   383  								{
   384  									Repositories:    []string{"**"},
   385  									DeleteReferrers: true,
   386  									DeleteUntagged:  &trueVal,
   387  									KeepTags:        []config.KeepTagsPolicy{},
   388  								},
   389  							},
   390  						},
   391  					}, audit, log)
   392  
   393  					err := gc.CleanRepo(ctx, "gc-test1")
   394  					So(err, ShouldBeNil)
   395  
   396  					err = gc.CleanRepo(ctx, "gc-test2")
   397  					So(err, ShouldBeNil)
   398  
   399  					err = gc.CleanRepo(ctx, "gc-test3")
   400  					So(err, ShouldBeNil)
   401  
   402  					err = gc.CleanRepo(ctx, "retention")
   403  					So(err, ShouldBeNil)
   404  
   405  					_, _, _, err = imgStore.GetImageManifest("gc-test1", gcTest1.DigestStr())
   406  					So(err, ShouldBeNil)
   407  
   408  					_, _, _, err = imgStore.GetImageManifest("gc-test1", gcUntagged1.DigestStr())
   409  					So(err, ShouldNotBeNil)
   410  
   411  					_, _, _, err = imgStore.GetImageManifest("gc-test1", ref1.DigestStr())
   412  					So(err, ShouldBeNil)
   413  
   414  					_, _, _, err = imgStore.GetImageManifest("gc-test1", refOfRef1.DigestStr())
   415  					So(err, ShouldBeNil)
   416  
   417  					_, _, _, err = imgStore.GetImageManifest("gc-test2", gcTest2.DigestStr())
   418  					So(err, ShouldBeNil)
   419  
   420  					_, _, _, err = imgStore.GetImageManifest("gc-test2", gcUntagged2.DigestStr())
   421  					So(err, ShouldNotBeNil)
   422  
   423  					_, _, _, err = imgStore.GetImageManifest("gc-test2", ref2.DigestStr())
   424  					So(err, ShouldBeNil)
   425  
   426  					_, _, _, err = imgStore.GetImageManifest("gc-test2", refOfRef2.DigestStr())
   427  					So(err, ShouldBeNil)
   428  
   429  					_, _, _, err = imgStore.GetImageManifest("gc-test3", gcTest3.DigestStr())
   430  					So(err, ShouldBeNil)
   431  
   432  					_, _, _, err = imgStore.GetImageManifest("gc-test3", gcUntagged3.DigestStr())
   433  					So(err, ShouldNotBeNil)
   434  
   435  					_, _, _, err = imgStore.GetImageManifest("gc-test3", ref3.DigestStr())
   436  					So(err, ShouldBeNil)
   437  
   438  					_, _, _, err = imgStore.GetImageManifest("gc-test3", refOfRef3.DigestStr())
   439  					So(err, ShouldBeNil)
   440  				})
   441  
   442  				Convey("gc all tags, untagged, and afterwards referrers", func() {
   443  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   444  						Delay: 1 * time.Millisecond,
   445  						ImageRetention: config.ImageRetention{
   446  							Delay: 1 * time.Millisecond,
   447  							Policies: []config.RetentionPolicy{
   448  								{
   449  									Repositories:    []string{"gc-test1"},
   450  									DeleteReferrers: true,
   451  									DeleteUntagged:  &trueVal,
   452  									KeepTags: []config.KeepTagsPolicy{
   453  										{
   454  											Patterns: []string{"v1"}, // should not match any tag
   455  										},
   456  									},
   457  								},
   458  							},
   459  						},
   460  					}, audit, log)
   461  
   462  					err := gc.CleanRepo(ctx, "gc-test1")
   463  					So(err, ShouldBeNil)
   464  
   465  					_, _, _, err = imgStore.GetImageManifest("gc-test1", gcUntagged1.DigestStr())
   466  					So(err, ShouldNotBeNil)
   467  
   468  					// although we have two tags both should be deleted
   469  					_, _, _, err = imgStore.GetImageManifest("gc-test1", gcTest1.DigestStr())
   470  					So(err, ShouldNotBeNil)
   471  
   472  					_, _, _, err = imgStore.GetImageManifest("gc-test1", ref1.DigestStr())
   473  					So(err, ShouldNotBeNil)
   474  
   475  					_, _, _, err = imgStore.GetImageManifest("gc-test1", refOfRef1.DigestStr())
   476  					So(err, ShouldNotBeNil)
   477  
   478  					// now repo should get gc'ed
   479  					repos, err := imgStore.GetRepositories()
   480  					So(err, ShouldBeNil)
   481  					So(repos, ShouldNotContain, "gc-test1")
   482  					So(repos, ShouldContain, "gc-test2")
   483  					So(repos, ShouldContain, "gc-test3")
   484  					So(repos, ShouldContain, "retention")
   485  				})
   486  
   487  				Convey("gc with dry-run all tags, untagged, and afterwards referrers", func() {
   488  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   489  						Delay: 1 * time.Millisecond,
   490  						ImageRetention: config.ImageRetention{
   491  							Delay:  1 * time.Millisecond,
   492  							DryRun: true,
   493  							Policies: []config.RetentionPolicy{
   494  								{
   495  									Repositories:    []string{"gc-test1"},
   496  									DeleteReferrers: true,
   497  									DeleteUntagged:  &trueVal,
   498  									KeepTags: []config.KeepTagsPolicy{
   499  										{
   500  											Patterns: []string{"v1"}, // should not match any tag
   501  										},
   502  									},
   503  								},
   504  							},
   505  						},
   506  					}, audit, log)
   507  
   508  					err := gc.CleanRepo(ctx, "gc-test1")
   509  					So(err, ShouldBeNil)
   510  
   511  					_, _, _, err = imgStore.GetImageManifest("gc-test1", gcUntagged1.DigestStr())
   512  					So(err, ShouldBeNil)
   513  
   514  					_, _, _, err = imgStore.GetImageManifest("gc-test1", ref1.DigestStr())
   515  					So(err, ShouldBeNil)
   516  
   517  					_, _, _, err = imgStore.GetImageManifest("gc-test1", refOfRef1.DigestStr())
   518  					So(err, ShouldBeNil)
   519  
   520  					// now repo should not be gc'ed
   521  					repos, err := imgStore.GetRepositories()
   522  					So(err, ShouldBeNil)
   523  					So(repos, ShouldContain, "gc-test1")
   524  					So(repos, ShouldContain, "gc-test2")
   525  					So(repos, ShouldContain, "gc-test3")
   526  					So(repos, ShouldContain, "retention")
   527  
   528  					tags, err := imgStore.GetImageTags("gc-test1")
   529  					So(err, ShouldBeNil)
   530  					So(tags, ShouldContain, "0.0.1")
   531  					So(tags, ShouldContain, "0.0.2")
   532  				})
   533  
   534  				Convey("all tags matches for retention", func() {
   535  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   536  						Delay: storageConstants.DefaultGCDelay,
   537  						ImageRetention: config.ImageRetention{
   538  							Delay: storageConstants.DefaultRetentionDelay,
   539  							Policies: []config.RetentionPolicy{
   540  								{
   541  									Repositories:    []string{"**"},
   542  									DeleteReferrers: true,
   543  									DeleteUntagged:  &trueVal,
   544  									KeepTags: []config.KeepTagsPolicy{
   545  										{
   546  											Patterns: []string{"0.0.*"},
   547  										},
   548  									},
   549  								},
   550  							},
   551  						},
   552  					}, audit, log)
   553  
   554  					err = gc.CleanRepo(ctx, "retention")
   555  					So(err, ShouldBeNil)
   556  
   557  					_, _, _, err = imgStore.GetImageManifest("gc-test1", "0.0.1")
   558  					So(err, ShouldBeNil)
   559  
   560  					_, _, _, err = imgStore.GetImageManifest("gc-test1", "0.0.2")
   561  					So(err, ShouldBeNil)
   562  
   563  					_, _, _, err = imgStore.GetImageManifest("gc-test2", "0.0.1")
   564  					So(err, ShouldBeNil)
   565  
   566  					_, _, _, err = imgStore.GetImageManifest("gc-test3", "0.0.1")
   567  					So(err, ShouldBeNil)
   568  
   569  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.1")
   570  					So(err, ShouldBeNil)
   571  
   572  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.2")
   573  					So(err, ShouldBeNil)
   574  
   575  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.3")
   576  					So(err, ShouldBeNil)
   577  
   578  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.4")
   579  					So(err, ShouldBeNil)
   580  
   581  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.5")
   582  					So(err, ShouldBeNil)
   583  
   584  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.6")
   585  					So(err, ShouldBeNil)
   586  				})
   587  
   588  				Convey("retain new tags", func() {
   589  					sevenDays := 7 * 24 * time.Hour
   590  
   591  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   592  						Delay: storageConstants.DefaultGCDelay,
   593  						ImageRetention: config.ImageRetention{
   594  							Delay: storageConstants.DefaultRetentionDelay,
   595  							Policies: []config.RetentionPolicy{
   596  								{
   597  									Repositories:    []string{"**"},
   598  									DeleteReferrers: true,
   599  									DeleteUntagged:  &trueVal,
   600  									KeepTags: []config.KeepTagsPolicy{
   601  										{
   602  											Patterns:     []string{".*"},
   603  											PulledWithin: &sevenDays,
   604  											PushedWithin: &sevenDays,
   605  										},
   606  									},
   607  								},
   608  							},
   609  						},
   610  					}, audit, log)
   611  
   612  					err = gc.CleanRepo(ctx, "retention")
   613  					So(err, ShouldBeNil)
   614  
   615  					tags, err := imgStore.GetImageTags("retention")
   616  					So(err, ShouldBeNil)
   617  
   618  					So(tags, ShouldContain, "0.0.4")
   619  					So(tags, ShouldContain, "0.0.5")
   620  					So(tags, ShouldContain, "0.0.6")
   621  
   622  					So(tags, ShouldNotContain, "0.0.1")
   623  					So(tags, ShouldNotContain, "0.0.2")
   624  					So(tags, ShouldNotContain, "0.0.3")
   625  				})
   626  
   627  				Convey("retain 3 most recently pushed images", func() {
   628  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   629  						Delay: storageConstants.DefaultGCDelay,
   630  						ImageRetention: config.ImageRetention{
   631  							Delay: storageConstants.DefaultRetentionDelay,
   632  							Policies: []config.RetentionPolicy{
   633  								{
   634  									Repositories:    []string{"**"},
   635  									DeleteReferrers: true,
   636  									DeleteUntagged:  &trueVal,
   637  									KeepTags: []config.KeepTagsPolicy{
   638  										{
   639  											Patterns:                []string{".*"},
   640  											MostRecentlyPushedCount: 3,
   641  										},
   642  									},
   643  								},
   644  							},
   645  						},
   646  					}, audit, log)
   647  
   648  					err = gc.CleanRepo(ctx, "retention")
   649  					So(err, ShouldBeNil)
   650  
   651  					tags, err := imgStore.GetImageTags("retention")
   652  					So(err, ShouldBeNil)
   653  
   654  					So(tags, ShouldContain, "0.0.4")
   655  					So(tags, ShouldContain, "0.0.5")
   656  					So(tags, ShouldContain, "0.0.6")
   657  
   658  					So(tags, ShouldNotContain, "0.0.1")
   659  					So(tags, ShouldNotContain, "0.0.2")
   660  					So(tags, ShouldNotContain, "0.0.3")
   661  				})
   662  
   663  				Convey("retain 3 most recently pulled images", func() {
   664  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   665  						Delay: storageConstants.DefaultGCDelay,
   666  						ImageRetention: config.ImageRetention{
   667  							Delay: storageConstants.DefaultRetentionDelay,
   668  							Policies: []config.RetentionPolicy{
   669  								{
   670  									Repositories:    []string{"**"},
   671  									DeleteReferrers: true,
   672  									DeleteUntagged:  &trueVal,
   673  									KeepTags: []config.KeepTagsPolicy{
   674  										{
   675  											Patterns:                []string{".*"},
   676  											MostRecentlyPulledCount: 3,
   677  										},
   678  									},
   679  								},
   680  							},
   681  						},
   682  					}, audit, log)
   683  
   684  					err = gc.CleanRepo(ctx, "retention")
   685  					So(err, ShouldBeNil)
   686  
   687  					tags, err := imgStore.GetImageTags("retention")
   688  					So(err, ShouldBeNil)
   689  
   690  					So(tags, ShouldContain, "0.0.4")
   691  					So(tags, ShouldContain, "0.0.5")
   692  					So(tags, ShouldContain, "0.0.6")
   693  
   694  					So(tags, ShouldNotContain, "0.0.1")
   695  					So(tags, ShouldNotContain, "0.0.2")
   696  					So(tags, ShouldNotContain, "0.0.3")
   697  				})
   698  
   699  				Convey("retain 3 most recently pulled OR 4 most recently pushed images", func() {
   700  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   701  						Delay: storageConstants.DefaultGCDelay,
   702  						ImageRetention: config.ImageRetention{
   703  							Delay: storageConstants.DefaultRetentionDelay,
   704  							Policies: []config.RetentionPolicy{
   705  								{
   706  									Repositories:    []string{"**"},
   707  									DeleteReferrers: true,
   708  									DeleteUntagged:  &trueVal,
   709  									KeepTags: []config.KeepTagsPolicy{
   710  										{
   711  											Patterns:                []string{".*"},
   712  											MostRecentlyPulledCount: 3,
   713  											MostRecentlyPushedCount: 4,
   714  										},
   715  									},
   716  								},
   717  							},
   718  						},
   719  					}, audit, log)
   720  
   721  					err = gc.CleanRepo(ctx, "retention")
   722  					So(err, ShouldBeNil)
   723  
   724  					tags, err := imgStore.GetImageTags("retention")
   725  					So(err, ShouldBeNil)
   726  
   727  					So(tags, ShouldContain, "0.0.1")
   728  					So(tags, ShouldContain, "0.0.4")
   729  					So(tags, ShouldContain, "0.0.5")
   730  					So(tags, ShouldContain, "0.0.6")
   731  
   732  					So(tags, ShouldNotContain, "0.0.2")
   733  					So(tags, ShouldNotContain, "0.0.3")
   734  				})
   735  
   736  				Convey("test if first match rule logic works", func() {
   737  					twoDays := 2 * 24 * time.Hour
   738  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   739  						Delay: storageConstants.DefaultGCDelay,
   740  						ImageRetention: config.ImageRetention{
   741  							Delay: storageConstants.DefaultRetentionDelay,
   742  							Policies: []config.RetentionPolicy{
   743  								{
   744  									Repositories:    []string{"**"},
   745  									DeleteReferrers: true,
   746  									DeleteUntagged:  &trueVal,
   747  									KeepTags: []config.KeepTagsPolicy{
   748  										{
   749  											Patterns: []string{"0.0.1"},
   750  										},
   751  										{
   752  											Patterns: []string{"0.0.2"},
   753  										},
   754  										{
   755  											Patterns:     []string{".*"},
   756  											PulledWithin: &twoDays,
   757  										},
   758  									},
   759  								},
   760  							},
   761  						},
   762  					}, audit, log)
   763  
   764  					err = gc.CleanRepo(ctx, "retention")
   765  					So(err, ShouldBeNil)
   766  
   767  					tags, err := imgStore.GetImageTags("retention")
   768  					So(err, ShouldBeNil)
   769  					t.Log(tags)
   770  					So(tags, ShouldContain, "0.0.1")
   771  					So(tags, ShouldContain, "0.0.2")
   772  					So(tags, ShouldContain, "0.0.4")
   773  
   774  					So(tags, ShouldNotContain, "0.0.3")
   775  					So(tags, ShouldNotContain, "0.0.5")
   776  					So(tags, ShouldNotContain, "0.0.6")
   777  				})
   778  
   779  				Convey("gc - do not match any repo", func() {
   780  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   781  						Delay: 1 * time.Millisecond,
   782  						ImageRetention: config.ImageRetention{
   783  							Delay: 1 * time.Millisecond,
   784  							Policies: []config.RetentionPolicy{
   785  								{
   786  									Repositories:    []string{"no-match"},
   787  									DeleteReferrers: true,
   788  									DeleteUntagged:  &trueVal,
   789  								},
   790  							},
   791  						},
   792  					}, audit, log)
   793  
   794  					err := gc.CleanRepo(ctx, "gc-test1")
   795  					So(err, ShouldBeNil)
   796  
   797  					_, _, _, err = imgStore.GetImageManifest("gc-test1", gcUntagged1.DigestStr())
   798  					So(err, ShouldBeNil)
   799  
   800  					_, _, _, err = imgStore.GetImageManifest("gc-test1", ref1.DigestStr())
   801  					So(err, ShouldBeNil)
   802  
   803  					_, _, _, err = imgStore.GetImageManifest("gc-test1", refOfRef1.DigestStr())
   804  					So(err, ShouldBeNil)
   805  
   806  					repos, err := imgStore.GetRepositories()
   807  					So(err, ShouldBeNil)
   808  					So(repos, ShouldContain, "gc-test1")
   809  					So(repos, ShouldContain, "gc-test2")
   810  					So(repos, ShouldContain, "gc-test3")
   811  					So(repos, ShouldContain, "retention")
   812  				})
   813  
   814  				Convey("remove one tag because it didn't match, preserve tags without statistics in metaDB", func() {
   815  					// add new tag in retention repo which can not be found in metaDB, should be always retained
   816  					err = WriteImageToFileSystem(CreateRandomImage(), "retention", "0.0.7", storeController)
   817  					So(err, ShouldBeNil)
   818  
   819  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   820  						Delay: storageConstants.DefaultGCDelay,
   821  						ImageRetention: config.ImageRetention{
   822  							Delay: storageConstants.DefaultRetentionDelay,
   823  							Policies: []config.RetentionPolicy{
   824  								{
   825  									Repositories:    []string{"**"},
   826  									DeleteReferrers: true,
   827  									DeleteUntagged:  &trueVal,
   828  									KeepTags: []config.KeepTagsPolicy{
   829  										{
   830  											Patterns: []string{"0.0.[1-5]"},
   831  										},
   832  									},
   833  								},
   834  							},
   835  						},
   836  					}, audit, log)
   837  
   838  					err = gc.CleanRepo(ctx, "retention")
   839  					So(err, ShouldBeNil)
   840  
   841  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.1")
   842  					So(err, ShouldBeNil)
   843  
   844  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.2")
   845  					So(err, ShouldBeNil)
   846  
   847  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.3")
   848  					So(err, ShouldBeNil)
   849  
   850  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.4")
   851  					So(err, ShouldBeNil)
   852  
   853  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.5")
   854  					So(err, ShouldBeNil)
   855  
   856  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.6")
   857  					So(err, ShouldNotBeNil)
   858  
   859  					_, _, _, err = imgStore.GetImageManifest("retention", "0.0.7")
   860  					So(err, ShouldBeNil)
   861  				})
   862  
   863  				Convey("gc with context done", func() {
   864  					gc := gc.NewGarbageCollect(imgStore, metaDB, gc.Options{
   865  						Delay: storageConstants.DefaultGCDelay,
   866  						ImageRetention: config.ImageRetention{
   867  							Delay: 1 * time.Millisecond,
   868  							Policies: []config.RetentionPolicy{
   869  								{
   870  									Repositories:    []string{"**"},
   871  									DeleteReferrers: true,
   872  									DeleteUntagged:  &trueVal,
   873  									KeepTags: []config.KeepTagsPolicy{
   874  										{
   875  											Patterns: []string{"0.0.*"},
   876  										},
   877  									},
   878  								},
   879  							},
   880  						},
   881  					}, audit, log)
   882  
   883  					ctx, cancel := context.WithCancel(ctx)
   884  					cancel()
   885  
   886  					err := gc.CleanRepo(ctx, "gc-test1")
   887  					So(err, ShouldNotBeNil)
   888  				})
   889  			})
   890  		})
   891  	}
   892  }