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

     1  package local_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/rand"
     7  	_ "crypto/sha256"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"io/fs"
    13  	"math/big"
    14  	"os"
    15  	"path"
    16  	"strings"
    17  	"syscall"
    18  	"testing"
    19  	"time"
    20  
    21  	godigest "github.com/opencontainers/go-digest"
    22  	imeta "github.com/opencontainers/image-spec/specs-go"
    23  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    24  	"github.com/rs/zerolog"
    25  	. "github.com/smartystreets/goconvey/convey"
    26  
    27  	zerr "zotregistry.dev/zot/errors"
    28  	"zotregistry.dev/zot/pkg/api/config"
    29  	"zotregistry.dev/zot/pkg/common"
    30  	"zotregistry.dev/zot/pkg/extensions/monitoring"
    31  	zlog "zotregistry.dev/zot/pkg/log"
    32  	"zotregistry.dev/zot/pkg/scheduler"
    33  	"zotregistry.dev/zot/pkg/storage"
    34  	"zotregistry.dev/zot/pkg/storage/cache"
    35  	storageConstants "zotregistry.dev/zot/pkg/storage/constants"
    36  	"zotregistry.dev/zot/pkg/storage/gc"
    37  	"zotregistry.dev/zot/pkg/storage/local"
    38  	storageTypes "zotregistry.dev/zot/pkg/storage/types"
    39  	. "zotregistry.dev/zot/pkg/test/image-utils"
    40  	"zotregistry.dev/zot/pkg/test/mocks"
    41  	"zotregistry.dev/zot/pkg/test/signature"
    42  )
    43  
    44  const (
    45  	tag      = "1.0"
    46  	repoName = "test"
    47  )
    48  
    49  var trueVal bool = true //nolint: gochecknoglobals
    50  
    51  var DeleteReferrers = config.ImageRetention{ //nolint: gochecknoglobals
    52  	Delay: storageConstants.DefaultRetentionDelay,
    53  	Policies: []config.RetentionPolicy{
    54  		{
    55  			Repositories:    []string{"**"},
    56  			DeleteReferrers: true,
    57  			DeleteUntagged:  &trueVal,
    58  		},
    59  	},
    60  }
    61  
    62  var errCache = errors.New("new cache error")
    63  
    64  func runAndGetScheduler() *scheduler.Scheduler {
    65  	log := zlog.Logger{}
    66  	metrics := monitoring.NewMetricsServer(true, log)
    67  	taskScheduler := scheduler.NewScheduler(config.New(), metrics, log)
    68  	taskScheduler.RateLimit = 50 * time.Millisecond
    69  
    70  	taskScheduler.RunScheduler()
    71  
    72  	return taskScheduler
    73  }
    74  
    75  func TestStorageFSAPIs(t *testing.T) {
    76  	dir := t.TempDir()
    77  
    78  	log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
    79  	metrics := monitoring.NewMetricsServer(false, log)
    80  	cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
    81  		RootDir:     dir,
    82  		Name:        "cache",
    83  		UseRelPaths: true,
    84  	}, log)
    85  
    86  	imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
    87  
    88  	Convey("Repo layout", t, func(c C) {
    89  		Convey("Bad image manifest", func() {
    90  			upload, err := imgStore.NewBlobUpload(repoName)
    91  			So(err, ShouldBeNil)
    92  			So(upload, ShouldNotBeEmpty)
    93  
    94  			content := []byte("test-data1")
    95  			buf := bytes.NewBuffer(content)
    96  			buflen := buf.Len()
    97  			digest := godigest.FromBytes(content)
    98  
    99  			blob, err := imgStore.PutBlobChunk(repoName, upload, 0, int64(buflen), buf)
   100  			So(err, ShouldBeNil)
   101  			So(blob, ShouldEqual, buflen)
   102  
   103  			err = imgStore.FinishBlobUpload(repoName, upload, buf, digest)
   104  			So(err, ShouldBeNil)
   105  
   106  			annotationsMap := make(map[string]string)
   107  			annotationsMap[ispec.AnnotationRefName] = tag
   108  
   109  			cblob, cdigest := GetRandomImageConfig()
   110  			_, clen, err := imgStore.FullBlobUpload(repoName, bytes.NewReader(cblob), cdigest)
   111  			So(err, ShouldBeNil)
   112  			So(clen, ShouldEqual, len(cblob))
   113  			hasBlob, _, err := imgStore.CheckBlob(repoName, cdigest)
   114  			So(err, ShouldBeNil)
   115  			So(hasBlob, ShouldEqual, true)
   116  
   117  			manifest := ispec.Manifest{
   118  				Config: ispec.Descriptor{
   119  					MediaType: "application/vnd.oci.image.config.v1+json",
   120  					Digest:    cdigest,
   121  					Size:      int64(len(cblob)),
   122  				},
   123  				Layers: []ispec.Descriptor{
   124  					{
   125  						MediaType: "application/vnd.oci.image.layer.v1.tar",
   126  						Digest:    digest,
   127  						Size:      int64(buflen),
   128  					},
   129  				},
   130  				Annotations: annotationsMap,
   131  			}
   132  
   133  			manifest.SchemaVersion = 2
   134  			manifestBuf, err := json.Marshal(manifest)
   135  			So(err, ShouldBeNil)
   136  			digest = godigest.FromBytes(manifestBuf)
   137  
   138  			err = os.Chmod(path.Join(imgStore.RootDir(), repoName, "index.json"), 0o000)
   139  			if err != nil {
   140  				panic(err)
   141  			}
   142  
   143  			_, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, manifestBuf)
   144  			So(err, ShouldNotBeNil)
   145  
   146  			err = os.Chmod(path.Join(imgStore.RootDir(), repoName, "index.json"), 0o755)
   147  			if err != nil {
   148  				panic(err)
   149  			}
   150  
   151  			_, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, manifestBuf)
   152  			So(err, ShouldBeNil)
   153  
   154  			manifestPath := path.Join(imgStore.RootDir(), repoName, "blobs", digest.Algorithm().String(), digest.Encoded())
   155  
   156  			err = os.Chmod(manifestPath, 0o000)
   157  			if err != nil {
   158  				panic(err)
   159  			}
   160  
   161  			_, _, _, err = imgStore.GetImageManifest(repoName, digest.String())
   162  			So(err, ShouldNotBeNil)
   163  
   164  			err = os.Remove(manifestPath)
   165  			if err != nil {
   166  				panic(err)
   167  			}
   168  
   169  			_, _, _, err = imgStore.GetImageManifest(repoName, digest.String())
   170  			So(err, ShouldNotBeNil)
   171  
   172  			err = os.Chmod(path.Join(imgStore.RootDir(), repoName), 0o000)
   173  			if err != nil {
   174  				panic(err)
   175  			}
   176  
   177  			_, _, err = imgStore.PutImageManifest(repoName, "2.0", ispec.MediaTypeImageManifest, manifestBuf)
   178  			So(err, ShouldNotBeNil)
   179  			err = os.Chmod(path.Join(imgStore.RootDir(), repoName), 0o755)
   180  			if err != nil {
   181  				panic(err)
   182  			}
   183  
   184  			// invalid DeleteImageManifest
   185  			indexPath := path.Join(imgStore.RootDir(), repoName, "index.json")
   186  			err = os.Chmod(indexPath, 0o000)
   187  			if err != nil {
   188  				panic(err)
   189  			}
   190  
   191  			err = imgStore.DeleteImageManifest(repoName, digest.String(), false)
   192  			So(err, ShouldNotBeNil)
   193  
   194  			err = os.RemoveAll(path.Join(imgStore.RootDir(), repoName))
   195  			if err != nil {
   196  				panic(err)
   197  			}
   198  		})
   199  	})
   200  }
   201  
   202  func FuzzNewBlobUpload(f *testing.F) {
   203  	f.Fuzz(func(t *testing.T, data string) {
   204  		dir := t.TempDir()
   205  		defer os.RemoveAll(dir)
   206  		t.Logf("Input argument is %s", data)
   207  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
   208  		metrics := monitoring.NewMetricsServer(false, log)
   209  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   210  			RootDir:     dir,
   211  			Name:        "cache",
   212  			UseRelPaths: true,
   213  		}, log)
   214  
   215  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
   216  
   217  		_, err := imgStore.NewBlobUpload(data)
   218  		if err != nil {
   219  			if isKnownErr(err) {
   220  				return
   221  			}
   222  
   223  			t.Error(err)
   224  		}
   225  	})
   226  }
   227  
   228  func FuzzPutBlobChunk(f *testing.F) {
   229  	f.Fuzz(func(t *testing.T, data string) {
   230  		dir := t.TempDir()
   231  		defer os.RemoveAll(dir)
   232  		t.Logf("Input argument is %s", data)
   233  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
   234  
   235  		metrics := monitoring.NewMetricsServer(false, log)
   236  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   237  			RootDir:     dir,
   238  			Name:        "cache",
   239  			UseRelPaths: true,
   240  		}, log)
   241  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
   242  
   243  		repoName := data
   244  		uuid, err := imgStore.NewBlobUpload(repoName)
   245  		if err != nil {
   246  			if isKnownErr(err) {
   247  				return
   248  			}
   249  
   250  			t.Error(err)
   251  		}
   252  
   253  		buf := bytes.NewBufferString(data)
   254  		buflen := buf.Len()
   255  		_, err = imgStore.PutBlobChunk(repoName, uuid, 0, int64(buflen), buf)
   256  		if err != nil {
   257  			t.Error(err)
   258  		}
   259  	})
   260  }
   261  
   262  func FuzzPutBlobChunkStreamed(f *testing.F) {
   263  	f.Fuzz(func(t *testing.T, data string) {
   264  		dir := t.TempDir()
   265  		defer os.RemoveAll(dir)
   266  		t.Logf("Input argument is %s", data)
   267  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
   268  		metrics := monitoring.NewMetricsServer(false, log)
   269  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   270  			RootDir:     dir,
   271  			Name:        "cache",
   272  			UseRelPaths: true,
   273  		}, log)
   274  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
   275  
   276  		repoName := data
   277  
   278  		uuid, err := imgStore.NewBlobUpload(repoName)
   279  		if err != nil {
   280  			if isKnownErr(err) {
   281  				return
   282  			}
   283  
   284  			t.Error(err)
   285  		}
   286  
   287  		buf := bytes.NewBufferString(data)
   288  		_, err = imgStore.PutBlobChunkStreamed(repoName, uuid, buf)
   289  		if err != nil {
   290  			t.Error(err)
   291  		}
   292  	})
   293  }
   294  
   295  func FuzzGetBlobUpload(f *testing.F) {
   296  	f.Fuzz(func(t *testing.T, data1 string, data2 string) {
   297  		dir := t.TempDir()
   298  		defer os.RemoveAll(dir)
   299  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
   300  		metrics := monitoring.NewMetricsServer(false, log)
   301  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   302  			RootDir:     dir,
   303  			Name:        "cache",
   304  			UseRelPaths: true,
   305  		}, log)
   306  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil,
   307  			cacheDriver)
   308  
   309  		_, err := imgStore.GetBlobUpload(data1, data2)
   310  		if err != nil {
   311  			if errors.Is(err, zerr.ErrUploadNotFound) || isKnownErr(err) {
   312  				return
   313  			}
   314  			t.Error(err)
   315  		}
   316  	})
   317  }
   318  
   319  func FuzzTestPutGetImageManifest(f *testing.F) {
   320  	f.Fuzz(func(t *testing.T, data []byte) {
   321  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   322  		metrics := monitoring.NewMetricsServer(false, *log)
   323  
   324  		dir := t.TempDir()
   325  		defer os.RemoveAll(dir)
   326  
   327  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   328  			RootDir:     dir,
   329  			Name:        "cache",
   330  			UseRelPaths: true,
   331  		}, *log)
   332  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   333  
   334  		cblob, cdigest := GetRandomImageConfig()
   335  
   336  		ldigest, lblob, err := newRandomBlobForFuzz(data)
   337  		if err != nil {
   338  			t.Errorf("error occurred while generating random blob, %v", err)
   339  		}
   340  
   341  		_, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(cblob), cdigest)
   342  		if err != nil {
   343  			t.Error(err)
   344  		}
   345  		_, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(lblob), ldigest)
   346  		if err != nil {
   347  			t.Error(err)
   348  		}
   349  
   350  		manifest, err := NewRandomImgManifest(data, cdigest, ldigest, cblob, lblob)
   351  		if err != nil {
   352  			t.Error(err)
   353  		}
   354  		manifestBuf, err := json.Marshal(manifest)
   355  		if err != nil {
   356  			t.Errorf("Error %v occurred while marshaling manifest", err)
   357  		}
   358  		mdigest := godigest.FromBytes(manifestBuf)
   359  		_, _, err = imgStore.PutImageManifest(repoName, mdigest.String(), ispec.MediaTypeImageManifest, manifestBuf)
   360  		if err != nil && errors.Is(err, zerr.ErrBadManifest) {
   361  			t.Errorf("the error that occurred is %v \n", err)
   362  		}
   363  		_, _, _, err = imgStore.GetImageManifest(repoName, mdigest.String())
   364  		if err != nil {
   365  			t.Errorf("the error that occurred is %v \n", err)
   366  		}
   367  	})
   368  }
   369  
   370  func FuzzTestPutDeleteImageManifest(f *testing.F) {
   371  	f.Fuzz(func(t *testing.T, data []byte) {
   372  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   373  		metrics := monitoring.NewMetricsServer(false, *log)
   374  
   375  		dir := t.TempDir()
   376  		defer os.RemoveAll(dir)
   377  
   378  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   379  			RootDir:     dir,
   380  			Name:        "cache",
   381  			UseRelPaths: true,
   382  		}, *log)
   383  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   384  
   385  		cblob, cdigest := GetRandomImageConfig()
   386  
   387  		ldigest, lblob, err := newRandomBlobForFuzz(data)
   388  		if err != nil {
   389  			t.Errorf("error occurred while generating random blob, %v", err)
   390  		}
   391  
   392  		_, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(cblob), cdigest)
   393  		if err != nil {
   394  			t.Error(err)
   395  		}
   396  
   397  		_, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(lblob), ldigest)
   398  		if err != nil {
   399  			t.Error(err)
   400  		}
   401  
   402  		manifest, err := NewRandomImgManifest(data, cdigest, ldigest, cblob, lblob)
   403  		if err != nil {
   404  			t.Error(err)
   405  		}
   406  
   407  		manifestBuf, err := json.Marshal(manifest)
   408  		if err != nil {
   409  			t.Errorf("Error %v occurred while marshaling manifest", err)
   410  		}
   411  		mdigest := godigest.FromBytes(manifestBuf)
   412  		_, _, err = imgStore.PutImageManifest(repoName, mdigest.String(), ispec.MediaTypeImageManifest, manifestBuf)
   413  		if err != nil && errors.Is(err, zerr.ErrBadManifest) {
   414  			t.Errorf("the error that occurred is %v \n", err)
   415  		}
   416  
   417  		err = imgStore.DeleteImageManifest(repoName, mdigest.String(), false)
   418  		if err != nil {
   419  			if isKnownErr(err) {
   420  				return
   421  			}
   422  			t.Errorf("the error that occurred is %v \n", err)
   423  		}
   424  	})
   425  }
   426  
   427  // no integration with PutImageManifest, just throw fuzz data.
   428  func FuzzTestDeleteImageManifest(f *testing.F) {
   429  	f.Fuzz(func(t *testing.T, data []byte) {
   430  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   431  		metrics := monitoring.NewMetricsServer(false, *log)
   432  
   433  		dir := t.TempDir()
   434  		defer os.RemoveAll(dir)
   435  
   436  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   437  			RootDir:     dir,
   438  			Name:        "cache",
   439  			UseRelPaths: true,
   440  		}, *log)
   441  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   442  
   443  		digest, _, err := newRandomBlobForFuzz(data)
   444  		if err != nil {
   445  			return
   446  		}
   447  		err = imgStore.DeleteImageManifest(string(data), digest.String(), false)
   448  		if err != nil {
   449  			if errors.Is(err, zerr.ErrRepoNotFound) || isKnownErr(err) {
   450  				return
   451  			}
   452  			t.Error(err)
   453  		}
   454  	})
   455  }
   456  
   457  func FuzzDirExists(f *testing.F) {
   458  	f.Fuzz(func(t *testing.T, data string) {
   459  		_ = common.DirExists(data)
   460  	})
   461  }
   462  
   463  func FuzzInitRepo(f *testing.F) {
   464  	f.Fuzz(func(t *testing.T, data string) {
   465  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   466  		metrics := monitoring.NewMetricsServer(false, *log)
   467  
   468  		dir := t.TempDir()
   469  		defer os.RemoveAll(dir)
   470  
   471  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   472  			RootDir:     dir,
   473  			Name:        "cache",
   474  			UseRelPaths: true,
   475  		}, *log)
   476  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   477  		err := imgStore.InitRepo(data)
   478  		if err != nil {
   479  			if isKnownErr(err) {
   480  				return
   481  			}
   482  			t.Error(err)
   483  		}
   484  	})
   485  }
   486  
   487  func FuzzInitValidateRepo(f *testing.F) {
   488  	f.Fuzz(func(t *testing.T, data string) {
   489  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   490  		metrics := monitoring.NewMetricsServer(false, *log)
   491  
   492  		dir := t.TempDir()
   493  		defer os.RemoveAll(dir)
   494  
   495  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   496  			RootDir:     dir,
   497  			Name:        "cache",
   498  			UseRelPaths: true,
   499  		}, *log)
   500  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   501  		err := imgStore.InitRepo(data)
   502  		if err != nil {
   503  			if isKnownErr(err) {
   504  				return
   505  			}
   506  			t.Error(err)
   507  		}
   508  		_, err = imgStore.ValidateRepo(data)
   509  		if err != nil {
   510  			if errors.Is(err, zerr.ErrRepoNotFound) || errors.Is(err, zerr.ErrRepoBadVersion) || isKnownErr(err) {
   511  				return
   512  			}
   513  			t.Error(err)
   514  		}
   515  	})
   516  }
   517  
   518  func FuzzGetImageTags(f *testing.F) {
   519  	f.Fuzz(func(t *testing.T, data string) {
   520  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   521  		metrics := monitoring.NewMetricsServer(false, *log)
   522  
   523  		dir := t.TempDir()
   524  		defer os.RemoveAll(dir)
   525  
   526  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   527  			RootDir:     dir,
   528  			Name:        "cache",
   529  			UseRelPaths: true,
   530  		}, *log)
   531  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   532  		_, err := imgStore.GetImageTags(data)
   533  		if err != nil {
   534  			if errors.Is(err, zerr.ErrRepoNotFound) || isKnownErr(err) {
   535  				return
   536  			}
   537  			t.Error(err)
   538  		}
   539  	})
   540  }
   541  
   542  func FuzzBlobUploadPath(f *testing.F) {
   543  	f.Fuzz(func(t *testing.T, repo, uuid string) {
   544  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   545  		metrics := monitoring.NewMetricsServer(false, *log)
   546  
   547  		dir := t.TempDir()
   548  		defer os.RemoveAll(dir)
   549  
   550  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   551  			RootDir:     dir,
   552  			Name:        "cache",
   553  			UseRelPaths: true,
   554  		}, *log)
   555  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   556  
   557  		_ = imgStore.BlobUploadPath(repo, uuid)
   558  	})
   559  }
   560  
   561  func FuzzBlobUploadInfo(f *testing.F) {
   562  	f.Fuzz(func(t *testing.T, data string, uuid string) {
   563  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   564  		metrics := monitoring.NewMetricsServer(false, *log)
   565  
   566  		dir := t.TempDir()
   567  		defer os.RemoveAll(dir)
   568  
   569  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   570  			RootDir:     dir,
   571  			Name:        "cache",
   572  			UseRelPaths: true,
   573  		}, *log)
   574  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   575  		repo := data
   576  
   577  		_, err := imgStore.BlobUploadInfo(repo, uuid)
   578  		if err != nil {
   579  			if isKnownErr(err) {
   580  				return
   581  			}
   582  			t.Error(err)
   583  		}
   584  	})
   585  }
   586  
   587  func FuzzTestGetImageManifest(f *testing.F) {
   588  	f.Fuzz(func(t *testing.T, data string) {
   589  		dir := t.TempDir()
   590  		defer os.RemoveAll(dir)
   591  
   592  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
   593  		metrics := monitoring.NewMetricsServer(false, log)
   594  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   595  			RootDir:     dir,
   596  			Name:        "cache",
   597  			UseRelPaths: true,
   598  		}, log)
   599  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
   600  
   601  		repoName := data
   602  
   603  		digest := godigest.FromBytes([]byte(data))
   604  
   605  		_, _, _, err := imgStore.GetImageManifest(repoName, digest.String())
   606  		if err != nil {
   607  			if isKnownErr(err) {
   608  				return
   609  			}
   610  			t.Error(err)
   611  		}
   612  	})
   613  }
   614  
   615  func FuzzFinishBlobUpload(f *testing.F) {
   616  	f.Fuzz(func(t *testing.T, data string) {
   617  		dir := t.TempDir()
   618  		defer os.RemoveAll(dir)
   619  
   620  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
   621  		metrics := monitoring.NewMetricsServer(false, log)
   622  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   623  			RootDir:     dir,
   624  			Name:        "cache",
   625  			UseRelPaths: true,
   626  		}, log)
   627  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
   628  
   629  		repoName := data
   630  
   631  		upload, err := imgStore.NewBlobUpload(repoName)
   632  		if err != nil {
   633  			if isKnownErr(err) {
   634  				return
   635  			}
   636  			t.Error(err)
   637  		}
   638  
   639  		content := []byte(data)
   640  		buf := bytes.NewBuffer(content)
   641  		buflen := buf.Len()
   642  		digest := godigest.FromBytes(content)
   643  
   644  		_, err = imgStore.PutBlobChunk(repoName, upload, 0, int64(buflen), buf)
   645  		if err != nil {
   646  			if isKnownErr(err) {
   647  				return
   648  			}
   649  			t.Error(err)
   650  		}
   651  
   652  		err = imgStore.FinishBlobUpload(repoName, upload, buf, digest)
   653  		if err != nil {
   654  			if isKnownErr(err) {
   655  				return
   656  			}
   657  			t.Error(err)
   658  		}
   659  	})
   660  }
   661  
   662  func FuzzFullBlobUpload(f *testing.F) {
   663  	f.Fuzz(func(t *testing.T, data []byte) {
   664  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   665  		metrics := monitoring.NewMetricsServer(false, *log)
   666  		repoName := "test"
   667  
   668  		dir := t.TempDir()
   669  		defer os.RemoveAll(dir)
   670  
   671  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   672  			RootDir:     dir,
   673  			Name:        "cache",
   674  			UseRelPaths: true,
   675  		}, *log)
   676  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   677  
   678  		ldigest, lblob, err := newRandomBlobForFuzz(data)
   679  		if err != nil {
   680  			t.Errorf("error occurred while generating random blob, %v", err)
   681  		}
   682  
   683  		_, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(lblob), ldigest)
   684  		if err != nil {
   685  			if isKnownErr(err) {
   686  				return
   687  			}
   688  			t.Error(err)
   689  		}
   690  	})
   691  }
   692  
   693  func TestStorageCacheErrors(t *testing.T) {
   694  	Convey("get error in DedupeBlob() when cache.Put() deduped blob", t, func() {
   695  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
   696  		metrics := monitoring.NewMetricsServer(false, log)
   697  
   698  		dir := t.TempDir()
   699  
   700  		originRepo := "dedupe1"
   701  		dedupedRepo := "dedupe2"
   702  
   703  		cblob, cdigest := GetRandomImageConfig()
   704  
   705  		getBlobPath := ""
   706  
   707  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, &mocks.CacheMock{
   708  			PutBlobFn: func(digest godigest.Digest, path string) error {
   709  				if strings.Contains(path, dedupedRepo) {
   710  					return errCache
   711  				}
   712  
   713  				return nil
   714  			},
   715  			GetBlobFn: func(digest godigest.Digest) (string, error) {
   716  				return getBlobPath, nil
   717  			},
   718  		})
   719  
   720  		err := imgStore.InitRepo(originRepo)
   721  		So(err, ShouldBeNil)
   722  
   723  		err = imgStore.InitRepo(dedupedRepo)
   724  		So(err, ShouldBeNil)
   725  
   726  		_, _, err = imgStore.FullBlobUpload(originRepo, bytes.NewReader(cblob), cdigest)
   727  		So(err, ShouldBeNil)
   728  
   729  		getBlobPath = imgStore.BlobPath(originRepo, cdigest)
   730  		_, _, err = imgStore.FullBlobUpload(dedupedRepo, bytes.NewReader(cblob), cdigest)
   731  		So(err, ShouldNotBeNil)
   732  	})
   733  }
   734  
   735  func FuzzDedupeBlob(f *testing.F) {
   736  	f.Fuzz(func(t *testing.T, data string) {
   737  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   738  		metrics := monitoring.NewMetricsServer(false, *log)
   739  
   740  		dir := t.TempDir()
   741  		defer os.RemoveAll(dir)
   742  
   743  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   744  			RootDir:     dir,
   745  			Name:        "cache",
   746  			UseRelPaths: true,
   747  		}, *log)
   748  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   749  
   750  		blobDigest := godigest.FromString(data)
   751  
   752  		// replacement for .uploads folder, usually retrieved from BlobUploadPath
   753  		src := path.Join(imgStore.RootDir(), "src")
   754  		blob := bytes.NewReader([]byte(data))
   755  
   756  		_, _, err := imgStore.FullBlobUpload("repoName", blob, blobDigest)
   757  		if err != nil {
   758  			t.Error(err)
   759  		}
   760  
   761  		dst := imgStore.BlobPath("repoName", blobDigest)
   762  
   763  		err = os.MkdirAll(src, 0o755)
   764  		if err != nil {
   765  			t.Error(err)
   766  		}
   767  
   768  		err = imgStore.DedupeBlob(src, blobDigest, "repoName", dst)
   769  		if err != nil {
   770  			t.Error(err)
   771  		}
   772  	})
   773  }
   774  
   775  func FuzzDeleteBlobUpload(f *testing.F) {
   776  	f.Fuzz(func(t *testing.T, data string) {
   777  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   778  		metrics := monitoring.NewMetricsServer(false, *log)
   779  		repoName := data
   780  
   781  		dir := t.TempDir()
   782  		defer os.RemoveAll(dir)
   783  
   784  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   785  			RootDir:     dir,
   786  			Name:        "cache",
   787  			UseRelPaths: true,
   788  		}, *log)
   789  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   790  
   791  		uuid, err := imgStore.NewBlobUpload(repoName)
   792  		if err != nil {
   793  			if isKnownErr(err) {
   794  				return
   795  			}
   796  			t.Error(err)
   797  		}
   798  
   799  		err = imgStore.DeleteBlobUpload(repoName, uuid)
   800  		if err != nil {
   801  			t.Error(err)
   802  		}
   803  	})
   804  }
   805  
   806  func FuzzBlobPath(f *testing.F) {
   807  	f.Fuzz(func(t *testing.T, data string) {
   808  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   809  		metrics := monitoring.NewMetricsServer(false, *log)
   810  		repoName := data
   811  
   812  		dir := t.TempDir()
   813  		defer os.RemoveAll(dir)
   814  
   815  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   816  			RootDir:     dir,
   817  			Name:        "cache",
   818  			UseRelPaths: true,
   819  		}, *log)
   820  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   821  		digest := godigest.FromString(data)
   822  
   823  		_ = imgStore.BlobPath(repoName, digest)
   824  	})
   825  }
   826  
   827  func FuzzCheckBlob(f *testing.F) {
   828  	f.Fuzz(func(t *testing.T, data string) {
   829  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   830  		metrics := monitoring.NewMetricsServer(false, *log)
   831  		repoName := data
   832  
   833  		dir := t.TempDir()
   834  		defer os.RemoveAll(dir)
   835  
   836  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   837  			RootDir:     dir,
   838  			Name:        "cache",
   839  			UseRelPaths: true,
   840  		}, *log)
   841  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   842  		digest := godigest.FromString(data)
   843  
   844  		_, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest)
   845  		if err != nil {
   846  			if isKnownErr(err) {
   847  				return
   848  			}
   849  			t.Error(err)
   850  		}
   851  		_, _, err = imgStore.CheckBlob(repoName, digest)
   852  		if err != nil {
   853  			t.Error(err)
   854  		}
   855  	})
   856  }
   857  
   858  func FuzzGetBlob(f *testing.F) {
   859  	f.Fuzz(func(t *testing.T, data string) {
   860  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   861  		metrics := monitoring.NewMetricsServer(false, *log)
   862  		repoName := data
   863  
   864  		dir := t.TempDir()
   865  		defer os.RemoveAll(dir)
   866  
   867  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   868  			RootDir:     dir,
   869  			Name:        "cache",
   870  			UseRelPaths: true,
   871  		}, *log)
   872  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   873  		digest := godigest.FromString(data)
   874  
   875  		_, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest)
   876  		if err != nil {
   877  			if isKnownErr(err) {
   878  				return
   879  			}
   880  			t.Error(err)
   881  		}
   882  
   883  		blobReadCloser, _, err := imgStore.GetBlob(repoName, digest, "application/vnd.oci.image.layer.v1.tar+gzip")
   884  		if err != nil {
   885  			if isKnownErr(err) {
   886  				return
   887  			}
   888  			t.Error(err)
   889  		}
   890  		if err = blobReadCloser.Close(); err != nil {
   891  			t.Error(err)
   892  		}
   893  	})
   894  }
   895  
   896  func FuzzDeleteBlob(f *testing.F) {
   897  	f.Fuzz(func(t *testing.T, data string) {
   898  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   899  		metrics := monitoring.NewMetricsServer(false, *log)
   900  		repoName := data
   901  
   902  		dir := t.TempDir()
   903  		defer os.RemoveAll(dir)
   904  
   905  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   906  			RootDir:     dir,
   907  			Name:        "cache",
   908  			UseRelPaths: true,
   909  		}, *log)
   910  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   911  		digest := godigest.FromString(data)
   912  
   913  		_, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest)
   914  		if err != nil {
   915  			if isKnownErr(err) {
   916  				return
   917  			}
   918  			t.Error(err)
   919  		}
   920  
   921  		err = imgStore.DeleteBlob(repoName, digest)
   922  		if err != nil {
   923  			if isKnownErr(err) {
   924  				return
   925  			}
   926  			t.Error(err)
   927  		}
   928  	})
   929  }
   930  
   931  func FuzzGetIndexContent(f *testing.F) {
   932  	f.Fuzz(func(t *testing.T, data string) {
   933  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   934  		metrics := monitoring.NewMetricsServer(false, *log)
   935  		repoName := data
   936  
   937  		dir := t.TempDir()
   938  		defer os.RemoveAll(dir)
   939  
   940  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   941  			RootDir:     dir,
   942  			Name:        "cache",
   943  			UseRelPaths: true,
   944  		}, *log)
   945  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   946  		digest := godigest.FromString(data)
   947  
   948  		_, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest)
   949  		if err != nil {
   950  			if isKnownErr(err) {
   951  				return
   952  			}
   953  			t.Error(err)
   954  		}
   955  
   956  		_, err = imgStore.GetIndexContent(repoName)
   957  		if err != nil {
   958  			if isKnownErr(err) {
   959  				return
   960  			}
   961  			t.Error(err)
   962  		}
   963  	})
   964  }
   965  
   966  func FuzzGetBlobContent(f *testing.F) {
   967  	f.Fuzz(func(t *testing.T, data string) {
   968  		log := &zlog.Logger{Logger: zerolog.New(os.Stdout)}
   969  		metrics := monitoring.NewMetricsServer(false, *log)
   970  		repoName := data
   971  
   972  		dir := t.TempDir()
   973  		defer os.RemoveAll(dir)
   974  
   975  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
   976  			RootDir:     dir,
   977  			Name:        "cache",
   978  			UseRelPaths: true,
   979  		}, *log)
   980  		imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver)
   981  		digest := godigest.FromString(data)
   982  
   983  		_, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest)
   984  		if err != nil {
   985  			if isKnownErr(err) {
   986  				return
   987  			}
   988  			t.Error(err)
   989  		}
   990  
   991  		_, err = imgStore.GetBlobContent(repoName, digest)
   992  		if err != nil {
   993  			if isKnownErr(err) {
   994  				return
   995  			}
   996  			t.Error(err)
   997  		}
   998  	})
   999  }
  1000  
  1001  func FuzzRunGCRepo(f *testing.F) {
  1002  	f.Fuzz(func(t *testing.T, data string) {
  1003  		log := zlog.NewLogger("debug", "")
  1004  		audit := zlog.NewAuditLogger("debug", "")
  1005  
  1006  		metrics := monitoring.NewMetricsServer(false, log)
  1007  		dir := t.TempDir()
  1008  		defer os.RemoveAll(dir)
  1009  
  1010  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1011  			RootDir:     dir,
  1012  			Name:        "cache",
  1013  			UseRelPaths: true,
  1014  		}, log)
  1015  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1016  
  1017  		gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{
  1018  			Delay:          storageConstants.DefaultGCDelay,
  1019  			ImageRetention: DeleteReferrers,
  1020  		}, audit, log)
  1021  
  1022  		if err := gc.CleanRepo(context.Background(), data); err != nil {
  1023  			t.Error(err)
  1024  		}
  1025  	})
  1026  }
  1027  
  1028  func TestDedupeLinks(t *testing.T) {
  1029  	testCases := []struct {
  1030  		dedupe   bool
  1031  		expected bool
  1032  	}{
  1033  		{
  1034  			dedupe:   true,
  1035  			expected: true,
  1036  		},
  1037  		{
  1038  			dedupe:   false,
  1039  			expected: false,
  1040  		},
  1041  	}
  1042  
  1043  	log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1044  	metrics := monitoring.NewMetricsServer(false, log)
  1045  
  1046  	for _, testCase := range testCases {
  1047  		Convey(fmt.Sprintf("Dedupe %t", testCase.dedupe), t, func(c C) {
  1048  			dir := t.TempDir()
  1049  
  1050  			cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1051  				RootDir:     dir,
  1052  				Name:        "cache",
  1053  				UseRelPaths: true,
  1054  			}, log)
  1055  
  1056  			var imgStore storageTypes.ImageStore
  1057  
  1058  			if testCase.dedupe {
  1059  				imgStore = local.NewImageStore(dir, testCase.dedupe, true, log, metrics, nil, cacheDriver)
  1060  			} else {
  1061  				imgStore = local.NewImageStore(dir, testCase.dedupe, true, log, metrics, nil, nil)
  1062  			}
  1063  
  1064  			// run on empty image store
  1065  			// switch dedupe to true from false
  1066  			taskScheduler := runAndGetScheduler()
  1067  			defer taskScheduler.Shutdown()
  1068  
  1069  			// rebuild with dedupe true
  1070  			imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler)
  1071  			// wait until rebuild finishes
  1072  			time.Sleep(1 * time.Second)
  1073  
  1074  			taskScheduler.Shutdown()
  1075  
  1076  			// manifest1
  1077  			upload, err := imgStore.NewBlobUpload("dedupe1")
  1078  			So(err, ShouldBeNil)
  1079  			So(upload, ShouldNotBeEmpty)
  1080  
  1081  			content := []byte("test-data3")
  1082  			buf := bytes.NewBuffer(content)
  1083  			buflen := buf.Len()
  1084  			digest := godigest.FromBytes(content)
  1085  			blob, err := imgStore.PutBlobChunkStreamed("dedupe1", upload, buf)
  1086  			So(err, ShouldBeNil)
  1087  			So(blob, ShouldEqual, buflen)
  1088  			blobDigest1 := strings.Split(digest.String(), ":")[1]
  1089  			So(blobDigest1, ShouldNotBeEmpty)
  1090  
  1091  			err = imgStore.FinishBlobUpload("dedupe1", upload, buf, digest)
  1092  			So(err, ShouldBeNil)
  1093  			So(blob, ShouldEqual, buflen)
  1094  
  1095  			_, _, err = imgStore.CheckBlob("dedupe1", digest)
  1096  			So(err, ShouldBeNil)
  1097  
  1098  			blobrc, _, err := imgStore.GetBlob("dedupe1", digest, "application/vnd.oci.image.layer.v1.tar+gzip")
  1099  			So(err, ShouldBeNil)
  1100  			err = blobrc.Close()
  1101  			So(err, ShouldBeNil)
  1102  
  1103  			cblob, cdigest := GetRandomImageConfig()
  1104  			_, clen, err := imgStore.FullBlobUpload("dedupe1", bytes.NewReader(cblob), cdigest)
  1105  			So(err, ShouldBeNil)
  1106  			So(clen, ShouldEqual, len(cblob))
  1107  			hasBlob, _, err := imgStore.CheckBlob("dedupe1", cdigest)
  1108  			So(err, ShouldBeNil)
  1109  			So(hasBlob, ShouldEqual, true)
  1110  
  1111  			manifest := ispec.Manifest{
  1112  				Config: ispec.Descriptor{
  1113  					MediaType: "application/vnd.oci.image.config.v1+json",
  1114  					Digest:    cdigest,
  1115  					Size:      int64(len(cblob)),
  1116  				},
  1117  				Layers: []ispec.Descriptor{
  1118  					{
  1119  						MediaType: "application/vnd.oci.image.layer.v1.tar",
  1120  						Digest:    digest,
  1121  						Size:      int64(buflen),
  1122  					},
  1123  				},
  1124  			}
  1125  			manifest.SchemaVersion = 2
  1126  			manifestBuf, err := json.Marshal(manifest)
  1127  			So(err, ShouldBeNil)
  1128  			manifestDigest := godigest.FromBytes(manifestBuf)
  1129  			_, _, err = imgStore.PutImageManifest("dedupe1", manifestDigest.String(),
  1130  				ispec.MediaTypeImageManifest, manifestBuf)
  1131  			So(err, ShouldBeNil)
  1132  
  1133  			_, _, _, err = imgStore.GetImageManifest("dedupe1", manifestDigest.String())
  1134  			So(err, ShouldBeNil)
  1135  
  1136  			// manifest2
  1137  			upload, err = imgStore.NewBlobUpload("dedupe2")
  1138  			So(err, ShouldBeNil)
  1139  			So(upload, ShouldNotBeEmpty)
  1140  
  1141  			content = []byte("test-data3")
  1142  			buf = bytes.NewBuffer(content)
  1143  			buflen = buf.Len()
  1144  			digest = godigest.FromBytes(content)
  1145  			blob, err = imgStore.PutBlobChunkStreamed("dedupe2", upload, buf)
  1146  			So(err, ShouldBeNil)
  1147  			So(blob, ShouldEqual, buflen)
  1148  			blobDigest2 := strings.Split(digest.String(), ":")[1]
  1149  			So(blobDigest2, ShouldNotBeEmpty)
  1150  
  1151  			err = imgStore.FinishBlobUpload("dedupe2", upload, buf, digest)
  1152  			So(err, ShouldBeNil)
  1153  			So(blob, ShouldEqual, buflen)
  1154  
  1155  			_, _, err = imgStore.CheckBlob("dedupe2", digest)
  1156  			So(err, ShouldBeNil)
  1157  
  1158  			blobrc, _, err = imgStore.GetBlob("dedupe2", digest, "application/vnd.oci.image.layer.v1.tar+gzip")
  1159  			So(err, ShouldBeNil)
  1160  			err = blobrc.Close()
  1161  			So(err, ShouldBeNil)
  1162  
  1163  			cblob, cdigest = GetRandomImageConfig()
  1164  			_, clen, err = imgStore.FullBlobUpload("dedupe2", bytes.NewReader(cblob), cdigest)
  1165  			So(err, ShouldBeNil)
  1166  			So(clen, ShouldEqual, len(cblob))
  1167  			hasBlob, _, err = imgStore.CheckBlob("dedupe2", cdigest)
  1168  			So(err, ShouldBeNil)
  1169  			So(hasBlob, ShouldEqual, true)
  1170  
  1171  			manifest = ispec.Manifest{
  1172  				Config: ispec.Descriptor{
  1173  					MediaType: "application/vnd.oci.image.config.v1+json",
  1174  					Digest:    cdigest,
  1175  					Size:      int64(len(cblob)),
  1176  				},
  1177  				Layers: []ispec.Descriptor{
  1178  					{
  1179  						MediaType: "application/vnd.oci.image.layer.v1.tar",
  1180  						Digest:    digest,
  1181  						Size:      int64(buflen),
  1182  					},
  1183  				},
  1184  			}
  1185  			manifest.SchemaVersion = 2
  1186  			manifestBuf, err = json.Marshal(manifest)
  1187  			So(err, ShouldBeNil)
  1188  			digest = godigest.FromBytes(manifestBuf)
  1189  			_, _, err = imgStore.PutImageManifest("dedupe2", "1.0", ispec.MediaTypeImageManifest, manifestBuf)
  1190  			So(err, ShouldBeNil)
  1191  
  1192  			_, _, _, err = imgStore.GetImageManifest("dedupe2", digest.String())
  1193  			So(err, ShouldBeNil)
  1194  
  1195  			// verify that dedupe with hard links happened
  1196  			fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1))
  1197  			So(err, ShouldBeNil)
  1198  			fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2))
  1199  			So(err, ShouldBeNil)
  1200  			So(os.SameFile(fi1, fi2), ShouldEqual, testCase.expected)
  1201  
  1202  			if !testCase.dedupe {
  1203  				Convey("delete blobs from storage/cache should work when dedupe is false", func() {
  1204  					So(blobDigest1, ShouldEqual, blobDigest2)
  1205  
  1206  					// to not trigger BlobInUse err, delete manifest first
  1207  					err = imgStore.DeleteImageManifest("dedupe1", manifestDigest.String(), false)
  1208  					So(err, ShouldBeNil)
  1209  
  1210  					err = imgStore.DeleteImageManifest("dedupe2", "1.0", false)
  1211  					So(err, ShouldBeNil)
  1212  
  1213  					err = imgStore.DeleteBlob("dedupe1", godigest.NewDigestFromEncoded(godigest.SHA256, blobDigest1))
  1214  					So(err, ShouldBeNil)
  1215  
  1216  					err = imgStore.DeleteBlob("dedupe2", godigest.NewDigestFromEncoded(godigest.SHA256, blobDigest2))
  1217  					So(err, ShouldBeNil)
  1218  				})
  1219  
  1220  				Convey("test RunDedupeForDigest directly, trigger stat error on original blob", func() {
  1221  					// rebuild with dedupe true
  1222  					imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1223  
  1224  					duplicateBlobs := []string{
  1225  						path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1),
  1226  						path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest2),
  1227  					}
  1228  
  1229  					// remove original blob so that it can not be statted
  1230  					err := os.Remove(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1))
  1231  					So(err, ShouldBeNil)
  1232  
  1233  					err = imgStore.RunDedupeForDigest(context.TODO(), godigest.Digest(blobDigest1), true, duplicateBlobs)
  1234  					So(err, ShouldNotBeNil)
  1235  				})
  1236  
  1237  				Convey("Intrerrupt rebuilding and restart, checking idempotency", func() {
  1238  					for i := 0; i < 10; i++ {
  1239  						taskScheduler := runAndGetScheduler()
  1240  						defer taskScheduler.Shutdown()
  1241  
  1242  						// rebuild with dedupe true
  1243  						imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1244  
  1245  						imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler)
  1246  						sleepValue := i * 5
  1247  						time.Sleep(time.Duration(sleepValue) * time.Millisecond)
  1248  
  1249  						taskScheduler.Shutdown()
  1250  					}
  1251  
  1252  					taskScheduler := runAndGetScheduler()
  1253  					defer taskScheduler.Shutdown()
  1254  
  1255  					// rebuild with dedupe true
  1256  					imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1257  					imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler)
  1258  
  1259  					// wait until rebuild finishes
  1260  					time.Sleep(10 * time.Second)
  1261  
  1262  					taskScheduler.Shutdown()
  1263  
  1264  					fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1))
  1265  					So(err, ShouldBeNil)
  1266  					fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2))
  1267  					So(err, ShouldBeNil)
  1268  					So(os.SameFile(fi1, fi2), ShouldEqual, true)
  1269  				})
  1270  
  1271  				Convey("rebuild dedupe index error cache nil", func() {
  1272  					// switch dedupe to true from false
  1273  					taskScheduler := runAndGetScheduler()
  1274  					defer taskScheduler.Shutdown()
  1275  
  1276  					imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, nil)
  1277  
  1278  					// rebuild with dedupe true
  1279  					imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler)
  1280  					// wait until rebuild finishes
  1281  
  1282  					time.Sleep(3 * time.Second)
  1283  
  1284  					taskScheduler.Shutdown()
  1285  
  1286  					fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1))
  1287  					So(err, ShouldBeNil)
  1288  					fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2))
  1289  					So(err, ShouldBeNil)
  1290  
  1291  					So(os.SameFile(fi1, fi2), ShouldEqual, false)
  1292  				})
  1293  
  1294  				Convey("rebuild dedupe index cache error on original blob", func() {
  1295  					// switch dedupe to true from false
  1296  					taskScheduler := runAndGetScheduler()
  1297  					defer taskScheduler.Shutdown()
  1298  
  1299  					imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, &mocks.CacheMock{
  1300  						HasBlobFn: func(digest godigest.Digest, path string) bool {
  1301  							return false
  1302  						},
  1303  						PutBlobFn: func(digest godigest.Digest, path string) error {
  1304  							return errCache
  1305  						},
  1306  					})
  1307  					// rebuild with dedupe true, should have samefile blobs
  1308  					imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler)
  1309  					// wait until rebuild finishes
  1310  
  1311  					time.Sleep(10 * time.Second)
  1312  
  1313  					taskScheduler.Shutdown()
  1314  
  1315  					fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1))
  1316  					So(err, ShouldBeNil)
  1317  					fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2))
  1318  					So(err, ShouldBeNil)
  1319  
  1320  					So(os.SameFile(fi1, fi2), ShouldEqual, false)
  1321  				})
  1322  
  1323  				Convey("rebuild dedupe index cache error on duplicate blob", func() {
  1324  					// switch dedupe to true from false
  1325  					taskScheduler := runAndGetScheduler()
  1326  					defer taskScheduler.Shutdown()
  1327  
  1328  					imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, &mocks.CacheMock{
  1329  						HasBlobFn: func(digest godigest.Digest, path string) bool {
  1330  							return false
  1331  						},
  1332  						PutBlobFn: func(digest godigest.Digest, path string) error {
  1333  							if strings.Contains(path, "dedupe2") {
  1334  								return errCache
  1335  							}
  1336  
  1337  							return nil
  1338  						},
  1339  					})
  1340  					// rebuild with dedupe true, should have samefile blobs
  1341  					imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler)
  1342  					// wait until rebuild finishes
  1343  
  1344  					time.Sleep(15 * time.Second)
  1345  
  1346  					taskScheduler.Shutdown()
  1347  
  1348  					fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1))
  1349  					So(err, ShouldBeNil)
  1350  					fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2))
  1351  					So(err, ShouldBeNil)
  1352  
  1353  					So(os.SameFile(fi1, fi2), ShouldEqual, true)
  1354  				})
  1355  			}
  1356  
  1357  			Convey("delete blobs from storage/cache should work when dedupe is true", func() {
  1358  				So(blobDigest1, ShouldEqual, blobDigest2)
  1359  
  1360  				// to not trigger BlobInUse err, delete manifest first
  1361  				err = imgStore.DeleteImageManifest("dedupe1", manifestDigest.String(), false)
  1362  				So(err, ShouldBeNil)
  1363  
  1364  				err = imgStore.DeleteImageManifest("dedupe2", "1.0", false)
  1365  				So(err, ShouldBeNil)
  1366  
  1367  				err = imgStore.DeleteBlob("dedupe1", godigest.NewDigestFromEncoded(godigest.SHA256, blobDigest1))
  1368  				So(err, ShouldBeNil)
  1369  
  1370  				err = imgStore.DeleteBlob("dedupe2", godigest.NewDigestFromEncoded(godigest.SHA256, blobDigest2))
  1371  				So(err, ShouldBeNil)
  1372  			})
  1373  
  1374  			Convey("storage and cache inconsistency", func() {
  1375  				// delete blobs
  1376  				err = os.Remove(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1))
  1377  				So(err, ShouldBeNil)
  1378  
  1379  				err := os.Remove(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2))
  1380  				So(err, ShouldBeNil)
  1381  
  1382  				// now cache is inconsistent with storage (blobs present in cache but not in storage)
  1383  				upload, err = imgStore.NewBlobUpload("dedupe3")
  1384  				So(err, ShouldBeNil)
  1385  				So(upload, ShouldNotBeEmpty)
  1386  
  1387  				content = []byte("test-data3")
  1388  				buf = bytes.NewBuffer(content)
  1389  				buflen = buf.Len()
  1390  				digest = godigest.FromBytes(content)
  1391  				blob, err = imgStore.PutBlobChunkStreamed("dedupe3", upload, buf)
  1392  				So(err, ShouldBeNil)
  1393  				So(blob, ShouldEqual, buflen)
  1394  				blobDigest2 := strings.Split(digest.String(), ":")[1]
  1395  				So(blobDigest2, ShouldNotBeEmpty)
  1396  
  1397  				err = imgStore.FinishBlobUpload("dedupe3", upload, buf, digest)
  1398  				So(err, ShouldBeNil)
  1399  				So(blob, ShouldEqual, buflen)
  1400  			})
  1401  		})
  1402  	}
  1403  }
  1404  
  1405  func TestDedupe(t *testing.T) {
  1406  	Convey("Dedupe", t, func(c C) {
  1407  		Convey("Nil ImageStore", func() {
  1408  			var is storageTypes.ImageStore
  1409  			So(func() { _ = is.DedupeBlob("", "", "", "") }, ShouldPanic)
  1410  		})
  1411  
  1412  		Convey("Valid ImageStore", func() {
  1413  			dir := t.TempDir()
  1414  
  1415  			log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1416  			metrics := monitoring.NewMetricsServer(false, log)
  1417  			cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1418  				RootDir:     dir,
  1419  				Name:        "cache",
  1420  				UseRelPaths: true,
  1421  			}, log)
  1422  
  1423  			il := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1424  
  1425  			So(il.DedupeBlob("", "", "", ""), ShouldNotBeNil)
  1426  		})
  1427  	})
  1428  }
  1429  
  1430  //nolint:gocyclo
  1431  func TestNegativeCases(t *testing.T) {
  1432  	Convey("Invalid root dir", t, func(c C) {
  1433  		dir := t.TempDir()
  1434  
  1435  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1436  		metrics := monitoring.NewMetricsServer(false, log)
  1437  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1438  			RootDir:     dir,
  1439  			Name:        "cache",
  1440  			UseRelPaths: true,
  1441  		}, log)
  1442  
  1443  		So(local.NewImageStore(dir, true,
  1444  			true, log, metrics, nil, cacheDriver), ShouldNotBeNil)
  1445  		if os.Geteuid() != 0 {
  1446  			cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1447  				RootDir:     "/deadBEEF",
  1448  				Name:        "cache",
  1449  				UseRelPaths: true,
  1450  			}, log)
  1451  			So(local.NewImageStore("/deadBEEF", true, true, log, metrics, nil, cacheDriver), ShouldBeNil)
  1452  		}
  1453  	})
  1454  
  1455  	Convey("Invalid init repo", t, func(c C) {
  1456  		dir := t.TempDir()
  1457  
  1458  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1459  		metrics := monitoring.NewMetricsServer(false, log)
  1460  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1461  			RootDir:     dir,
  1462  			Name:        "cache",
  1463  			UseRelPaths: true,
  1464  		}, log)
  1465  
  1466  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1467  
  1468  		err := os.Chmod(dir, 0o000) // remove all perms
  1469  		if err != nil {
  1470  			panic(err)
  1471  		}
  1472  
  1473  		if os.Geteuid() != 0 {
  1474  			err = imgStore.InitRepo("test")
  1475  			So(err, ShouldNotBeNil)
  1476  		}
  1477  
  1478  		err = os.Chmod(dir, 0o755)
  1479  		if err != nil {
  1480  			panic(err)
  1481  		}
  1482  
  1483  		// Init repo should fail if repo is a file.
  1484  		err = os.WriteFile(path.Join(dir, "file-test"), []byte("this is test file"), 0o755) //nolint:gosec
  1485  		So(err, ShouldBeNil)
  1486  		err = imgStore.InitRepo("file-test")
  1487  		So(err, ShouldNotBeNil)
  1488  
  1489  		err = os.Mkdir(path.Join(dir, "test-dir"), 0o755)
  1490  		So(err, ShouldBeNil)
  1491  
  1492  		err = imgStore.InitRepo("test-dir")
  1493  		So(err, ShouldBeNil)
  1494  
  1495  		// Init repo should fail if repo is invalid UTF-8
  1496  		err = imgStore.InitRepo("hi \255")
  1497  		So(err, ShouldNotBeNil)
  1498  
  1499  		// Init repo should fail if repo name does not match spec
  1500  		err = imgStore.InitRepo("_trivy")
  1501  		So(err, ShouldNotBeNil)
  1502  		So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue)
  1503  	})
  1504  
  1505  	Convey("Invalid validate repo", t, func(c C) {
  1506  		dir := t.TempDir()
  1507  
  1508  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1509  		metrics := monitoring.NewMetricsServer(false, log)
  1510  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1511  			RootDir:     dir,
  1512  			Name:        "cache",
  1513  			UseRelPaths: true,
  1514  		}, log)
  1515  
  1516  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1517  
  1518  		So(imgStore, ShouldNotBeNil)
  1519  		So(imgStore.InitRepo("test"), ShouldBeNil)
  1520  
  1521  		err := os.MkdirAll(path.Join(dir, "invalid-test"), 0o755)
  1522  		So(err, ShouldBeNil)
  1523  
  1524  		err = os.Chmod(path.Join(dir, "invalid-test"), 0o000) // remove all perms
  1525  		if err != nil {
  1526  			panic(err)
  1527  		}
  1528  		_, err = imgStore.ValidateRepo("invalid-test")
  1529  		So(err, ShouldNotBeNil)
  1530  		So(err, ShouldEqual, zerr.ErrRepoNotFound)
  1531  
  1532  		err = os.Chmod(path.Join(dir, "invalid-test"), 0o755) // remove all perms
  1533  		if err != nil {
  1534  			panic(err)
  1535  		}
  1536  
  1537  		err = os.WriteFile(path.Join(dir, "invalid-test", "blobs"), []byte{}, 0o755) //nolint: gosec
  1538  		if err != nil {
  1539  			panic(err)
  1540  		}
  1541  
  1542  		err = os.WriteFile(path.Join(dir, "invalid-test", "index.json"), []byte{}, 0o755) //nolint: gosec
  1543  		if err != nil {
  1544  			panic(err)
  1545  		}
  1546  
  1547  		err = os.WriteFile(path.Join(dir, "invalid-test", ispec.ImageLayoutFile), []byte{}, 0o755) //nolint: gosec
  1548  		if err != nil {
  1549  			panic(err)
  1550  		}
  1551  
  1552  		isValid, err := imgStore.ValidateRepo("invalid-test")
  1553  		So(err, ShouldBeNil)
  1554  		So(isValid, ShouldEqual, false)
  1555  
  1556  		err = os.Remove(path.Join(dir, "invalid-test", "blobs"))
  1557  		if err != nil {
  1558  			panic(err)
  1559  		}
  1560  		err = os.Mkdir(path.Join(dir, "invalid-test", "blobs"), 0o755)
  1561  		if err != nil {
  1562  			panic(err)
  1563  		}
  1564  		isValid, err = imgStore.ValidateRepo("invalid-test")
  1565  		So(err, ShouldNotBeNil)
  1566  		So(isValid, ShouldEqual, false)
  1567  
  1568  		err = os.WriteFile(path.Join(dir, "invalid-test", ispec.ImageLayoutFile), []byte("{}"), 0o755) //nolint: gosec
  1569  		if err != nil {
  1570  			panic(err)
  1571  		}
  1572  
  1573  		isValid, err = imgStore.ValidateRepo("invalid-test")
  1574  		So(err, ShouldNotBeNil)
  1575  		So(err, ShouldEqual, zerr.ErrRepoBadVersion)
  1576  		So(isValid, ShouldEqual, false)
  1577  
  1578  		files, err := os.ReadDir(path.Join(dir, "test"))
  1579  		if err != nil {
  1580  			panic(err)
  1581  		}
  1582  
  1583  		for _, f := range files {
  1584  			os.Remove(path.Join(dir, "test", f.Name()))
  1585  		}
  1586  
  1587  		_, err = imgStore.ValidateRepo("test")
  1588  		So(err, ShouldNotBeNil)
  1589  
  1590  		err = os.RemoveAll(path.Join(dir, "test"))
  1591  		if err != nil {
  1592  			panic(err)
  1593  		}
  1594  
  1595  		_, err = imgStore.ValidateRepo("test")
  1596  		So(err, ShouldNotBeNil)
  1597  
  1598  		err = os.Chmod(dir, 0o000) // remove all perms
  1599  		if err != nil {
  1600  			panic(err)
  1601  		}
  1602  
  1603  		_, err = imgStore.GetRepositories()
  1604  		So(err, ShouldNotBeNil)
  1605  
  1606  		err = os.Chmod(dir, 0o755) // add perms
  1607  		if err != nil {
  1608  			panic(err)
  1609  		}
  1610  
  1611  		err = os.RemoveAll(dir)
  1612  		if err != nil {
  1613  			panic(err)
  1614  		}
  1615  	})
  1616  
  1617  	Convey("Invalid get image tags", t, func(c C) {
  1618  		dir := t.TempDir()
  1619  
  1620  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1621  		metrics := monitoring.NewMetricsServer(false, log)
  1622  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1623  			RootDir:     dir,
  1624  			Name:        "cache",
  1625  			UseRelPaths: true,
  1626  		}, log)
  1627  
  1628  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1629  
  1630  		So(imgStore, ShouldNotBeNil)
  1631  		So(imgStore.InitRepo("test"), ShouldBeNil)
  1632  		So(os.Remove(path.Join(dir, "test", "index.json")), ShouldBeNil)
  1633  		_, err := imgStore.GetImageTags("test")
  1634  		So(err, ShouldNotBeNil)
  1635  		So(os.RemoveAll(path.Join(dir, "test")), ShouldBeNil)
  1636  		So(imgStore.InitRepo("test"), ShouldBeNil)
  1637  		So(os.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0o600), ShouldBeNil)
  1638  		_, err = imgStore.GetImageTags("test")
  1639  		So(err, ShouldNotBeNil)
  1640  	})
  1641  
  1642  	Convey("Invalid get image manifest", t, func(c C) {
  1643  		dir := t.TempDir()
  1644  
  1645  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1646  		metrics := monitoring.NewMetricsServer(false, log)
  1647  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1648  			RootDir:     dir,
  1649  			Name:        "cache",
  1650  			UseRelPaths: true,
  1651  		}, log)
  1652  
  1653  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1654  
  1655  		So(imgStore, ShouldNotBeNil)
  1656  		So(imgStore.InitRepo("test"), ShouldBeNil)
  1657  
  1658  		err := os.Chmod(path.Join(dir, "test", "index.json"), 0o000)
  1659  		if err != nil {
  1660  			panic(err)
  1661  		}
  1662  
  1663  		_, _, _, err = imgStore.GetImageManifest("test", "")
  1664  		So(err, ShouldNotBeNil)
  1665  
  1666  		err = os.Remove(path.Join(dir, "test", "index.json"))
  1667  		if err != nil {
  1668  			panic(err)
  1669  		}
  1670  
  1671  		_, _, _, err = imgStore.GetImageManifest("test", "")
  1672  		So(err, ShouldNotBeNil)
  1673  
  1674  		err = os.RemoveAll(path.Join(dir, "test"))
  1675  		if err != nil {
  1676  			panic(err)
  1677  		}
  1678  
  1679  		So(imgStore.InitRepo("test"), ShouldBeNil)
  1680  
  1681  		err = os.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0o600)
  1682  		if err != nil {
  1683  			panic(err)
  1684  		}
  1685  		_, _, _, err = imgStore.GetImageManifest("test", "")
  1686  		So(err, ShouldNotBeNil)
  1687  	})
  1688  
  1689  	Convey("Invalid new blob upload", t, func(c C) {
  1690  		dir := t.TempDir()
  1691  
  1692  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1693  		metrics := monitoring.NewMetricsServer(false, log)
  1694  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1695  			RootDir:     dir,
  1696  			Name:        "cache",
  1697  			UseRelPaths: true,
  1698  		}, log)
  1699  
  1700  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1701  
  1702  		So(imgStore, ShouldNotBeNil)
  1703  		So(imgStore.InitRepo("test"), ShouldBeNil)
  1704  
  1705  		err := os.Chmod(path.Join(dir, "test", ".uploads"), 0o000)
  1706  		if err != nil {
  1707  			panic(err)
  1708  		}
  1709  		_, err = imgStore.NewBlobUpload("test")
  1710  		So(err, ShouldNotBeNil)
  1711  
  1712  		err = os.Chmod(path.Join(dir, "test"), 0o000)
  1713  		if err != nil {
  1714  			panic(err)
  1715  		}
  1716  
  1717  		_, err = imgStore.NewBlobUpload("test")
  1718  		So(err, ShouldNotBeNil)
  1719  
  1720  		err = os.Chmod(path.Join(dir, "test"), 0o755)
  1721  		if err != nil {
  1722  			panic(err)
  1723  		}
  1724  
  1725  		So(imgStore.InitRepo("test"), ShouldBeNil)
  1726  
  1727  		_, err = imgStore.NewBlobUpload("test")
  1728  		So(err, ShouldNotBeNil)
  1729  
  1730  		err = os.Chmod(path.Join(dir, "test", ".uploads"), 0o755)
  1731  		if err != nil {
  1732  			panic(err)
  1733  		}
  1734  
  1735  		upload, err := imgStore.NewBlobUpload("test")
  1736  		So(err, ShouldBeNil)
  1737  
  1738  		err = os.Chmod(path.Join(dir, "test", ".uploads"), 0o000)
  1739  		if err != nil {
  1740  			panic(err)
  1741  		}
  1742  		t.Cleanup(func() {
  1743  			err = os.Chmod(path.Join(dir, "test", ".uploads"), 0o700)
  1744  			if err != nil {
  1745  				panic(err)
  1746  			}
  1747  		})
  1748  
  1749  		content := []byte("test-data3")
  1750  		buf := bytes.NewBuffer(content)
  1751  		l := buf.Len()
  1752  		_, err = imgStore.PutBlobChunkStreamed("test", upload, buf)
  1753  		So(err, ShouldNotBeNil)
  1754  
  1755  		_, err = imgStore.PutBlobChunk("test", upload, 0, int64(l), buf)
  1756  		So(err, ShouldNotBeNil)
  1757  	})
  1758  
  1759  	Convey("DirExists call with a filename as argument", t, func(c C) {
  1760  		dir := t.TempDir()
  1761  
  1762  		filePath := path.Join(dir, "file.txt")
  1763  		err := os.WriteFile(filePath, []byte("some dummy file content"), 0o644) //nolint: gosec
  1764  		if err != nil {
  1765  			panic(err)
  1766  		}
  1767  
  1768  		ok := common.DirExists(filePath)
  1769  		So(ok, ShouldBeFalse)
  1770  	})
  1771  
  1772  	Convey("DirExists call with invalid UTF-8 as argument", t, func(c C) {
  1773  		dir := t.TempDir()
  1774  
  1775  		filePath := path.Join(dir, "hi \255")
  1776  		ok := common.DirExists(filePath)
  1777  		So(ok, ShouldBeFalse)
  1778  	})
  1779  
  1780  	Convey("DirExists call with name too long as argument", t, func(c C) {
  1781  		var builder strings.Builder
  1782  		for i := 0; i < 1025; i++ {
  1783  			_, err := builder.WriteString("0")
  1784  			if err != nil {
  1785  				t.Fatal(err)
  1786  			}
  1787  		}
  1788  		path := builder.String()
  1789  		ok := common.DirExists(path)
  1790  		So(ok, ShouldBeFalse)
  1791  	})
  1792  }
  1793  
  1794  func TestHardLink(t *testing.T) {
  1795  	Convey("Test that ValidateHardLink creates rootDir if it does not exist", t, func() {
  1796  		var randomDir string
  1797  
  1798  		for {
  1799  			nBig, err := rand.Int(rand.Reader, big.NewInt(100))
  1800  			if err != nil {
  1801  				panic(err)
  1802  			}
  1803  			randomDir = "/tmp/" + randSeq(int(nBig.Int64()))
  1804  
  1805  			if _, err := os.Stat(randomDir); os.IsNotExist(err) {
  1806  				break
  1807  			}
  1808  		}
  1809  		defer os.RemoveAll(randomDir)
  1810  
  1811  		err := local.ValidateHardLink(randomDir)
  1812  		So(err, ShouldBeNil)
  1813  	})
  1814  	Convey("Test that ValidateHardLink returns error if rootDir is a file", t, func() {
  1815  		dir := t.TempDir()
  1816  
  1817  		filePath := path.Join(dir, "file.txt")
  1818  		err := os.WriteFile(filePath, []byte("some dummy file content"), 0o644) //nolint: gosec
  1819  		if err != nil {
  1820  			panic(err)
  1821  		}
  1822  
  1823  		err = local.ValidateHardLink(filePath)
  1824  		So(err, ShouldNotBeNil)
  1825  	})
  1826  	Convey("Test if filesystem supports hardlink", t, func() {
  1827  		dir := t.TempDir()
  1828  
  1829  		err := local.ValidateHardLink(dir)
  1830  		So(err, ShouldBeNil)
  1831  
  1832  		err = os.WriteFile(path.Join(dir, "hardtest.txt"), []byte("testing hard link code"), 0o644) //nolint: gosec
  1833  		if err != nil {
  1834  			panic(err)
  1835  		}
  1836  
  1837  		err = os.Chmod(dir, 0o400)
  1838  		if err != nil {
  1839  			panic(err)
  1840  		}
  1841  		// Allow hardtest.txt to be cleaned up by t.TempDir()
  1842  		t.Cleanup(func() {
  1843  			err = os.Chmod(dir, 0o700)
  1844  			if err != nil {
  1845  				t.Fatal(err)
  1846  			}
  1847  		})
  1848  
  1849  		err = os.Link(path.Join(dir, "hardtest.txt"), path.Join(dir, "duphardtest.txt"))
  1850  		So(err, ShouldNotBeNil)
  1851  
  1852  		err = os.Chmod(dir, 0o644)
  1853  		if err != nil {
  1854  			panic(err)
  1855  		}
  1856  	})
  1857  }
  1858  
  1859  func TestInjectWriteFile(t *testing.T) {
  1860  	Convey("writeFile without commit", t, func() {
  1861  		dir := t.TempDir()
  1862  
  1863  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  1864  		metrics := monitoring.NewMetricsServer(false, log)
  1865  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1866  			RootDir:     dir,
  1867  			Name:        "cache",
  1868  			UseRelPaths: true,
  1869  		}, log)
  1870  
  1871  		imgStore := local.NewImageStore(dir, true, false, log, metrics, nil, cacheDriver)
  1872  
  1873  		Convey("Failure path not reached", func() {
  1874  			err := imgStore.InitRepo("repo1")
  1875  			So(err, ShouldBeNil)
  1876  		})
  1877  	})
  1878  }
  1879  
  1880  func TestGarbageCollectForImageStore(t *testing.T) {
  1881  	//nolint: contextcheck
  1882  	Convey("Garbage collect for a specific repo from an ImageStore", t, func(c C) {
  1883  		dir := t.TempDir()
  1884  
  1885  		ctx := context.Background()
  1886  
  1887  		Convey("Garbage collect error for repo with config removed", func() {
  1888  			logFile, _ := os.CreateTemp("", "zot-log*.txt")
  1889  
  1890  			defer os.Remove(logFile.Name()) // clean up
  1891  
  1892  			log := zlog.NewLogger("debug", logFile.Name())
  1893  			audit := zlog.NewAuditLogger("debug", "")
  1894  
  1895  			metrics := monitoring.NewMetricsServer(false, log)
  1896  			cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1897  				RootDir:     dir,
  1898  				Name:        "cache",
  1899  				UseRelPaths: true,
  1900  			}, log)
  1901  
  1902  			imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1903  			repoName := "gc-all-repos-short"
  1904  
  1905  			gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{
  1906  				Delay:          1 * time.Second,
  1907  				ImageRetention: DeleteReferrers,
  1908  			}, audit, log)
  1909  
  1910  			image := CreateDefaultVulnerableImage()
  1911  			err := WriteImageToFileSystem(image, repoName, "0.0.1", storage.StoreController{
  1912  				DefaultStore: imgStore,
  1913  			})
  1914  			So(err, ShouldBeNil)
  1915  
  1916  			manifestDigest := image.ManifestDescriptor.Digest
  1917  			err = os.Remove(path.Join(dir, repoName, "blobs/sha256", manifestDigest.Encoded()))
  1918  			if err != nil {
  1919  				panic(err)
  1920  			}
  1921  
  1922  			err = gc.CleanRepo(ctx, repoName)
  1923  			So(err, ShouldNotBeNil)
  1924  
  1925  			time.Sleep(500 * time.Millisecond)
  1926  
  1927  			data, err := os.ReadFile(logFile.Name())
  1928  			So(err, ShouldBeNil)
  1929  			So(string(data), ShouldContainSubstring,
  1930  				fmt.Sprintf("failed to run GC for %s", path.Join(imgStore.RootDir(), repoName)))
  1931  		})
  1932  
  1933  		Convey("Garbage collect error - not enough permissions to access index.json", func() {
  1934  			logFile, _ := os.CreateTemp("", "zot-log*.txt")
  1935  
  1936  			defer os.Remove(logFile.Name()) // clean up
  1937  
  1938  			log := zlog.NewLogger("debug", logFile.Name())
  1939  			audit := zlog.NewAuditLogger("debug", "")
  1940  
  1941  			metrics := monitoring.NewMetricsServer(false, log)
  1942  			cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1943  				RootDir:     dir,
  1944  				Name:        "cache",
  1945  				UseRelPaths: true,
  1946  			}, log)
  1947  
  1948  			imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1949  			repoName := "gc-all-repos-short"
  1950  
  1951  			gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{
  1952  				Delay:          1 * time.Second,
  1953  				ImageRetention: DeleteReferrers,
  1954  			}, audit, log)
  1955  
  1956  			image := CreateDefaultVulnerableImage()
  1957  			err := WriteImageToFileSystem(image, repoName, "0.0.1", storage.StoreController{
  1958  				DefaultStore: imgStore,
  1959  			})
  1960  			So(err, ShouldBeNil)
  1961  
  1962  			So(os.Chmod(path.Join(dir, repoName, "index.json"), 0o000), ShouldBeNil)
  1963  
  1964  			err = gc.CleanRepo(ctx, repoName)
  1965  			So(err, ShouldNotBeNil)
  1966  
  1967  			time.Sleep(500 * time.Millisecond)
  1968  
  1969  			data, err := os.ReadFile(logFile.Name())
  1970  			So(err, ShouldBeNil)
  1971  			So(string(data), ShouldContainSubstring,
  1972  				fmt.Sprintf("failed to run GC for %s", path.Join(imgStore.RootDir(), repoName)))
  1973  			So(os.Chmod(path.Join(dir, repoName, "index.json"), 0o755), ShouldBeNil)
  1974  		})
  1975  
  1976  		Convey("Garbage collect - the manifest which the reference points to can be found", func() {
  1977  			logFile, _ := os.CreateTemp("", "zot-log*.txt")
  1978  
  1979  			defer os.Remove(logFile.Name()) // clean up
  1980  
  1981  			log := zlog.NewLogger("debug", logFile.Name())
  1982  			audit := zlog.NewAuditLogger("debug", "")
  1983  
  1984  			metrics := monitoring.NewMetricsServer(false, log)
  1985  			cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  1986  				RootDir:     dir,
  1987  				Name:        "cache",
  1988  				UseRelPaths: true,
  1989  			}, log)
  1990  			imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  1991  			repoName := "gc-sig"
  1992  
  1993  			gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{
  1994  				Delay:          1 * time.Second,
  1995  				ImageRetention: DeleteReferrers,
  1996  			}, audit, log)
  1997  
  1998  			storeController := storage.StoreController{DefaultStore: imgStore}
  1999  			img := CreateRandomImage()
  2000  
  2001  			err := WriteImageToFileSystem(img, repoName, "tag1", storeController)
  2002  			So(err, ShouldBeNil)
  2003  
  2004  			// add fake signature for tag1
  2005  			cosignTag, err := signature.GetCosignSignatureTagForManifest(img.Manifest)
  2006  			So(err, ShouldBeNil)
  2007  
  2008  			cosignSig := CreateRandomImage()
  2009  			So(err, ShouldBeNil)
  2010  
  2011  			err = WriteImageToFileSystem(cosignSig, repoName, cosignTag, storeController)
  2012  			So(err, ShouldBeNil)
  2013  
  2014  			// add sbom
  2015  			manifestBlob, err := json.Marshal(img.Manifest)
  2016  			So(err, ShouldBeNil)
  2017  
  2018  			manifestDigest := godigest.FromBytes(manifestBlob)
  2019  			sbomTag := fmt.Sprintf("sha256-%s.%s", manifestDigest.Encoded(), "sbom")
  2020  			So(err, ShouldBeNil)
  2021  
  2022  			sbomImg := CreateRandomImage()
  2023  			So(err, ShouldBeNil)
  2024  
  2025  			err = WriteImageToFileSystem(sbomImg, repoName, sbomTag, storeController)
  2026  			So(err, ShouldBeNil)
  2027  
  2028  			// add fake signature for tag1
  2029  			notationSig := CreateImageWith().
  2030  				RandomLayers(1, 10).
  2031  				ArtifactConfig("application/vnd.cncf.notary.signature").
  2032  				Subject(img.DescriptorRef()).Build()
  2033  
  2034  			err = WriteImageToFileSystem(notationSig, repoName, "notation", storeController)
  2035  			So(err, ShouldBeNil)
  2036  
  2037  			// add fake signature for tag1
  2038  			cosignWithReferrersSig := CreateImageWith().
  2039  				RandomLayers(1, 10).
  2040  				ArtifactConfig(common.ArtifactTypeCosign).
  2041  				Subject(img.DescriptorRef()).Build()
  2042  
  2043  			err = WriteImageToFileSystem(cosignWithReferrersSig, repoName, "cosign", storeController)
  2044  			So(err, ShouldBeNil)
  2045  
  2046  			err = gc.CleanRepo(ctx, repoName)
  2047  			So(err, ShouldBeNil)
  2048  		})
  2049  	})
  2050  }
  2051  
  2052  func TestGarbageCollectImageUnknownManifest(t *testing.T) {
  2053  	Convey("Garbage collect with short delay", t, func() {
  2054  		ctx := context.Background()
  2055  
  2056  		dir := t.TempDir()
  2057  
  2058  		log := zlog.NewLogger("debug", "")
  2059  		audit := zlog.NewAuditLogger("debug", "")
  2060  
  2061  		metrics := monitoring.NewMetricsServer(false, log)
  2062  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2063  			RootDir:     dir,
  2064  			Name:        "cache",
  2065  			UseRelPaths: true,
  2066  		}, log)
  2067  
  2068  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2069  
  2070  		storeController := storage.StoreController{
  2071  			DefaultStore: imgStore,
  2072  		}
  2073  
  2074  		gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{
  2075  			Delay:          1 * time.Second,
  2076  			ImageRetention: DeleteReferrers,
  2077  		}, audit, log)
  2078  
  2079  		unsupportedMediaType := "application/vnd.oci.artifact.manifest.v1+json"
  2080  
  2081  		img := CreateRandomImage()
  2082  
  2083  		err := WriteImageToFileSystem(img, repoName, "v1", storeController)
  2084  		So(err, ShouldBeNil)
  2085  
  2086  		// add image with unsupported media type
  2087  		artifact := CreateRandomImage()
  2088  
  2089  		err = WriteImageToFileSystem(artifact, repoName, "artifact", storeController)
  2090  		So(err, ShouldBeNil)
  2091  
  2092  		// add referrer with unsupported media type
  2093  		subjectDesc := img.Descriptor()
  2094  		referrer := CreateRandomImageWith().Subject(&subjectDesc).Build()
  2095  
  2096  		err = WriteImageToFileSystem(referrer, repoName, referrer.Digest().String(), storeController)
  2097  		So(err, ShouldBeNil)
  2098  
  2099  		// modify artifact media type
  2100  		artifactBuf, err := os.ReadFile(imgStore.BlobPath(repoName, artifact.Digest()))
  2101  		So(err, ShouldBeNil)
  2102  
  2103  		var artifactManifest ispec.Manifest
  2104  		err = json.Unmarshal(artifactBuf, &artifactManifest)
  2105  		So(err, ShouldBeNil)
  2106  
  2107  		artifactManifest.MediaType = unsupportedMediaType
  2108  		artifactBuf, err = json.Marshal(artifactManifest)
  2109  		So(err, ShouldBeNil)
  2110  
  2111  		artifactDigest := godigest.FromBytes(artifactBuf)
  2112  		err = os.WriteFile(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", artifactDigest.Encoded()),
  2113  			artifactBuf, storageConstants.DefaultFilePerms)
  2114  		So(err, ShouldBeNil)
  2115  
  2116  		// modify referrer media type
  2117  		referrerBuf, err := os.ReadFile(imgStore.BlobPath(repoName, referrer.Digest()))
  2118  		So(err, ShouldBeNil)
  2119  
  2120  		var referrerManifest ispec.Manifest
  2121  		err = json.Unmarshal(referrerBuf, &referrerManifest)
  2122  		So(err, ShouldBeNil)
  2123  
  2124  		referrerManifest.MediaType = unsupportedMediaType
  2125  		referrerBuf, err = json.Marshal(referrerManifest)
  2126  		So(err, ShouldBeNil)
  2127  
  2128  		referrerDigest := godigest.FromBytes(referrerBuf)
  2129  		err = os.WriteFile(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", referrerDigest.Encoded()),
  2130  			referrerBuf, storageConstants.DefaultFilePerms)
  2131  		So(err, ShouldBeNil)
  2132  
  2133  		indexJSONBuf, err := os.ReadFile(path.Join(imgStore.RootDir(), repoName, "index.json"))
  2134  		So(err, ShouldBeNil)
  2135  
  2136  		var indexJSON ispec.Index
  2137  		err = json.Unmarshal(indexJSONBuf, &indexJSON)
  2138  		So(err, ShouldBeNil)
  2139  
  2140  		for idx, desc := range indexJSON.Manifests {
  2141  			if desc.Digest == artifact.Digest() {
  2142  				indexJSON.Manifests[idx].Digest = artifactDigest
  2143  				indexJSON.Manifests[idx].MediaType = unsupportedMediaType
  2144  			} else if desc.Digest == referrer.Digest() {
  2145  				indexJSON.Manifests[idx].Digest = referrerDigest
  2146  				indexJSON.Manifests[idx].MediaType = unsupportedMediaType
  2147  			}
  2148  		}
  2149  
  2150  		indexJSONBuf, err = json.Marshal(indexJSON)
  2151  		So(err, ShouldBeNil)
  2152  
  2153  		err = os.WriteFile(path.Join(imgStore.RootDir(), repoName, "index.json"),
  2154  			indexJSONBuf, storageConstants.DefaultFilePerms)
  2155  		So(err, ShouldBeNil)
  2156  
  2157  		// sleep so orphan blob can be GC'ed
  2158  		time.Sleep(1 * time.Second)
  2159  
  2160  		Convey("Garbage collect blobs referenced by manifest with unsupported media type", func() {
  2161  			err = gc.CleanRepo(ctx, repoName)
  2162  			So(err, ShouldBeNil)
  2163  
  2164  			_, _, _, err = imgStore.GetImageManifest(repoName, img.DigestStr())
  2165  			So(err, ShouldBeNil)
  2166  
  2167  			hasBlob, _, err := imgStore.CheckBlob(repoName, img.ConfigDescriptor.Digest)
  2168  			So(err, ShouldBeNil)
  2169  			So(hasBlob, ShouldEqual, true)
  2170  
  2171  			_, _, _, err = imgStore.GetImageManifest(repoName, artifactDigest.String())
  2172  			So(err, ShouldNotBeNil)
  2173  
  2174  			_, _, _, err = imgStore.GetImageManifest(repoName, referrerDigest.String())
  2175  			So(err, ShouldNotBeNil)
  2176  
  2177  			hasBlob, _, err = imgStore.CheckBlob(repoName, artifactDigest)
  2178  			So(err, ShouldNotBeNil)
  2179  			So(hasBlob, ShouldEqual, false)
  2180  
  2181  			hasBlob, _, err = imgStore.CheckBlob(repoName, referrerDigest)
  2182  			So(err, ShouldNotBeNil)
  2183  			So(hasBlob, ShouldEqual, false)
  2184  
  2185  			hasBlob, _, err = imgStore.CheckBlob(repoName, artifact.ConfigDescriptor.Digest)
  2186  			So(err, ShouldNotBeNil)
  2187  			So(hasBlob, ShouldEqual, false)
  2188  
  2189  			hasBlob, _, err = imgStore.CheckBlob(repoName, referrer.ConfigDescriptor.Digest)
  2190  			So(err, ShouldNotBeNil)
  2191  			So(hasBlob, ShouldEqual, false)
  2192  		})
  2193  
  2194  		Convey("Garbage collect - gc repo after manifest delete", func() {
  2195  			err = imgStore.DeleteImageManifest(repoName, img.DigestStr(), true)
  2196  			So(err, ShouldBeNil)
  2197  
  2198  			err = gc.CleanRepo(ctx, repoName)
  2199  			So(err, ShouldBeNil)
  2200  
  2201  			_, _, _, err = imgStore.GetImageManifest(repoName, img.DigestStr())
  2202  			So(err, ShouldNotBeNil)
  2203  
  2204  			hasBlob, _, err := imgStore.CheckBlob(repoName, img.ConfigDescriptor.Digest)
  2205  			So(err, ShouldNotBeNil)
  2206  			So(hasBlob, ShouldEqual, false)
  2207  
  2208  			_, _, _, err = imgStore.GetImageManifest(repoName, artifactDigest.String())
  2209  			So(err, ShouldNotBeNil)
  2210  
  2211  			_, _, _, err = imgStore.GetImageManifest(repoName, referrerDigest.String())
  2212  			So(err, ShouldNotBeNil)
  2213  
  2214  			hasBlob, _, err = imgStore.CheckBlob(repoName, artifactDigest)
  2215  			So(err, ShouldNotBeNil)
  2216  			So(hasBlob, ShouldEqual, false)
  2217  
  2218  			hasBlob, _, err = imgStore.CheckBlob(repoName, referrerDigest)
  2219  			So(err, ShouldNotBeNil)
  2220  			So(hasBlob, ShouldEqual, false)
  2221  
  2222  			hasBlob, _, err = imgStore.CheckBlob(repoName, artifact.ConfigDescriptor.Digest)
  2223  			So(err, ShouldNotBeNil)
  2224  			So(hasBlob, ShouldEqual, false)
  2225  
  2226  			hasBlob, _, err = imgStore.CheckBlob(repoName, referrer.ConfigDescriptor.Digest)
  2227  			So(err, ShouldNotBeNil)
  2228  			So(hasBlob, ShouldEqual, false)
  2229  		})
  2230  	})
  2231  }
  2232  
  2233  func TestGarbageCollectErrors(t *testing.T) {
  2234  	Convey("Make image store", t, func(c C) {
  2235  		ctx := context.Background()
  2236  
  2237  		dir := t.TempDir()
  2238  
  2239  		log := zlog.NewLogger("debug", "")
  2240  		audit := zlog.NewAuditLogger("debug", "")
  2241  
  2242  		metrics := monitoring.NewMetricsServer(false, log)
  2243  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2244  			RootDir:     dir,
  2245  			Name:        "cache",
  2246  			UseRelPaths: true,
  2247  		}, log)
  2248  
  2249  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2250  		repoName := "gc-index"
  2251  
  2252  		gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{
  2253  			Delay:          500 * time.Millisecond,
  2254  			ImageRetention: DeleteReferrers,
  2255  		}, audit, log)
  2256  
  2257  		// create a blob/layer
  2258  		upload, err := imgStore.NewBlobUpload(repoName)
  2259  		So(err, ShouldBeNil)
  2260  		So(upload, ShouldNotBeEmpty)
  2261  
  2262  		content := []byte("this is a blob1")
  2263  		buf := bytes.NewBuffer(content)
  2264  		buflen := buf.Len()
  2265  		digest := godigest.FromBytes(content)
  2266  		So(digest, ShouldNotBeNil)
  2267  		blob, err := imgStore.PutBlobChunkStreamed(repoName, upload, buf)
  2268  		So(err, ShouldBeNil)
  2269  		So(blob, ShouldEqual, buflen)
  2270  		bdgst1 := digest
  2271  		bsize1 := len(content)
  2272  
  2273  		err = imgStore.FinishBlobUpload(repoName, upload, buf, digest)
  2274  		So(err, ShouldBeNil)
  2275  		So(blob, ShouldEqual, buflen)
  2276  
  2277  		Convey("Trigger error on GetImageIndex", func() {
  2278  			var index ispec.Index
  2279  			index.SchemaVersion = 2
  2280  			index.MediaType = ispec.MediaTypeImageIndex
  2281  
  2282  			for i := 0; i < 4; i++ {
  2283  				// upload image config blob
  2284  				upload, err = imgStore.NewBlobUpload(repoName)
  2285  				So(err, ShouldBeNil)
  2286  				So(upload, ShouldNotBeEmpty)
  2287  
  2288  				cblob, cdigest := GetRandomImageConfig()
  2289  				buf = bytes.NewBuffer(cblob)
  2290  				buflen = buf.Len()
  2291  				blob, err = imgStore.PutBlobChunkStreamed(repoName, upload, buf)
  2292  				So(err, ShouldBeNil)
  2293  				So(blob, ShouldEqual, buflen)
  2294  
  2295  				err = imgStore.FinishBlobUpload(repoName, upload, buf, cdigest)
  2296  				So(err, ShouldBeNil)
  2297  				So(blob, ShouldEqual, buflen)
  2298  
  2299  				// create a manifest
  2300  				manifest := ispec.Manifest{
  2301  					Config: ispec.Descriptor{
  2302  						MediaType: ispec.MediaTypeImageConfig,
  2303  						Digest:    cdigest,
  2304  						Size:      int64(len(cblob)),
  2305  					},
  2306  					Layers: []ispec.Descriptor{
  2307  						{
  2308  							MediaType: ispec.MediaTypeImageLayer,
  2309  							Digest:    bdgst1,
  2310  							Size:      int64(bsize1),
  2311  						},
  2312  					},
  2313  				}
  2314  				manifest.SchemaVersion = 2
  2315  				content, err = json.Marshal(manifest)
  2316  				So(err, ShouldBeNil)
  2317  				digest = godigest.FromBytes(content)
  2318  				So(digest, ShouldNotBeNil)
  2319  				_, _, err = imgStore.PutImageManifest(repoName, digest.String(), ispec.MediaTypeImageManifest, content)
  2320  				So(err, ShouldBeNil)
  2321  
  2322  				index.Manifests = append(index.Manifests, ispec.Descriptor{
  2323  					Digest:    digest,
  2324  					MediaType: ispec.MediaTypeImageManifest,
  2325  					Size:      int64(len(content)),
  2326  				})
  2327  			}
  2328  
  2329  			// upload index image
  2330  			indexContent, err := json.Marshal(index)
  2331  			So(err, ShouldBeNil)
  2332  			indexDigest := godigest.FromBytes(indexContent)
  2333  			So(indexDigest, ShouldNotBeNil)
  2334  
  2335  			_, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageIndex, indexContent)
  2336  			So(err, ShouldBeNil)
  2337  
  2338  			err = os.Chmod(imgStore.BlobPath(repoName, indexDigest), 0o000)
  2339  			So(err, ShouldBeNil)
  2340  
  2341  			time.Sleep(500 * time.Millisecond)
  2342  
  2343  			err = gc.CleanRepo(ctx, repoName)
  2344  			So(err, ShouldNotBeNil)
  2345  		})
  2346  
  2347  		Convey("Trigger error on GetBlobContent and Unmarshal for untagged manifest", func() {
  2348  			// upload image config blob
  2349  			upload, err = imgStore.NewBlobUpload(repoName)
  2350  			So(err, ShouldBeNil)
  2351  			So(upload, ShouldNotBeEmpty)
  2352  
  2353  			cblob, cdigest := GetRandomImageConfig()
  2354  			buf = bytes.NewBuffer(cblob)
  2355  			buflen = buf.Len()
  2356  			blob, err = imgStore.PutBlobChunkStreamed(repoName, upload, buf)
  2357  			So(err, ShouldBeNil)
  2358  			So(blob, ShouldEqual, buflen)
  2359  
  2360  			err = imgStore.FinishBlobUpload(repoName, upload, buf, cdigest)
  2361  			So(err, ShouldBeNil)
  2362  			So(blob, ShouldEqual, buflen)
  2363  
  2364  			// create a manifest
  2365  			manifest := ispec.Manifest{
  2366  				Config: ispec.Descriptor{
  2367  					MediaType: ispec.MediaTypeImageConfig,
  2368  					Digest:    cdigest,
  2369  					Size:      int64(len(cblob)),
  2370  				},
  2371  				Layers: []ispec.Descriptor{
  2372  					{
  2373  						MediaType: ispec.MediaTypeImageLayer,
  2374  						Digest:    bdgst1,
  2375  						Size:      int64(bsize1),
  2376  					},
  2377  				},
  2378  			}
  2379  			manifest.SchemaVersion = 2
  2380  			content, err = json.Marshal(manifest)
  2381  			So(err, ShouldBeNil)
  2382  			digest = godigest.FromBytes(content)
  2383  			So(digest, ShouldNotBeNil)
  2384  
  2385  			_, _, err = imgStore.PutImageManifest(repoName, digest.String(), ispec.MediaTypeImageManifest, content)
  2386  			So(err, ShouldBeNil)
  2387  
  2388  			// trigger GetBlobContent error
  2389  			err = os.Remove(imgStore.BlobPath(repoName, digest))
  2390  			So(err, ShouldBeNil)
  2391  
  2392  			time.Sleep(500 * time.Millisecond)
  2393  
  2394  			err = gc.CleanRepo(ctx, repoName)
  2395  			So(err, ShouldNotBeNil)
  2396  
  2397  			// trigger Unmarshal error
  2398  			_, err = os.Create(imgStore.BlobPath(repoName, digest))
  2399  			So(err, ShouldBeNil)
  2400  
  2401  			err = gc.CleanRepo(ctx, repoName)
  2402  			So(err, ShouldNotBeNil)
  2403  		})
  2404  
  2405  		Convey("Trigger manifest conflict error", func() {
  2406  			// upload image config blob
  2407  			upload, err = imgStore.NewBlobUpload(repoName)
  2408  			So(err, ShouldBeNil)
  2409  			So(upload, ShouldNotBeEmpty)
  2410  
  2411  			cblob, cdigest := GetRandomImageConfig()
  2412  			buf = bytes.NewBuffer(cblob)
  2413  			buflen = buf.Len()
  2414  			blob, err = imgStore.PutBlobChunkStreamed(repoName, upload, buf)
  2415  			So(err, ShouldBeNil)
  2416  			So(blob, ShouldEqual, buflen)
  2417  
  2418  			err = imgStore.FinishBlobUpload(repoName, upload, buf, cdigest)
  2419  			So(err, ShouldBeNil)
  2420  			So(blob, ShouldEqual, buflen)
  2421  
  2422  			// create a manifest
  2423  			manifest := ispec.Manifest{
  2424  				Config: ispec.Descriptor{
  2425  					MediaType: ispec.MediaTypeImageConfig,
  2426  					Digest:    cdigest,
  2427  					Size:      int64(len(cblob)),
  2428  				},
  2429  				Layers: []ispec.Descriptor{
  2430  					{
  2431  						MediaType: ispec.MediaTypeImageLayer,
  2432  						Digest:    bdgst1,
  2433  						Size:      int64(bsize1),
  2434  					},
  2435  				},
  2436  			}
  2437  			manifest.SchemaVersion = 2
  2438  			content, err = json.Marshal(manifest)
  2439  			So(err, ShouldBeNil)
  2440  			digest = godigest.FromBytes(content)
  2441  			So(digest, ShouldNotBeNil)
  2442  
  2443  			_, _, err = imgStore.PutImageManifest(repoName, digest.String(), ispec.MediaTypeImageManifest, content)
  2444  			So(err, ShouldBeNil)
  2445  			// upload again same manifest so that we trigger manifest conflict
  2446  			_, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, content)
  2447  			So(err, ShouldBeNil)
  2448  
  2449  			time.Sleep(500 * time.Millisecond)
  2450  
  2451  			err = gc.CleanRepo(ctx, repoName)
  2452  			So(err, ShouldBeNil)
  2453  
  2454  			// blob shouldn't be gc'ed //TODO check this one
  2455  			found, _, err := imgStore.CheckBlob(repoName, digest)
  2456  			So(err, ShouldBeNil)
  2457  			So(found, ShouldEqual, true)
  2458  		})
  2459  	})
  2460  }
  2461  
  2462  func randSeq(n int) string {
  2463  	letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
  2464  
  2465  	buf := make([]rune, n)
  2466  	for index := range buf {
  2467  		nBig, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
  2468  		if err != nil {
  2469  			panic(err)
  2470  		}
  2471  
  2472  		buf[index] = letters[int(nBig.Int64())]
  2473  	}
  2474  
  2475  	return string(buf)
  2476  }
  2477  
  2478  func TestInitRepo(t *testing.T) {
  2479  	Convey("Get error when creating BlobUploadDir subdir on initRepo", t, func() {
  2480  		dir := t.TempDir()
  2481  
  2482  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2483  		metrics := monitoring.NewMetricsServer(false, log)
  2484  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2485  			RootDir:     dir,
  2486  			Name:        "cache",
  2487  			UseRelPaths: true,
  2488  		}, log)
  2489  
  2490  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2491  
  2492  		err := os.Mkdir(path.Join(dir, "test-dir"), 0o000)
  2493  		So(err, ShouldBeNil)
  2494  
  2495  		err = imgStore.InitRepo("test-dir")
  2496  		So(err, ShouldNotBeNil)
  2497  	})
  2498  }
  2499  
  2500  func TestValidateRepo(t *testing.T) {
  2501  	Convey("Get error when unable to read directory", t, func() {
  2502  		dir := t.TempDir()
  2503  
  2504  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2505  		metrics := monitoring.NewMetricsServer(false, log)
  2506  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2507  			RootDir:     dir,
  2508  			Name:        "cache",
  2509  			UseRelPaths: true,
  2510  		}, log)
  2511  
  2512  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2513  
  2514  		err := os.Mkdir(path.Join(dir, "test-dir"), 0o000)
  2515  		So(err, ShouldBeNil)
  2516  
  2517  		_, err = imgStore.ValidateRepo("test-dir")
  2518  		So(err, ShouldNotBeNil)
  2519  	})
  2520  
  2521  	Convey("Get error when repo name is not compliant with repo spec", t, func() {
  2522  		dir := t.TempDir()
  2523  
  2524  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2525  		metrics := monitoring.NewMetricsServer(false, log)
  2526  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2527  			RootDir:     dir,
  2528  			Name:        "cache",
  2529  			UseRelPaths: true,
  2530  		}, log)
  2531  
  2532  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2533  
  2534  		_, err := imgStore.ValidateRepo(".")
  2535  		So(err, ShouldNotBeNil)
  2536  		So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue)
  2537  
  2538  		_, err = imgStore.ValidateRepo("..")
  2539  		So(err, ShouldNotBeNil)
  2540  		So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue)
  2541  
  2542  		err = os.Mkdir(path.Join(dir, "_test-dir"), 0o755)
  2543  		So(err, ShouldBeNil)
  2544  
  2545  		_, err = imgStore.ValidateRepo("_test-dir")
  2546  		So(err, ShouldNotBeNil)
  2547  		So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue)
  2548  
  2549  		err = os.Mkdir(path.Join(dir, ".test-dir"), 0o755)
  2550  		So(err, ShouldBeNil)
  2551  
  2552  		_, err = imgStore.ValidateRepo(".test-dir")
  2553  		So(err, ShouldNotBeNil)
  2554  		So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue)
  2555  
  2556  		err = os.Mkdir(path.Join(dir, "-test-dir"), 0o755)
  2557  		So(err, ShouldBeNil)
  2558  
  2559  		_, err = imgStore.ValidateRepo("-test-dir")
  2560  		So(err, ShouldNotBeNil)
  2561  		So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue)
  2562  	})
  2563  }
  2564  
  2565  func TestGetRepositories(t *testing.T) {
  2566  	Convey("Verify errors and repos returned by GetRepositories()", t, func() {
  2567  		dir := t.TempDir()
  2568  
  2569  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2570  		metrics := monitoring.NewMetricsServer(false, log)
  2571  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2572  			RootDir:     dir,
  2573  			Name:        "cache",
  2574  			UseRelPaths: true,
  2575  		}, log)
  2576  
  2577  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2578  
  2579  		// Create valid directory with permissions
  2580  		err := os.Mkdir(path.Join(dir, "test-dir"), 0o755) //nolint: gosec
  2581  		So(err, ShouldBeNil)
  2582  
  2583  		err = os.WriteFile(path.Join(dir, "test-dir/test-file"), []byte("this is test file"), 0o755) //nolint: gosec
  2584  		So(err, ShouldBeNil)
  2585  
  2586  		// Folder is not a repo as it is missing the requires files/subfolder
  2587  		repos, err := imgStore.GetRepositories()
  2588  		So(err, ShouldBeNil)
  2589  		So(len(repos), ShouldEqual, 0)
  2590  
  2591  		il := ispec.ImageLayout{Version: ispec.ImageLayoutVersion}
  2592  		layoutFileContent, err := json.Marshal(il)
  2593  		So(err, ShouldBeNil)
  2594  
  2595  		// Folder becomes a repo after the missing content is added
  2596  		err = os.Mkdir(path.Join(dir, "test-dir", "blobs"), 0o755) //nolint: gosec
  2597  		So(err, ShouldBeNil)
  2598  
  2599  		err = os.Mkdir(path.Join(dir, "test-dir", storageConstants.BlobUploadDir), 0o755) //nolint: gosec
  2600  		So(err, ShouldBeNil)
  2601  
  2602  		err = os.WriteFile(path.Join(dir, "test-dir", "index.json"), []byte{}, 0o755) //nolint: gosec
  2603  		So(err, ShouldBeNil)
  2604  
  2605  		err = os.WriteFile(path.Join(dir, "test-dir", ispec.ImageLayoutFile), layoutFileContent, 0o755) //nolint: gosec
  2606  		So(err, ShouldBeNil)
  2607  
  2608  		// Verify the new repo is turned
  2609  		repos, err = imgStore.GetRepositories()
  2610  		So(err, ShouldBeNil)
  2611  		So(len(repos), ShouldEqual, 1)
  2612  		So(repos[0], ShouldEqual, "test-dir")
  2613  
  2614  		// create directory starting with underscore, which is not OCI a dist spec compliant repo name
  2615  		// [a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*
  2616  		err = os.MkdirAll(path.Join(dir, "_trivy", "db"), 0o755)
  2617  		So(err, ShouldBeNil)
  2618  
  2619  		err = os.WriteFile(path.Join(dir, "_trivy", "db", "trivy.db"), []byte("this is test file"), 0o755) //nolint: gosec
  2620  		So(err, ShouldBeNil)
  2621  
  2622  		// Folder with invalid name is not a repo as it is missing the requires files/subfolder
  2623  		repos, err = imgStore.GetRepositories()
  2624  		So(err, ShouldBeNil)
  2625  		So(len(repos), ShouldEqual, 1)
  2626  		So(repos[0], ShouldEqual, "test-dir")
  2627  
  2628  		// Add missing content to folder with invalid name
  2629  		err = os.Mkdir(path.Join(dir, "_trivy", "blobs"), 0o755) //nolint: gosec
  2630  		So(err, ShouldBeNil)
  2631  
  2632  		err = os.Mkdir(path.Join(dir, "_trivy", storageConstants.BlobUploadDir), 0o755) //nolint: gosec
  2633  		So(err, ShouldBeNil)
  2634  
  2635  		err = os.WriteFile(path.Join(dir, "_trivy", "index.json"), []byte{}, 0o755) //nolint: gosec
  2636  		So(err, ShouldBeNil)
  2637  
  2638  		err = os.WriteFile(path.Join(dir, "_trivy", ispec.ImageLayoutFile), layoutFileContent, 0o755) //nolint: gosec
  2639  		So(err, ShouldBeNil)
  2640  
  2641  		// Folder with invalid name doesn't become a repo after the missing content is added
  2642  		repos, err = imgStore.GetRepositories()
  2643  		So(err, ShouldBeNil)
  2644  		t.Logf("repos %v", repos)
  2645  		So(len(repos), ShouldEqual, 1)
  2646  		So(repos[0], ShouldEqual, "test-dir")
  2647  
  2648  		// Rename folder with invalid name to a valid one
  2649  		err = os.Rename(path.Join(dir, "_trivy"), path.Join(dir, "test-dir-2"))
  2650  		So(err, ShouldBeNil)
  2651  
  2652  		// Verify both repos are now visible
  2653  		repos, err = imgStore.GetRepositories()
  2654  		So(err, ShouldBeNil)
  2655  		t.Logf("repos %v", repos)
  2656  		So(len(repos), ShouldEqual, 2)
  2657  		So(repos, ShouldContain, "test-dir")
  2658  		So(repos, ShouldContain, "test-dir-2")
  2659  	})
  2660  
  2661  	Convey("Verify GetRepositories() doesn't return '.' when having an oci layout as root directory ", t, func() {
  2662  		dir := t.TempDir()
  2663  
  2664  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2665  		metrics := monitoring.NewMetricsServer(false, log)
  2666  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2667  			RootDir:     dir,
  2668  			Name:        "cache",
  2669  			UseRelPaths: true,
  2670  		}, log)
  2671  
  2672  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2673  
  2674  		// Root dir does not contain repos
  2675  		repos, err := imgStore.GetRepositories()
  2676  		So(err, ShouldBeNil)
  2677  		So(len(repos), ShouldEqual, 0)
  2678  
  2679  		// Configure root directory as an oci layout
  2680  		err = os.Mkdir(path.Join(dir, "blobs"), 0o755) //nolint: gosec
  2681  		So(err, ShouldBeNil)
  2682  
  2683  		err = os.Mkdir(path.Join(dir, storageConstants.BlobUploadDir), 0o755) //nolint: gosec
  2684  		So(err, ShouldBeNil)
  2685  
  2686  		err = os.WriteFile(path.Join(dir, "index.json"), []byte{}, 0o755) //nolint: gosec
  2687  		So(err, ShouldBeNil)
  2688  
  2689  		il := ispec.ImageLayout{Version: ispec.ImageLayoutVersion}
  2690  		layoutFileContent, err := json.Marshal(il)
  2691  		So(err, ShouldBeNil)
  2692  
  2693  		err = os.WriteFile(path.Join(dir, ispec.ImageLayoutFile), layoutFileContent, 0o755) //nolint: gosec
  2694  		So(err, ShouldBeNil)
  2695  
  2696  		// Verify root directory is not returned as a repo
  2697  		repos, err = imgStore.GetRepositories()
  2698  		So(err, ShouldBeNil)
  2699  		t.Logf("repos %v", repos)
  2700  		So(len(repos), ShouldEqual, 0)
  2701  	})
  2702  
  2703  	Convey("Verify GetRepositories() doesn't return '..'", t, func() {
  2704  		dir := t.TempDir()
  2705  
  2706  		rootDir := path.Join(dir, "rootDir")
  2707  		err := os.Mkdir(rootDir, 0o755)
  2708  		So(err, ShouldBeNil)
  2709  
  2710  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2711  		metrics := monitoring.NewMetricsServer(false, log)
  2712  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2713  			RootDir:     rootDir,
  2714  			Name:        "cache",
  2715  			UseRelPaths: true,
  2716  		}, log)
  2717  
  2718  		imgStore := local.NewImageStore(rootDir,
  2719  			true, true, log, metrics, nil, cacheDriver,
  2720  		)
  2721  
  2722  		// Root dir does not contain repos
  2723  		repos, err := imgStore.GetRepositories()
  2724  		So(err, ShouldBeNil)
  2725  		So(len(repos), ShouldEqual, 0)
  2726  
  2727  		// Configure parent of root directory as an oci layout
  2728  		err = os.Mkdir(path.Join(dir, "blobs"), 0o755) //nolint: gosec
  2729  		So(err, ShouldBeNil)
  2730  
  2731  		err = os.Mkdir(path.Join(dir, storageConstants.BlobUploadDir), 0o755) //nolint: gosec
  2732  		So(err, ShouldBeNil)
  2733  
  2734  		err = os.WriteFile(path.Join(dir, "index.json"), []byte{}, 0o755) //nolint: gosec
  2735  		So(err, ShouldBeNil)
  2736  
  2737  		il := ispec.ImageLayout{Version: ispec.ImageLayoutVersion}
  2738  		layoutFileContent, err := json.Marshal(il)
  2739  		So(err, ShouldBeNil)
  2740  
  2741  		err = os.WriteFile(path.Join(dir, ispec.ImageLayoutFile), layoutFileContent, 0o755) //nolint: gosec
  2742  		So(err, ShouldBeNil)
  2743  
  2744  		// Verify root directory is not returned as a repo
  2745  		repos, err = imgStore.GetRepositories()
  2746  		So(err, ShouldBeNil)
  2747  		t.Logf("repos %v", repos)
  2748  		So(len(repos), ShouldEqual, 0)
  2749  	})
  2750  }
  2751  
  2752  func TestGetNextRepository(t *testing.T) {
  2753  	dir := t.TempDir()
  2754  	log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2755  	metrics := monitoring.NewMetricsServer(false, log)
  2756  	cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2757  		RootDir:     dir,
  2758  		Name:        "cache",
  2759  		UseRelPaths: true,
  2760  	}, log)
  2761  
  2762  	imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2763  	firstRepoName := "repo1"
  2764  	secondRepoName := "repo2"
  2765  
  2766  	srcStorageCtlr := storage.StoreController{DefaultStore: imgStore}
  2767  	image := CreateDefaultImage()
  2768  
  2769  	err := WriteImageToFileSystem(image, firstRepoName, "0.0.1", srcStorageCtlr)
  2770  	if err != nil {
  2771  		t.Log(err)
  2772  		t.FailNow()
  2773  	}
  2774  
  2775  	err = WriteImageToFileSystem(image, secondRepoName, "0.0.1", srcStorageCtlr)
  2776  	if err != nil {
  2777  		t.Log(err)
  2778  		t.FailNow()
  2779  	}
  2780  
  2781  	Convey("Return first repository", t, func() {
  2782  		firstRepo, err := imgStore.GetNextRepository("")
  2783  		So(firstRepo, ShouldEqual, firstRepoName)
  2784  		So(err, ShouldBeNil)
  2785  	})
  2786  
  2787  	Convey("Return second repository", t, func() {
  2788  		secondRepo, err := imgStore.GetNextRepository(firstRepoName)
  2789  		So(secondRepo, ShouldEqual, secondRepoName)
  2790  		So(err, ShouldBeNil)
  2791  	})
  2792  
  2793  	Convey("Return error", t, func() {
  2794  		err := os.Chmod(imgStore.RootDir(), 0o000)
  2795  		So(err, ShouldBeNil)
  2796  		_, err = imgStore.GetNextRepository(firstRepoName)
  2797  		So(err, ShouldNotBeNil)
  2798  		err = os.Chmod(imgStore.RootDir(), 0o755)
  2799  		So(err, ShouldBeNil)
  2800  	})
  2801  }
  2802  
  2803  func TestPutBlobChunkStreamed(t *testing.T) {
  2804  	Convey("Get error on opening file", t, func() {
  2805  		dir := t.TempDir()
  2806  
  2807  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2808  		metrics := monitoring.NewMetricsServer(false, log)
  2809  		cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2810  			RootDir:     dir,
  2811  			Name:        "cache",
  2812  			UseRelPaths: true,
  2813  		}, log)
  2814  
  2815  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2816  
  2817  		uuid, err := imgStore.NewBlobUpload("test")
  2818  		So(err, ShouldBeNil)
  2819  
  2820  		var reader io.Reader
  2821  		blobPath := imgStore.BlobUploadPath("test", uuid)
  2822  		err = os.Chmod(blobPath, 0o000)
  2823  		So(err, ShouldBeNil)
  2824  
  2825  		_, err = imgStore.PutBlobChunkStreamed("test", uuid, reader)
  2826  		So(err, ShouldNotBeNil)
  2827  	})
  2828  }
  2829  
  2830  func TestPullRange(t *testing.T) {
  2831  	Convey("Repo layout", t, func(c C) {
  2832  		dir := t.TempDir()
  2833  
  2834  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2835  		metrics := monitoring.NewMetricsServer(false, log)
  2836  
  2837  		Convey("Negative cases", func() {
  2838  			cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2839  				RootDir:     dir,
  2840  				Name:        "cache",
  2841  				UseRelPaths: true,
  2842  			}, log)
  2843  
  2844  			imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2845  			repoName := "pull-range"
  2846  
  2847  			upload, err := imgStore.NewBlobUpload(repoName)
  2848  			So(err, ShouldBeNil)
  2849  			So(upload, ShouldNotBeEmpty)
  2850  
  2851  			content := []byte("test-data1")
  2852  			buf := bytes.NewBuffer(content)
  2853  			buflen := buf.Len()
  2854  			bdigest := godigest.FromBytes(content)
  2855  
  2856  			blob, err := imgStore.PutBlobChunk(repoName, upload, 0, int64(buflen), buf)
  2857  			So(err, ShouldBeNil)
  2858  			So(blob, ShouldEqual, buflen)
  2859  
  2860  			err = imgStore.FinishBlobUpload(repoName, upload, buf, bdigest)
  2861  			So(err, ShouldBeNil)
  2862  
  2863  			_, _, _, err = imgStore.GetBlobPartial(repoName, "", "application/octet-stream", 0, 1)
  2864  			So(err, ShouldNotBeNil)
  2865  
  2866  			_, _, _, err = imgStore.GetBlobPartial(repoName, bdigest, "application/octet-stream", 1, 0)
  2867  			So(err, ShouldNotBeNil)
  2868  
  2869  			_, _, _, err = imgStore.GetBlobPartial(repoName, bdigest, "application/octet-stream", 1, 0)
  2870  			So(err, ShouldNotBeNil)
  2871  
  2872  			blobPath := path.Join(imgStore.RootDir(), repoName, "blobs", bdigest.Algorithm().String(), bdigest.Encoded())
  2873  			err = os.Chmod(blobPath, 0o000)
  2874  			So(err, ShouldBeNil)
  2875  			_, _, _, err = imgStore.GetBlobPartial(repoName, bdigest, "application/octet-stream", -1, 1)
  2876  			So(err, ShouldNotBeNil)
  2877  		})
  2878  	})
  2879  }
  2880  
  2881  func TestStatIndex(t *testing.T) {
  2882  	Convey("NewImageStore", t, func() {
  2883  		dir := t.TempDir()
  2884  		log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2885  		metrics := monitoring.NewMetricsServer(false, log)
  2886  		imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, nil)
  2887  
  2888  		err := WriteImageToFileSystem(CreateRandomImage(), "repo", "tag",
  2889  			storage.StoreController{DefaultStore: imgStore})
  2890  		So(err, ShouldBeNil)
  2891  
  2892  		Convey("StatIndex PathNotFoundError", func() {
  2893  			_, _, _, err := imgStore.StatIndex("not-found")
  2894  			So(err, ShouldNotBeNil)
  2895  		})
  2896  	})
  2897  }
  2898  
  2899  func TestStorageDriverErr(t *testing.T) {
  2900  	dir := t.TempDir()
  2901  
  2902  	log := zlog.Logger{Logger: zerolog.New(os.Stdout)}
  2903  	metrics := monitoring.NewMetricsServer(false, log)
  2904  	cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
  2905  		RootDir:     dir,
  2906  		Name:        "cache",
  2907  		UseRelPaths: true,
  2908  	}, log)
  2909  
  2910  	imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver)
  2911  
  2912  	Convey("Init repo", t, func() {
  2913  		err := imgStore.InitRepo(repoName)
  2914  		So(err, ShouldBeNil)
  2915  
  2916  		Convey("New blob upload error", func() {
  2917  			err := os.Chmod(path.Join(imgStore.RootDir(), repoName, storageConstants.BlobUploadDir), 0o000)
  2918  			So(err, ShouldBeNil)
  2919  
  2920  			_, err = imgStore.NewBlobUpload(repoName)
  2921  			So(err, ShouldNotBeNil)
  2922  
  2923  			err = os.Chmod(path.Join(imgStore.RootDir(), repoName, storageConstants.BlobUploadDir),
  2924  				storageConstants.DefaultDirPerms)
  2925  			So(err, ShouldBeNil)
  2926  
  2927  			uuid, err := imgStore.NewBlobUpload(repoName)
  2928  			So(err, ShouldBeNil)
  2929  
  2930  			size, err := imgStore.GetBlobUpload(repoName, uuid)
  2931  			So(err, ShouldBeNil)
  2932  			So(size, ShouldEqual, 0)
  2933  
  2934  			content := []byte("test-blob")
  2935  			buf := bytes.NewBuffer(content)
  2936  			bufLen := buf.Len()
  2937  			digest := godigest.FromBytes(content)
  2938  
  2939  			size, err = imgStore.PutBlobChunkStreamed(repoName, uuid, buf)
  2940  			So(err, ShouldBeNil)
  2941  			So(size, ShouldEqual, bufLen)
  2942  
  2943  			size, err = imgStore.GetBlobUpload(repoName, uuid)
  2944  			So(err, ShouldBeNil)
  2945  			So(size, ShouldEqual, bufLen)
  2946  
  2947  			err = imgStore.DeleteBlobUpload(repoName, uuid)
  2948  			So(err, ShouldBeNil)
  2949  
  2950  			err = imgStore.DeleteBlobUpload(repoName, uuid)
  2951  			So(err, ShouldNotBeNil)
  2952  
  2953  			_, err = imgStore.GetBlobUpload(repoName, uuid)
  2954  			So(err, ShouldNotBeNil)
  2955  
  2956  			// push again
  2957  			buf = bytes.NewBuffer(content)
  2958  
  2959  			uuid, err = imgStore.NewBlobUpload(repoName)
  2960  			So(err, ShouldBeNil)
  2961  
  2962  			size, err = imgStore.PutBlobChunkStreamed(repoName, uuid, buf)
  2963  			So(err, ShouldBeNil)
  2964  			So(size, ShouldEqual, bufLen)
  2965  
  2966  			// finish blob upload
  2967  			err = os.Chmod(path.Join(imgStore.BlobUploadPath(repoName, uuid)), 0o000)
  2968  			So(err, ShouldBeNil)
  2969  
  2970  			err = imgStore.FinishBlobUpload(repoName, uuid, &io.LimitedReader{}, digest)
  2971  			So(err, ShouldNotBeNil)
  2972  
  2973  			err = os.Chmod(path.Join(imgStore.BlobUploadPath(repoName, uuid)), storageConstants.DefaultFilePerms)
  2974  			So(err, ShouldBeNil)
  2975  
  2976  			err = imgStore.FinishBlobUpload(repoName, uuid, &io.LimitedReader{}, digest)
  2977  			So(err, ShouldBeNil)
  2978  
  2979  			err = imgStore.FinishBlobUpload(repoName, uuid, &io.LimitedReader{}, digest)
  2980  			So(err, ShouldNotBeNil)
  2981  
  2982  			// delete blob
  2983  			err = imgStore.DeleteBlob(repoName, digest)
  2984  			So(err, ShouldBeNil)
  2985  
  2986  			err = imgStore.DeleteBlob(repoName, digest)
  2987  			So(err, ShouldNotBeNil)
  2988  		})
  2989  	})
  2990  }
  2991  
  2992  func NewRandomImgManifest(data []byte, cdigest, ldigest godigest.Digest, cblob, lblob []byte) (*ispec.Manifest, error) {
  2993  	annotationsMap := make(map[string]string)
  2994  
  2995  	key := string(data)
  2996  	val := string(data)
  2997  	annotationsMap[key] = val
  2998  
  2999  	schemaVersion := 2
  3000  
  3001  	manifest := ispec.Manifest{
  3002  		MediaType: "application/vnd.oci.image.manifest.v1+json",
  3003  		Config: ispec.Descriptor{
  3004  			MediaType: "application/vnd.oci.image.config.v1+json",
  3005  			Digest:    cdigest,
  3006  			Size:      int64(len(cblob)),
  3007  		},
  3008  		Layers: []ispec.Descriptor{
  3009  			{
  3010  				MediaType: "application/vnd.oci.image.layer.v1.tar",
  3011  				Digest:    ldigest,
  3012  				Size:      int64(len(lblob)),
  3013  			},
  3014  		},
  3015  		Annotations: annotationsMap,
  3016  		Versioned: imeta.Versioned{
  3017  			SchemaVersion: schemaVersion,
  3018  		},
  3019  	}
  3020  
  3021  	return &manifest, nil
  3022  }
  3023  
  3024  func newRandomBlobForFuzz(data []byte) (godigest.Digest, []byte, error) { //nolint:unparam
  3025  	return godigest.FromBytes(data), data, nil
  3026  }
  3027  
  3028  func isKnownErr(err error) bool {
  3029  	if errors.Is(err, zerr.ErrInvalidRepositoryName) || errors.Is(err, zerr.ErrManifestNotFound) ||
  3030  		errors.Is(err, zerr.ErrRepoNotFound) ||
  3031  		errors.Is(err, zerr.ErrBadManifest) {
  3032  		return true
  3033  	}
  3034  
  3035  	if err, ok := err.(*fs.PathError); ok && errors.Is(err.Err, syscall.EACCES) || //nolint: errorlint
  3036  		errors.Is(err.Err, syscall.ENAMETOOLONG) ||
  3037  		errors.Is(err.Err, syscall.EINVAL) ||
  3038  		errors.Is(err.Err, syscall.ENOENT) {
  3039  		return true
  3040  	}
  3041  
  3042  	return false
  3043  }