github.com/anuvu/zot@v1.3.4/pkg/storage/storage_fs_test.go (about)

     1  package storage_test
     2  
     3  import (
     4  	"bytes"
     5  	_ "crypto/sha256"
     6  	"encoding/json"
     7  	"io/ioutil"
     8  	"math/rand"
     9  	"os"
    10  	"os/exec"
    11  	"path"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/anuvu/zot/errors"
    17  	"github.com/anuvu/zot/pkg/extensions/monitoring"
    18  	"github.com/anuvu/zot/pkg/log"
    19  	"github.com/anuvu/zot/pkg/storage"
    20  	godigest "github.com/opencontainers/go-digest"
    21  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    22  	"github.com/rs/zerolog"
    23  	. "github.com/smartystreets/goconvey/convey"
    24  )
    25  
    26  func TestStorageFSAPIs(t *testing.T) {
    27  	dir, err := ioutil.TempDir("", "oci-repo-test")
    28  	if err != nil {
    29  		panic(err)
    30  	}
    31  
    32  	defer os.RemoveAll(dir)
    33  
    34  	log := log.Logger{Logger: zerolog.New(os.Stdout)}
    35  	metrics := monitoring.NewMetricsServer(false, log)
    36  	il := storage.NewImageStore(dir, true, true, log, metrics)
    37  
    38  	Convey("Repo layout", t, func(c C) {
    39  		repoName := "test"
    40  
    41  		Convey("Bad image manifest", func() {
    42  			v, err := il.NewBlobUpload("test")
    43  			So(err, ShouldBeNil)
    44  			So(v, ShouldNotBeEmpty)
    45  
    46  			content := []byte("test-data1")
    47  			buf := bytes.NewBuffer(content)
    48  			l := buf.Len()
    49  			d := godigest.FromBytes(content)
    50  
    51  			b, err := il.PutBlobChunk(repoName, v, 0, int64(l), buf)
    52  			So(err, ShouldBeNil)
    53  			So(b, ShouldEqual, l)
    54  
    55  			err = il.FinishBlobUpload("test", v, buf, d.String())
    56  			So(err, ShouldBeNil)
    57  
    58  			annotationsMap := make(map[string]string)
    59  			annotationsMap[ispec.AnnotationRefName] = "1.0"
    60  			m := ispec.Manifest{
    61  				Config: ispec.Descriptor{
    62  					Digest: d,
    63  					Size:   int64(l),
    64  				},
    65  				Layers: []ispec.Descriptor{
    66  					{
    67  						MediaType: "application/vnd.oci.image.layer.v1.tar",
    68  						Digest:    d,
    69  						Size:      int64(l),
    70  					},
    71  				},
    72  				Annotations: annotationsMap,
    73  			}
    74  
    75  			m.SchemaVersion = 2
    76  			mb, _ := json.Marshal(m)
    77  			d = godigest.FromBytes(mb)
    78  
    79  			err = os.Chmod(path.Join(il.RootDir(), repoName, "index.json"), 0000)
    80  			if err != nil {
    81  				panic(err)
    82  			}
    83  
    84  			_, err = il.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, mb)
    85  			So(err, ShouldNotBeNil)
    86  
    87  			err = os.Chmod(path.Join(il.RootDir(), repoName, "index.json"), 0755)
    88  			if err != nil {
    89  				panic(err)
    90  			}
    91  
    92  			_, err = il.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, mb)
    93  			So(err, ShouldBeNil)
    94  
    95  			manifestPath := path.Join(il.RootDir(), repoName, "blobs", d.Algorithm().String(), d.Encoded())
    96  
    97  			err = os.Chmod(manifestPath, 0000)
    98  			if err != nil {
    99  				panic(err)
   100  			}
   101  
   102  			_, _, _, err = il.GetImageManifest(repoName, d.String())
   103  			So(err, ShouldNotBeNil)
   104  
   105  			err = os.Remove(manifestPath)
   106  			if err != nil {
   107  				panic(err)
   108  			}
   109  
   110  			_, _, _, err = il.GetImageManifest(repoName, d.String())
   111  			So(err, ShouldNotBeNil)
   112  
   113  			err = os.Chmod(path.Join(il.RootDir(), repoName), 0000)
   114  			if err != nil {
   115  				panic(err)
   116  			}
   117  
   118  			_, err = il.PutImageManifest(repoName, "2.0", ispec.MediaTypeImageManifest, mb)
   119  			So(err, ShouldNotBeNil)
   120  			err = os.Chmod(path.Join(il.RootDir(), repoName), 0755)
   121  			if err != nil {
   122  				panic(err)
   123  			}
   124  
   125  			// invalid GetReferrers
   126  			_, err = il.GetReferrers("invalid", "invalid", "invalid")
   127  			So(err, ShouldNotBeNil)
   128  
   129  			_, err = il.GetReferrers(repoName, "invalid", "invalid")
   130  			So(err, ShouldNotBeNil)
   131  
   132  			_, err = il.GetReferrers(repoName, d.String(), "invalid")
   133  			So(err, ShouldNotBeNil)
   134  
   135  			// invalid DeleteImageManifest
   136  			indexPath := path.Join(il.RootDir(), repoName, "index.json")
   137  			err = os.Chmod(indexPath, 0000)
   138  			if err != nil {
   139  				panic(err)
   140  			}
   141  
   142  			err = il.DeleteImageManifest(repoName, d.String())
   143  			So(err, ShouldNotBeNil)
   144  
   145  			err = os.RemoveAll(path.Join(il.RootDir(), repoName))
   146  			if err != nil {
   147  				panic(err)
   148  			}
   149  		})
   150  	})
   151  }
   152  
   153  func TestDedupeLinks(t *testing.T) {
   154  	dir, err := ioutil.TempDir("", "oci-repo-test")
   155  	if err != nil {
   156  		panic(err)
   157  	}
   158  
   159  	defer os.RemoveAll(dir)
   160  
   161  	log := log.Logger{Logger: zerolog.New(os.Stdout)}
   162  	metrics := monitoring.NewMetricsServer(false, log)
   163  	il := storage.NewImageStore(dir, true, true, log, metrics)
   164  
   165  	Convey("Dedupe", t, func(c C) {
   166  		blobDigest1 := ""
   167  		blobDigest2 := ""
   168  
   169  		// manifest1
   170  		v, err := il.NewBlobUpload("dedupe1")
   171  		So(err, ShouldBeNil)
   172  		So(v, ShouldNotBeEmpty)
   173  
   174  		content := []byte("test-data3")
   175  		buf := bytes.NewBuffer(content)
   176  		l := buf.Len()
   177  		d := godigest.FromBytes(content)
   178  		b, err := il.PutBlobChunkStreamed("dedupe1", v, buf)
   179  		So(err, ShouldBeNil)
   180  		So(b, ShouldEqual, l)
   181  		blobDigest1 = strings.Split(d.String(), ":")[1]
   182  		So(blobDigest1, ShouldNotBeEmpty)
   183  
   184  		err = il.FinishBlobUpload("dedupe1", v, buf, d.String())
   185  		So(err, ShouldBeNil)
   186  		So(b, ShouldEqual, l)
   187  
   188  		_, _, err = il.CheckBlob("dedupe1", d.String())
   189  		So(err, ShouldBeNil)
   190  
   191  		_, _, err = il.GetBlob("dedupe1", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
   192  		So(err, ShouldBeNil)
   193  
   194  		m := ispec.Manifest{}
   195  		m.SchemaVersion = 2
   196  		m = ispec.Manifest{
   197  			Config: ispec.Descriptor{
   198  				Digest: d,
   199  				Size:   int64(l),
   200  			},
   201  			Layers: []ispec.Descriptor{
   202  				{
   203  					MediaType: "application/vnd.oci.image.layer.v1.tar",
   204  					Digest:    d,
   205  					Size:      int64(l),
   206  				},
   207  			},
   208  		}
   209  		m.SchemaVersion = 2
   210  		mb, _ := json.Marshal(m)
   211  		d = godigest.FromBytes(mb)
   212  		_, err = il.PutImageManifest("dedupe1", d.String(), ispec.MediaTypeImageManifest, mb)
   213  		So(err, ShouldBeNil)
   214  
   215  		_, _, _, err = il.GetImageManifest("dedupe1", d.String())
   216  		So(err, ShouldBeNil)
   217  
   218  		// manifest2
   219  		v, err = il.NewBlobUpload("dedupe2")
   220  		So(err, ShouldBeNil)
   221  		So(v, ShouldNotBeEmpty)
   222  
   223  		content = []byte("test-data3")
   224  		buf = bytes.NewBuffer(content)
   225  		l = buf.Len()
   226  		d = godigest.FromBytes(content)
   227  		b, err = il.PutBlobChunkStreamed("dedupe2", v, buf)
   228  		So(err, ShouldBeNil)
   229  		So(b, ShouldEqual, l)
   230  		blobDigest2 = strings.Split(d.String(), ":")[1]
   231  		So(blobDigest2, ShouldNotBeEmpty)
   232  
   233  		err = il.FinishBlobUpload("dedupe2", v, buf, d.String())
   234  		So(err, ShouldBeNil)
   235  		So(b, ShouldEqual, l)
   236  
   237  		_, _, err = il.CheckBlob("dedupe2", d.String())
   238  		So(err, ShouldBeNil)
   239  
   240  		_, _, err = il.GetBlob("dedupe2", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
   241  		So(err, ShouldBeNil)
   242  
   243  		m = ispec.Manifest{}
   244  		m.SchemaVersion = 2
   245  		m = ispec.Manifest{
   246  			Config: ispec.Descriptor{
   247  				Digest: d,
   248  				Size:   int64(l),
   249  			},
   250  			Layers: []ispec.Descriptor{
   251  				{
   252  					MediaType: "application/vnd.oci.image.layer.v1.tar",
   253  					Digest:    d,
   254  					Size:      int64(l),
   255  				},
   256  			},
   257  		}
   258  		m.SchemaVersion = 2
   259  		mb, _ = json.Marshal(m)
   260  		d = godigest.FromBytes(mb)
   261  		_, err = il.PutImageManifest("dedupe2", "1.0", ispec.MediaTypeImageManifest, mb)
   262  		So(err, ShouldBeNil)
   263  
   264  		_, _, _, err = il.GetImageManifest("dedupe2", d.String())
   265  		So(err, ShouldBeNil)
   266  
   267  		// verify that dedupe with hard links happened
   268  		fi1, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest1))
   269  		So(err, ShouldBeNil)
   270  		fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2))
   271  		So(err, ShouldBeNil)
   272  		So(os.SameFile(fi1, fi2), ShouldBeTrue)
   273  	})
   274  }
   275  
   276  func TestDedupe(t *testing.T) {
   277  	Convey("Dedupe", t, func(c C) {
   278  		Convey("Nil ImageStore", func() {
   279  			var is storage.ImageStore
   280  			So(func() { _ = is.DedupeBlob("", "", "") }, ShouldPanic)
   281  		})
   282  
   283  		Convey("Valid ImageStore", func() {
   284  			dir, err := ioutil.TempDir("", "oci-repo-test")
   285  			if err != nil {
   286  				panic(err)
   287  			}
   288  			defer os.RemoveAll(dir)
   289  
   290  			log := log.Logger{Logger: zerolog.New(os.Stdout)}
   291  			metrics := monitoring.NewMetricsServer(false, log)
   292  			il := storage.NewImageStore(dir, true, true, log, metrics)
   293  
   294  			So(il.DedupeBlob("", "", ""), ShouldNotBeNil)
   295  		})
   296  	})
   297  }
   298  
   299  // nolint: gocyclo
   300  func TestNegativeCases(t *testing.T) {
   301  	Convey("Invalid root dir", t, func(c C) {
   302  		dir, err := ioutil.TempDir("", "oci-repo-test")
   303  		if err != nil {
   304  			panic(err)
   305  		}
   306  		os.RemoveAll(dir)
   307  
   308  		log := log.Logger{Logger: zerolog.New(os.Stdout)}
   309  		metrics := monitoring.NewMetricsServer(false, log)
   310  
   311  		So(storage.NewImageStore(dir, true, true, log, metrics), ShouldNotBeNil)
   312  		if os.Geteuid() != 0 {
   313  			So(storage.NewImageStore("/deadBEEF", true, true, log, metrics), ShouldBeNil)
   314  		}
   315  	})
   316  
   317  	Convey("Invalid init repo", t, func(c C) {
   318  		dir, err := ioutil.TempDir("", "oci-repo-test")
   319  		if err != nil {
   320  			panic(err)
   321  		}
   322  		defer os.RemoveAll(dir)
   323  
   324  		log := log.Logger{Logger: zerolog.New(os.Stdout)}
   325  		metrics := monitoring.NewMetricsServer(false, log)
   326  		il := storage.NewImageStore(dir, true, true, log, metrics)
   327  
   328  		err = os.Chmod(dir, 0000) // remove all perms
   329  		if err != nil {
   330  			panic(err)
   331  		}
   332  
   333  		if os.Geteuid() != 0 {
   334  			err = il.InitRepo("test")
   335  			So(err, ShouldNotBeNil)
   336  		}
   337  
   338  		err = os.Chmod(dir, 0755)
   339  		if err != nil {
   340  			panic(err)
   341  		}
   342  
   343  		// Init repo should fail if repo is a file.
   344  		err = ioutil.WriteFile(path.Join(dir, "file-test"), []byte("this is test file"), 0755) // nolint:gosec
   345  		So(err, ShouldBeNil)
   346  		err = il.InitRepo("file-test")
   347  		So(err, ShouldNotBeNil)
   348  
   349  		err = os.Mkdir(path.Join(dir, "test-dir"), 0755)
   350  		So(err, ShouldBeNil)
   351  
   352  		err = il.InitRepo("test-dir")
   353  		So(err, ShouldBeNil)
   354  	})
   355  
   356  	Convey("Invalid validate repo", t, func(c C) {
   357  		dir, err := ioutil.TempDir("", "oci-repo-test")
   358  		if err != nil {
   359  			panic(err)
   360  		}
   361  		defer os.RemoveAll(dir)
   362  
   363  		log := log.Logger{Logger: zerolog.New(os.Stdout)}
   364  		metrics := monitoring.NewMetricsServer(false, log)
   365  		il := storage.NewImageStore(dir, true, true, log, metrics)
   366  
   367  		So(il, ShouldNotBeNil)
   368  		So(il.InitRepo("test"), ShouldBeNil)
   369  
   370  		err = os.MkdirAll(path.Join(dir, "invalid-test"), 0755)
   371  		So(err, ShouldBeNil)
   372  
   373  		err = os.Chmod(path.Join(dir, "invalid-test"), 0000) // remove all perms
   374  		if err != nil {
   375  			panic(err)
   376  		}
   377  		_, err = il.ValidateRepo("invalid-test")
   378  		So(err, ShouldNotBeNil)
   379  		So(err, ShouldEqual, errors.ErrRepoNotFound)
   380  
   381  		err = os.Chmod(path.Join(dir, "invalid-test"), 0755) // remove all perms
   382  		if err != nil {
   383  			panic(err)
   384  		}
   385  
   386  		err = ioutil.WriteFile(path.Join(dir, "invalid-test", "blobs"), []byte{}, 0755) // nolint: gosec
   387  		if err != nil {
   388  			panic(err)
   389  		}
   390  
   391  		err = ioutil.WriteFile(path.Join(dir, "invalid-test", "index.json"), []byte{}, 0755) // nolint: gosec
   392  		if err != nil {
   393  			panic(err)
   394  		}
   395  
   396  		err = ioutil.WriteFile(path.Join(dir, "invalid-test", ispec.ImageLayoutFile), []byte{}, 0755) // nolint: gosec
   397  		if err != nil {
   398  			panic(err)
   399  		}
   400  
   401  		isValid, err := il.ValidateRepo("invalid-test")
   402  		So(err, ShouldBeNil)
   403  		So(isValid, ShouldEqual, false)
   404  
   405  		err = os.Remove(path.Join(dir, "invalid-test", "blobs"))
   406  		if err != nil {
   407  			panic(err)
   408  		}
   409  		err = os.Mkdir(path.Join(dir, "invalid-test", "blobs"), 0755)
   410  		if err != nil {
   411  			panic(err)
   412  		}
   413  		isValid, err = il.ValidateRepo("invalid-test")
   414  		So(err, ShouldNotBeNil)
   415  		So(isValid, ShouldEqual, false)
   416  
   417  		err = ioutil.WriteFile(path.Join(dir, "invalid-test", ispec.ImageLayoutFile), []byte("{}"), 0755) // nolint: gosec
   418  		if err != nil {
   419  			panic(err)
   420  		}
   421  
   422  		isValid, err = il.ValidateRepo("invalid-test")
   423  		So(err, ShouldNotBeNil)
   424  		So(err, ShouldEqual, errors.ErrRepoBadVersion)
   425  		So(isValid, ShouldEqual, false)
   426  
   427  		files, err := ioutil.ReadDir(path.Join(dir, "test"))
   428  		if err != nil {
   429  			panic(err)
   430  		}
   431  
   432  		for _, f := range files {
   433  			os.Remove(path.Join(dir, "test", f.Name()))
   434  		}
   435  
   436  		_, err = il.ValidateRepo("test")
   437  		So(err, ShouldNotBeNil)
   438  
   439  		err = os.RemoveAll(path.Join(dir, "test"))
   440  		if err != nil {
   441  			panic(err)
   442  		}
   443  
   444  		_, err = il.ValidateRepo("test")
   445  		So(err, ShouldNotBeNil)
   446  
   447  		err = os.Chmod(dir, 0000) // remove all perms
   448  		if err != nil {
   449  			panic(err)
   450  		}
   451  
   452  		if os.Geteuid() != 0 {
   453  			So(func() { _, _ = il.ValidateRepo("test") }, ShouldPanic)
   454  		}
   455  
   456  		err = os.Chmod(dir, 0755) // remove all perms
   457  		if err != nil {
   458  			panic(err)
   459  		}
   460  
   461  		err = os.RemoveAll(dir)
   462  		if err != nil {
   463  			panic(err)
   464  		}
   465  
   466  		_, err = il.GetRepositories()
   467  		So(err, ShouldNotBeNil)
   468  	})
   469  
   470  	Convey("Invalid get image tags", t, func(c C) {
   471  		var ilfs storage.ImageStoreFS
   472  		_, err := ilfs.GetImageTags("test")
   473  		So(err, ShouldNotBeNil)
   474  
   475  		dir, err := ioutil.TempDir("", "oci-repo-test")
   476  		if err != nil {
   477  			panic(err)
   478  		}
   479  		defer os.RemoveAll(dir)
   480  
   481  		log := log.Logger{Logger: zerolog.New(os.Stdout)}
   482  		metrics := monitoring.NewMetricsServer(false, log)
   483  		il := storage.NewImageStore(dir, true, true, log, metrics)
   484  
   485  		So(il, ShouldNotBeNil)
   486  		So(il.InitRepo("test"), ShouldBeNil)
   487  		So(os.Remove(path.Join(dir, "test", "index.json")), ShouldBeNil)
   488  		_, err = il.GetImageTags("test")
   489  		So(err, ShouldNotBeNil)
   490  		So(os.RemoveAll(path.Join(dir, "test")), ShouldBeNil)
   491  		So(il.InitRepo("test"), ShouldBeNil)
   492  		So(ioutil.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0600), ShouldBeNil)
   493  		_, err = il.GetImageTags("test")
   494  		So(err, ShouldNotBeNil)
   495  	})
   496  
   497  	Convey("Invalid get image manifest", t, func(c C) {
   498  		var ilfs storage.ImageStoreFS
   499  		_, _, _, err := ilfs.GetImageManifest("test", "")
   500  		So(err, ShouldNotBeNil)
   501  
   502  		dir, err := ioutil.TempDir("", "oci-repo-test")
   503  		if err != nil {
   504  			panic(err)
   505  		}
   506  		defer os.RemoveAll(dir)
   507  
   508  		log := log.Logger{Logger: zerolog.New(os.Stdout)}
   509  		metrics := monitoring.NewMetricsServer(false, log)
   510  		il := storage.NewImageStore(dir, true, true, log, metrics)
   511  
   512  		So(il, ShouldNotBeNil)
   513  		So(il.InitRepo("test"), ShouldBeNil)
   514  
   515  		err = os.Chmod(path.Join(dir, "test", "index.json"), 0000)
   516  		if err != nil {
   517  			panic(err)
   518  		}
   519  
   520  		_, _, _, err = il.GetImageManifest("test", "")
   521  		So(err, ShouldNotBeNil)
   522  
   523  		err = os.Remove(path.Join(dir, "test", "index.json"))
   524  		if err != nil {
   525  			panic(err)
   526  		}
   527  
   528  		_, _, _, err = il.GetImageManifest("test", "")
   529  		So(err, ShouldNotBeNil)
   530  
   531  		err = os.RemoveAll(path.Join(dir, "test"))
   532  		if err != nil {
   533  			panic(err)
   534  		}
   535  
   536  		So(il.InitRepo("test"), ShouldBeNil)
   537  
   538  		err = ioutil.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0600)
   539  		if err != nil {
   540  			panic(err)
   541  		}
   542  		_, _, _, err = il.GetImageManifest("test", "")
   543  		So(err, ShouldNotBeNil)
   544  	})
   545  
   546  	Convey("Invalid new blob upload", t, func(c C) {
   547  		dir, err := ioutil.TempDir("", "oci-repo-test")
   548  		if err != nil {
   549  			panic(err)
   550  		}
   551  		defer os.RemoveAll(dir)
   552  
   553  		log := log.Logger{Logger: zerolog.New(os.Stdout)}
   554  		metrics := monitoring.NewMetricsServer(false, log)
   555  		il := storage.NewImageStore(dir, true, true, log, metrics)
   556  
   557  		So(il, ShouldNotBeNil)
   558  		So(il.InitRepo("test"), ShouldBeNil)
   559  
   560  		err = os.Chmod(path.Join(dir, "test", ".uploads"), 0000)
   561  		if err != nil {
   562  			panic(err)
   563  		}
   564  		_, err = il.NewBlobUpload("test")
   565  		So(err, ShouldNotBeNil)
   566  
   567  		err = os.Chmod(path.Join(dir, "test"), 0000)
   568  		if err != nil {
   569  			panic(err)
   570  		}
   571  
   572  		_, err = il.NewBlobUpload("test")
   573  		So(err, ShouldNotBeNil)
   574  
   575  		err = os.Chmod(path.Join(dir, "test"), 0755)
   576  		if err != nil {
   577  			panic(err)
   578  		}
   579  
   580  		So(il.InitRepo("test"), ShouldBeNil)
   581  
   582  		_, err = il.NewBlobUpload("test")
   583  		So(err, ShouldNotBeNil)
   584  
   585  		err = os.Chmod(path.Join(dir, "test", ".uploads"), 0755)
   586  		if err != nil {
   587  			panic(err)
   588  		}
   589  
   590  		v, err := il.NewBlobUpload("test")
   591  		So(err, ShouldBeNil)
   592  
   593  		err = os.Chmod(path.Join(dir, "test", ".uploads"), 0000)
   594  		if err != nil {
   595  			panic(err)
   596  		}
   597  
   598  		content := []byte("test-data3")
   599  		buf := bytes.NewBuffer(content)
   600  		l := buf.Len()
   601  		_, err = il.PutBlobChunkStreamed("test", v, buf)
   602  		So(err, ShouldNotBeNil)
   603  
   604  		_, err = il.PutBlobChunk("test", v, 0, int64(l), buf)
   605  		So(err, ShouldNotBeNil)
   606  	})
   607  
   608  	Convey("Invalid dedupe scenarios", t, func() {
   609  		dir, err := ioutil.TempDir("", "oci-repo-test")
   610  		if err != nil {
   611  			panic(err)
   612  		}
   613  		defer os.RemoveAll(dir)
   614  
   615  		log := log.Logger{Logger: zerolog.New(os.Stdout)}
   616  		metrics := monitoring.NewMetricsServer(false, log)
   617  		il := storage.NewImageStore(dir, true, true, log, metrics)
   618  
   619  		v, err := il.NewBlobUpload("dedupe1")
   620  		So(err, ShouldBeNil)
   621  		So(v, ShouldNotBeEmpty)
   622  
   623  		content := []byte("test-data3")
   624  		buf := bytes.NewBuffer(content)
   625  		l := buf.Len()
   626  		d := godigest.FromBytes(content)
   627  		b, err := il.PutBlobChunkStreamed("dedupe1", v, buf)
   628  		So(err, ShouldBeNil)
   629  		So(b, ShouldEqual, l)
   630  
   631  		blobDigest1 := strings.Split(d.String(), ":")[1]
   632  		So(blobDigest1, ShouldNotBeEmpty)
   633  
   634  		err = il.FinishBlobUpload("dedupe1", v, buf, d.String())
   635  		So(err, ShouldBeNil)
   636  		So(b, ShouldEqual, l)
   637  
   638  		// Create a file at the same place where FinishBlobUpload will create
   639  		err = il.InitRepo("dedupe2")
   640  		So(err, ShouldBeNil)
   641  
   642  		err = os.MkdirAll(path.Join(dir, "dedupe2", "blobs/sha256"), 0755)
   643  		if err != nil {
   644  			panic(err)
   645  		}
   646  
   647  		err = ioutil.WriteFile(path.Join(dir, "dedupe2", "blobs/sha256", blobDigest1), content, 0755) // nolint: gosec
   648  		if err != nil {
   649  			panic(err)
   650  		}
   651  
   652  		v, err = il.NewBlobUpload("dedupe2")
   653  		So(err, ShouldBeNil)
   654  		So(v, ShouldNotBeEmpty)
   655  
   656  		content = []byte("test-data3")
   657  		buf = bytes.NewBuffer(content)
   658  		l = buf.Len()
   659  		d = godigest.FromBytes(content)
   660  		b, err = il.PutBlobChunkStreamed("dedupe2", v, buf)
   661  		So(err, ShouldBeNil)
   662  		So(b, ShouldEqual, l)
   663  
   664  		cmd := exec.Command("sudo", "chattr", "+i", path.Join(dir, "dedupe2", "blobs/sha256", blobDigest1)) // nolint: gosec
   665  		_, err = cmd.Output()
   666  		if err != nil {
   667  			panic(err)
   668  		}
   669  
   670  		err = il.FinishBlobUpload("dedupe2", v, buf, d.String())
   671  		So(err, ShouldNotBeNil)
   672  		So(b, ShouldEqual, l)
   673  
   674  		cmd = exec.Command("sudo", "chattr", "-i", path.Join(dir, "dedupe2", "blobs/sha256", blobDigest1)) // nolint: gosec
   675  		_, err = cmd.Output()
   676  		if err != nil {
   677  			panic(err)
   678  		}
   679  
   680  		err = il.FinishBlobUpload("dedupe2", v, buf, d.String())
   681  		So(err, ShouldBeNil)
   682  		So(b, ShouldEqual, l)
   683  	})
   684  
   685  	Convey("DirExists call with a filename as argument", t, func(c C) {
   686  		dir, err := ioutil.TempDir("", "oci-repo-test")
   687  		if err != nil {
   688  			panic(err)
   689  		}
   690  		defer os.RemoveAll(dir)
   691  
   692  		filePath := path.Join(dir, "file.txt")
   693  		err = ioutil.WriteFile(filePath, []byte("some dummy file content"), 0644) //nolint: gosec
   694  		if err != nil {
   695  			panic(err)
   696  		}
   697  
   698  		ok := storage.DirExists(filePath)
   699  		So(ok, ShouldBeFalse)
   700  	})
   701  }
   702  
   703  func TestHardLink(t *testing.T) {
   704  	Convey("Test that ValidateHardLink creates rootDir if it does not exist", t, func() {
   705  		var randomDir string
   706  
   707  		rand.Seed(time.Now().UnixNano())
   708  		for {
   709  			randomLen := rand.Intn(100)
   710  			randomDir = "/tmp/" + randSeq(randomLen)
   711  
   712  			if _, err := os.Stat(randomDir); os.IsNotExist(err) {
   713  				break
   714  			}
   715  		}
   716  		defer os.RemoveAll(randomDir)
   717  
   718  		err := storage.ValidateHardLink(randomDir)
   719  		So(err, ShouldBeNil)
   720  	})
   721  	Convey("Test that ValidateHardLink returns error if rootDir is a file", t, func() {
   722  		dir, err := ioutil.TempDir("", "storage-hard-test")
   723  		if err != nil {
   724  			panic(err)
   725  		}
   726  		defer os.RemoveAll(dir)
   727  
   728  		filePath := path.Join(dir, "file.txt")
   729  		err = ioutil.WriteFile(filePath, []byte("some dummy file content"), 0644) //nolint: gosec
   730  		if err != nil {
   731  			panic(err)
   732  		}
   733  
   734  		err = storage.ValidateHardLink(filePath)
   735  		So(err, ShouldNotBeNil)
   736  	})
   737  	Convey("Test if filesystem supports hardlink", t, func() {
   738  		dir, err := ioutil.TempDir("", "storage-hard-test")
   739  		if err != nil {
   740  			panic(err)
   741  		}
   742  		defer os.RemoveAll(dir)
   743  
   744  		err = storage.ValidateHardLink(dir)
   745  		So(err, ShouldBeNil)
   746  
   747  		err = ioutil.WriteFile(path.Join(dir, "hardtest.txt"), []byte("testing hard link code"), 0644) //nolint: gosec
   748  		if err != nil {
   749  			panic(err)
   750  		}
   751  
   752  		err = os.Chmod(dir, 0400)
   753  		if err != nil {
   754  			panic(err)
   755  		}
   756  
   757  		err = os.Link(path.Join(dir, "hardtest.txt"), path.Join(dir, "duphardtest.txt"))
   758  		So(err, ShouldNotBeNil)
   759  
   760  		err = os.Chmod(dir, 0644)
   761  		if err != nil {
   762  			panic(err)
   763  		}
   764  	})
   765  }
   766  
   767  func randSeq(n int) string {
   768  	var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
   769  
   770  	b := make([]rune, n)
   771  	for i := range b {
   772  		b[i] = letters[rand.Intn(len(letters))]
   773  	}
   774  
   775  	return string(b)
   776  }