mirror of
https://github.com/project-zot/zot.git
synced 2024-12-30 22:34:13 -05:00
4170d2adbc
Moved boltdb to a driver implementation for such interface Added CreateCacheDatabaseDriver in controller Fixed default directory creation (boltDB will only create the file, not the dir Added coverage tests Added example config for boltdb Re-added caching on subpaths, rewrote CreateCacheDatabaseDriver Fix tests Made cacheDriver argument mandatory for NewImageStore, added more validation, added defaults Moved cache interface to own file, removed useRelPaths from config Got rid of cache config, refactored Moved cache to own package and folder Renamed + removed cache factory to backend, replaced CloudCache to RemoteCache Moved storage constants back to storage package moved cache interface and factory to storage package, changed remoteCache defaulting Signed-off-by: Catalin Hofnar <catalin.hofnar@gmail.com>
259 lines
7.4 KiB
Go
259 lines
7.4 KiB
Go
package storage_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
godigest "github.com/opencontainers/go-digest"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"zotregistry.io/zot/pkg/extensions/monitoring"
|
|
"zotregistry.io/zot/pkg/log"
|
|
"zotregistry.io/zot/pkg/storage"
|
|
"zotregistry.io/zot/pkg/storage/cache"
|
|
"zotregistry.io/zot/pkg/storage/local"
|
|
)
|
|
|
|
const (
|
|
repoName = "test"
|
|
)
|
|
|
|
func TestCheckAllBlobsIntegrity(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
log := log.NewLogger("debug", "")
|
|
|
|
metrics := monitoring.NewMetricsServer(false, log)
|
|
cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{
|
|
RootDir: dir,
|
|
Name: "cache",
|
|
UseRelPaths: true,
|
|
}, log)
|
|
imgStore := local.NewImageStore(dir, true, storage.DefaultGCDelay,
|
|
true, true, log, metrics, nil, cacheDriver)
|
|
|
|
Convey("Scrub only one repo", t, func(c C) {
|
|
// initialize repo
|
|
err := imgStore.InitRepo(repoName)
|
|
So(err, ShouldBeNil)
|
|
ok := imgStore.DirExists(path.Join(imgStore.RootDir(), repoName))
|
|
So(ok, ShouldBeTrue)
|
|
storeController := storage.StoreController{}
|
|
storeController.DefaultStore = imgStore
|
|
So(storeController.GetImageStore(repoName), ShouldResemble, imgStore)
|
|
|
|
storeCtlr := storage.StoreController{}
|
|
storeCtlr.DefaultStore = imgStore
|
|
|
|
const tag = "1.0"
|
|
|
|
var manifestDigest godigest.Digest
|
|
var configDigest godigest.Digest
|
|
var layerDigest godigest.Digest
|
|
var manifest string
|
|
var config string
|
|
var layer string
|
|
|
|
// create layer digest
|
|
body := []byte("this is a blob")
|
|
buf := bytes.NewBuffer(body)
|
|
buflen := buf.Len()
|
|
digest := godigest.FromBytes(body)
|
|
upload, n, err := imgStore.FullBlobUpload(repoName, buf, digest)
|
|
So(err, ShouldBeNil)
|
|
So(n, ShouldEqual, len(body))
|
|
So(upload, ShouldNotBeEmpty)
|
|
layerDigest = digest
|
|
layer = digest.String()
|
|
|
|
// create config digest
|
|
created := time.Now().Format("2006-01-02T15:04:05Z")
|
|
configBody := []byte(fmt.Sprintf(`{
|
|
"created": "%v",
|
|
"architecture": "amd64",
|
|
"os": "linux",
|
|
"rootfs": {
|
|
"type": "layers",
|
|
"diff_ids": [
|
|
"",
|
|
""
|
|
]
|
|
},
|
|
"history": [
|
|
{
|
|
"created": "%v",
|
|
"created_by": ""
|
|
},
|
|
{
|
|
"created": "%v",
|
|
"created_by": "",
|
|
"empty_layer": true
|
|
}
|
|
]
|
|
}`, created, created, created))
|
|
configBuf := bytes.NewBuffer(configBody)
|
|
configLen := configBuf.Len()
|
|
configDigest = godigest.FromBytes(configBody)
|
|
uConfig, nConfig, err := imgStore.FullBlobUpload(repoName, configBuf, configDigest)
|
|
So(err, ShouldBeNil)
|
|
So(nConfig, ShouldEqual, len(configBody))
|
|
So(uConfig, ShouldNotBeEmpty)
|
|
config = configDigest.String()
|
|
|
|
// create manifest and add it to the repository
|
|
annotationsMap := make(map[string]string)
|
|
annotationsMap[ispec.AnnotationRefName] = tag
|
|
mnfst := ispec.Manifest{
|
|
Config: ispec.Descriptor{
|
|
MediaType: "application/vnd.oci.image.config.v1+json",
|
|
Digest: configDigest,
|
|
Size: int64(configLen),
|
|
},
|
|
Layers: []ispec.Descriptor{
|
|
{
|
|
MediaType: "application/vnd.oci.image.layer.v1.tar",
|
|
Digest: digest,
|
|
Size: int64(buflen),
|
|
},
|
|
},
|
|
Annotations: annotationsMap,
|
|
}
|
|
|
|
mnfst.SchemaVersion = 2
|
|
mbytes, err := json.Marshal(mnfst)
|
|
So(err, ShouldBeNil)
|
|
|
|
manifestDigest, err = imgStore.PutImageManifest(repoName, tag, ispec.MediaTypeImageManifest,
|
|
mbytes)
|
|
So(err, ShouldBeNil)
|
|
|
|
manifest = manifestDigest.String()
|
|
|
|
Convey("Blobs integrity not affected", func() {
|
|
buff := bytes.NewBufferString("")
|
|
|
|
res, err := storeCtlr.CheckAllBlobsIntegrity()
|
|
res.PrintScrubResults(buff)
|
|
So(err, ShouldBeNil)
|
|
|
|
space := regexp.MustCompile(`\s+`)
|
|
str := space.ReplaceAllString(buff.String(), " ")
|
|
actual := strings.TrimSpace(str)
|
|
So(actual, ShouldContainSubstring, "IMAGE NAME TAG STATUS ERROR")
|
|
So(actual, ShouldContainSubstring, "test 1.0 ok")
|
|
})
|
|
|
|
Convey("Manifest integrity affected", func() {
|
|
// get content of manifest file
|
|
content, _, _, err := imgStore.GetImageManifest(repoName, manifest)
|
|
So(err, ShouldBeNil)
|
|
|
|
// delete content of manifest file
|
|
manifest = strings.ReplaceAll(manifest, "sha256:", "")
|
|
manifestFile := path.Join(imgStore.RootDir(), repoName, "/blobs/sha256", manifest)
|
|
err = os.Truncate(manifestFile, 0)
|
|
So(err, ShouldBeNil)
|
|
|
|
buff := bytes.NewBufferString("")
|
|
|
|
res, err := storeCtlr.CheckAllBlobsIntegrity()
|
|
res.PrintScrubResults(buff)
|
|
So(err, ShouldBeNil)
|
|
|
|
space := regexp.MustCompile(`\s+`)
|
|
str := space.ReplaceAllString(buff.String(), " ")
|
|
actual := strings.TrimSpace(str)
|
|
So(actual, ShouldContainSubstring, "IMAGE NAME TAG STATUS ERROR")
|
|
// verify error message
|
|
So(actual, ShouldContainSubstring, "test 1.0 affected parse application/vnd.oci.image.manifest.v1+json")
|
|
|
|
// put manifest content back to file
|
|
err = os.WriteFile(manifestFile, content, 0o600)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Config integrity affected", func() {
|
|
// get content of config file
|
|
content, err := imgStore.GetBlobContent(repoName, configDigest)
|
|
So(err, ShouldBeNil)
|
|
|
|
// delete content of config file
|
|
config = strings.ReplaceAll(config, "sha256:", "")
|
|
configFile := path.Join(imgStore.RootDir(), repoName, "/blobs/sha256", config)
|
|
err = os.Truncate(configFile, 0)
|
|
So(err, ShouldBeNil)
|
|
|
|
buff := bytes.NewBufferString("")
|
|
|
|
res, err := storeCtlr.CheckAllBlobsIntegrity()
|
|
res.PrintScrubResults(buff)
|
|
So(err, ShouldBeNil)
|
|
|
|
space := regexp.MustCompile(`\s+`)
|
|
str := space.ReplaceAllString(buff.String(), " ")
|
|
actual := strings.TrimSpace(str)
|
|
So(actual, ShouldContainSubstring, "IMAGE NAME TAG STATUS ERROR")
|
|
So(actual, ShouldContainSubstring, "test 1.0 affected stat: parse application/vnd.oci.image.config.v1+json")
|
|
|
|
// put config content back to file
|
|
err = os.WriteFile(configFile, content, 0o600)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Layers integrity affected", func() {
|
|
// get content of layer
|
|
content, err := imgStore.GetBlobContent(repoName, layerDigest)
|
|
So(err, ShouldBeNil)
|
|
|
|
// delete content of layer file
|
|
layer = strings.ReplaceAll(layer, "sha256:", "")
|
|
layerFile := path.Join(imgStore.RootDir(), repoName, "/blobs/sha256", layer)
|
|
err = os.Truncate(layerFile, 0)
|
|
So(err, ShouldBeNil)
|
|
|
|
buff := bytes.NewBufferString("")
|
|
|
|
res, err := storeCtlr.CheckAllBlobsIntegrity()
|
|
res.PrintScrubResults(buff)
|
|
So(err, ShouldBeNil)
|
|
|
|
space := regexp.MustCompile(`\s+`)
|
|
str := space.ReplaceAllString(buff.String(), " ")
|
|
actual := strings.TrimSpace(str)
|
|
So(actual, ShouldContainSubstring, "IMAGE NAME TAG STATUS ERROR")
|
|
So(actual, ShouldContainSubstring, "test 1.0 affected blob: bad blob digest")
|
|
|
|
// put layer content back to file
|
|
err = os.WriteFile(layerFile, content, 0o600)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Layer not found", func() {
|
|
// delete layer file
|
|
layer = strings.ReplaceAll(layer, "sha256:", "")
|
|
layerFile := path.Join(imgStore.RootDir(), repoName, "/blobs/sha256", layer)
|
|
err = os.Remove(layerFile)
|
|
So(err, ShouldBeNil)
|
|
|
|
buff := bytes.NewBufferString("")
|
|
|
|
res, err := storeCtlr.CheckAllBlobsIntegrity()
|
|
res.PrintScrubResults(buff)
|
|
So(err, ShouldBeNil)
|
|
|
|
space := regexp.MustCompile(`\s+`)
|
|
str := space.ReplaceAllString(buff.String(), " ")
|
|
actual := strings.TrimSpace(str)
|
|
So(actual, ShouldContainSubstring, "IMAGE NAME TAG STATUS ERROR")
|
|
So(actual, ShouldContainSubstring, "test 1.0 affected blob: not found")
|
|
})
|
|
})
|
|
}
|