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