0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-01-06 22:40:28 -05:00
zot/pkg/cli/client/image_cmd_test.go
peusebiu 7642e5af98
fix(scheduler): fix data race (#2085)
* fix(scheduler): data race when pushing new tasks

the problem here is that scheduler can be closed in two ways:
- canceling the context given as argument to scheduler.RunScheduler()
- running scheduler.Shutdown()

because of this shutdown can trigger a data race between calling scheduler.inShutdown()
and actually pushing tasks into the pool workers

solved that by keeping a quit channel and listening on both quit channel and ctx.Done()
and closing the worker chan and scheduler afterwards.

Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>

* refactor(scheduler): refactor into a single shutdown

before this we could stop scheduler either by closing the context
provided to RunScheduler(ctx) or by running Shutdown().

simplify things by getting rid of the external context in RunScheduler().
keep an internal context in the scheduler itself and pass it down to all tasks.

Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>

---------

Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
2023-12-11 10:00:34 -08:00

1312 lines
47 KiB
Go

//go:build search
// +build search
package client_test
import (
"bytes"
"context"
"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/sigstore/cosign/v2/cmd/cosign/cli/generate"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/resty.v1"
"zotregistry.io/zot/pkg/api"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/cli/client"
extconf "zotregistry.io/zot/pkg/extensions/config"
zlog "zotregistry.io/zot/pkg/log"
test "zotregistry.io/zot/pkg/test/common"
. "zotregistry.io/zot/pkg/test/image-utils"
ociutils "zotregistry.io/zot/pkg/test/oci-utils"
"zotregistry.io/zot/pkg/test/signature"
)
//nolint:dupl
func TestSignature(t *testing.T) {
space := regexp.MustCompile(`\s+`)
repoName := "repo7"
Convey("Test with cosign signature(tag)", t, func() {
currentWorkingDir, err := os.Getwd()
So(err, ShouldBeNil)
defer func() { _ = os.Chdir(currentWorkingDir) }()
currentDir := t.TempDir()
err = os.Chdir(currentDir)
So(err, ShouldBeNil)
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = currentDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
image := CreateDefaultImage()
err = UploadImage(image, url, repoName, "1.0")
So(err, ShouldBeNil)
// generate a keypair
if _, err := os.Stat(path.Join(currentDir, "cosign.key")); err != nil {
os.Setenv("COSIGN_PASSWORD", "")
err = generate.GenerateKeyPairCmd(context.TODO(), "", "cosign", nil)
So(err, ShouldBeNil)
}
_, err = os.Stat(path.Join(currentDir, "cosign.key"))
So(err, ShouldBeNil)
// sign the image
err = sign.SignCmd(&options.RootOptions{Verbose: true, Timeout: 1 * time.Minute},
options.KeyOpts{KeyRef: path.Join(currentDir, "cosign.key"), PassFunc: generate.GetPass},
options.SignOptions{
Registry: options.RegistryOptions{AllowInsecure: true},
AnnotationOptions: options.AnnotationOptions{Annotations: []string{"tag=test:1.0"}},
Upload: true,
},
[]string{fmt.Sprintf("localhost:%s/%s@%s", port, "repo7", image.DigestStr())})
So(err, ShouldBeNil)
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
searchConfig := getTestSearchConfig(url, client.NewSearchService())
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err = client.SearchAllImagesGQL(searchConfig)
So(err, ShouldBeNil)
actual := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 1.0 linux/amd64 db573b01 true 854B")
t.Log("Test getting all images using rest calls to get catalog and individual manifests")
buff = &bytes.Buffer{}
searchConfig.ResultWriter = buff
err = client.SearchAllImages(searchConfig)
So(err, ShouldBeNil)
actual = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 1.0 linux/amd64 db573b01 true 854B")
})
Convey("Test with cosign signature(withReferrers)", t, func() {
currentWorkingDir, err := os.Getwd()
So(err, ShouldBeNil)
defer func() { _ = os.Chdir(currentWorkingDir) }()
currentDir := t.TempDir()
err = os.Chdir(currentDir)
So(err, ShouldBeNil)
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = currentDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err = UploadImage(CreateDefaultImage(), url, repoName, "0.0.1")
So(err, ShouldBeNil)
err = signature.SignImageUsingCosign("repo7:0.0.1", port, true)
So(err, ShouldBeNil)
searchConfig := getTestSearchConfig(url, client.NewSearchService())
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err = client.SearchAllImagesGQL(searchConfig)
So(err, ShouldBeNil)
actual := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 db573b01 true 854B")
t.Log("Test getting all images using rest calls to get catalog and individual manifests")
buff = &bytes.Buffer{}
searchConfig.ResultWriter = buff
err = client.SearchAllImages(searchConfig)
So(err, ShouldBeNil)
actual = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 db573b01 true 854B")
})
Convey("Test with notation signature", t, func() {
currentWorkingDir, err := os.Getwd()
So(err, ShouldBeNil)
defer func() { _ = os.Chdir(currentWorkingDir) }()
currentDir := t.TempDir()
err = os.Chdir(currentDir)
So(err, ShouldBeNil)
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = currentDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err = UploadImage(CreateDefaultImage(), url, repoName, "0.0.1")
So(err, ShouldBeNil)
err = signature.SignImageUsingNotary("repo7:0.0.1", port, true)
So(err, ShouldBeNil)
searchConfig := getTestSearchConfig(url, client.NewSearchService())
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err = client.SearchAllImagesGQL(searchConfig)
So(err, ShouldBeNil)
actual := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 db573b01 true 854B")
t.Log("Test getting all images using rest calls to get catalog and individual manifests")
buff = &bytes.Buffer{}
searchConfig.ResultWriter = buff
err = client.SearchAllImages(searchConfig)
So(err, ShouldBeNil)
actual = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 db573b01 true 854B")
})
}
//nolint:dupl
func TestDerivedImageList(t *testing.T) {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err := uploadManifestDerivedBase(url)
if err != nil {
panic(err)
}
space := regexp.MustCompile(`\s+`)
searchConfig := getTestSearchConfig(url, client.NewSearchService())
t.Logf("rootDir: %s", ctlr.Config.Storage.RootDirectory)
Convey("Test from real server", t, func() {
Convey("Test derived images list working", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchDerivedImageListGQL(searchConfig, "repo7:test:2.0")
actual := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(err, ShouldBeNil)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 9d9461ed false 860B")
})
Convey("Test derived images list fails", func() {
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchDerivedImageListGQL(searchConfig, "repo7:test:missing")
So(err, ShouldNotBeNil)
})
Convey("Test derived images list cannot print", func() {
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
searchConfig.OutputFormat = "random"
err := client.SearchDerivedImageListGQL(searchConfig, "repo7:test:2.0")
So(err, ShouldNotBeNil)
})
})
}
//nolint:dupl
func TestBaseImageList(t *testing.T) {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err := uploadManifestDerivedBase(url)
if err != nil {
panic(err)
}
space := regexp.MustCompile(`\s+`)
searchConfig := getTestSearchConfig(url, client.NewSearchService())
t.Logf("rootDir: %s", ctlr.Config.Storage.RootDirectory)
Convey("Test from real server", t, func() {
Convey("Test base images list working", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchBaseImageListGQL(searchConfig, "repo7:test:1.0")
So(err, ShouldBeNil)
actual := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 214e4bed false 530B")
})
Convey("Test base images list fail", func() {
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchBaseImageListGQL(searchConfig, "repo7:test:missing")
So(err, ShouldNotBeNil)
})
Convey("Test base images list cannot print", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
buff := &bytes.Buffer{}
searchConfig.OutputFormat = "random"
searchConfig.ResultWriter = buff
err := client.SearchBaseImageListGQL(searchConfig, "repo7:test:missing")
So(err, ShouldNotBeNil)
})
})
}
func TestOutputFormatGQL(t *testing.T) {
Convey("Test from real server", t, func() {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err := uploadManifest(url)
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
So(err, ShouldBeNil)
Convey("Test json", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
args := []string{"name", "repo7", "--config", "imagetest", "-f", "json"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
expectedStr := `{"repoName":"repo7","tag":"test:1.0",` +
`"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"mediaType":"application/vnd.oci.image.manifest.v1+json",` +
`"manifests":[{"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"configDigest":"sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e",` +
`"lastUpdated":"2023-01-01T12:00:00Z","size":"528","platform":{"os":"linux","arch":"amd64",` +
`"variant":""},"isSigned":false,"downloadCount":0,"layers":[{"size":"15","digest":` +
`"sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6","score":0}],` +
`"history":null,"vulnerabilities":{"maxSeverity":"","count":0},` +
`"referrers":null,"artifactType":"","signatureInfo":null}],` +
`"size":"528","downloadCount":0,"lastUpdated":"2023-01-01T12:00:00Z","description":"","isSigned":false,` +
`"licenses":"","labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",` +
`"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"signatureInfo":null}` + "\n" +
`{"repoName":"repo7","tag":"test:2.0",` +
`"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"mediaType":"application/vnd.oci.image.manifest.v1+json",` +
`"manifests":[{"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"configDigest":"sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e",` +
`"lastUpdated":"2023-01-01T12:00:00Z","size":"528","platform":{"os":"linux","arch":"amd64",` +
`"variant":""},"isSigned":false,"downloadCount":0,"layers":[{"size":"15","digest":` +
`"sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6","score":0}],` +
`"history":null,"vulnerabilities":{"maxSeverity":"","count":0},` +
`"referrers":null,"artifactType":"","signatureInfo":null}],` +
`"size":"528","downloadCount":0,"lastUpdated":"2023-01-01T12:00:00Z","description":"","isSigned":false,` +
`"licenses":"","labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",` +
`"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"signatureInfo":null}` + "\n"
// Output is supposed to be in json lines format, keep all spaces as is for verification
So(buff.String(), ShouldEqual, expectedStr)
So(err, ShouldBeNil)
})
Convey("Test yaml", func() {
args := []string{"name", "repo7", "--config", "imagetest", "-f", "yaml"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
expectedStr := `--- reponame: repo7 tag: test:1.0 ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`configdigest: sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e ` +
`lastupdated: 2023-01-01T12:00:00Z size: "528" platform: os: linux arch: amd64 variant: "" ` +
`issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] artifacttype: "" signatureinfo: [] ` +
`size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] signatureinfo: [] ` +
`--- reponame: repo7 tag: test:2.0 ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`configdigest: sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e ` +
`lastupdated: 2023-01-01T12:00:00Z size: "528" platform: os: linux arch: amd64 variant: "" ` +
`issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] artifacttype: "" signatureinfo: [] ` +
`size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] signatureinfo: []`
So(strings.TrimSpace(str), ShouldEqual, expectedStr)
So(err, ShouldBeNil)
})
Convey("Test yml", func() {
args := []string{"name", "repo7", "--config", "imagetest", "-f", "yml"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
expectedStr := `--- reponame: repo7 tag: test:1.0 ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`configdigest: sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e ` +
`lastupdated: 2023-01-01T12:00:00Z size: "528" platform: os: linux arch: amd64 variant: "" ` +
`issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] artifacttype: "" signatureinfo: [] ` +
`size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] signatureinfo: [] ` +
`--- reponame: repo7 tag: test:2.0 ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
`digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`configdigest: sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e ` +
`lastupdated: 2023-01-01T12:00:00Z size: "528" platform: os: linux arch: amd64 variant: "" ` +
`issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" ` +
`count: 0 referrers: [] artifacttype: "" signatureinfo: [] ` +
`size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] signatureinfo: []`
So(strings.TrimSpace(str), ShouldEqual, expectedStr)
So(err, ShouldBeNil)
})
Convey("Test invalid", func() {
args := []string{"name", "repo7", "--config", "imagetest", "-f", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid cli output format")
})
})
}
func TestServerResponseGQL(t *testing.T) {
Convey("Test from real server", t, func() {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err := uploadManifest(url)
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
So(err, ShouldBeNil)
Convey("Test all images config url", func() {
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
args := []string{"list", "--config", "imagetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
Convey("Test all images invalid output format", func() {
args := []string{"list", "--config", "imagetest", "-f", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid cli output format")
})
})
Convey("Test all images verbose", func() {
args := []string{"list", "--config", "imagetest", "--verbose"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE
// repo7 test:2.0 linux/amd64 51e18f50 d14faead false 528B
// b8781e88 15B
// repo7 test:1.0 linux/amd64 51e18f50 d14faead false 528B
// b8781e88 15B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 d14faead false 528B b8781e88 15B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 d14faead false 528B b8781e88 15B")
})
Convey("Test all images with debug flag", func() {
args := []string{"list", "--config", "imagetest", "--debug"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "GET")
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
})
Convey("Test image by name config url", func() {
args := []string{"name", "repo7", "--config", "imagetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
Convey("invalid output format", func() {
args := []string{"name", "repo7", "--config", "imagetest", "-f", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid cli output format")
})
})
Convey("Test image by digest", func() {
args := []string{"digest", "51e18f50", "--config", "imagetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST SIZE
// repo7 test:2.0 a0ca253b 15B
// repo7 test:1.0 a0ca253b 15B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
Convey("nonexistent digest", func() {
args := []string{"digest", "d1g35t", "--config", "imagetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
So(len(buff.String()), ShouldEqual, 0)
})
Convey("invalid output format", func() {
args := []string{"digest", "51e18f50", "--config", "imagetest", "-f", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldNotBeNil)
So(buff.String(), ShouldContainSubstring, "invalid cli output format")
})
})
Convey("Test image by name nonexistent name", func() {
args := []string{"name", "repo777", "--config", "imagetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
So(len(buff.String()), ShouldEqual, 0)
})
Convey("Test list repos error", func() {
args := []string{"list", "--config", "config-test"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"config-test",
"url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath)
cmd := client.NewRepoCommand(client.NewSearchService())
buff := &bytes.Buffer{}
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY NAME")
So(actual, ShouldContainSubstring, "repo7")
})
})
}
func TestServerResponse(t *testing.T) {
port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
err := uploadManifest(url)
if err != nil {
panic(err)
}
space := regexp.MustCompile(`\s+`)
Convey("Test from real server", t, func() {
searchConfig := getTestSearchConfig(url, client.NewSearchService())
Convey("Test all images", func() {
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchAllImages(searchConfig)
So(err, ShouldBeNil)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
})
Convey("Test all images verbose", func() {
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
searchConfig.Verbose = true
defer func() { searchConfig.Verbose = false }()
err := client.SearchAllImages(searchConfig)
So(err, ShouldBeNil)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE
// repo7 test:2.0 linux/amd64 51e18f50 d14faead false 528B
// b8781e88 15B
// repo7 test:1.0 linux/amd64 51e18f50 d14faead false 528B
// b8781e88 15B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 d14faead false 528B b8781e88 15B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 d14faead false 528B b8781e88 15B")
})
Convey("Test image by name", func() {
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchImageByName(searchConfig, "repo7")
So(err, ShouldBeNil)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
})
Convey("Test image by digest", func() {
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchImagesByDigest(searchConfig, "51e18f50")
So(err, ShouldBeNil)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST SIZE
// repo7 test:2.0 linux/amd64 51e18f50 528B
// repo7 test:1.0 linux/amd64 51e18f50 528B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
Convey("nonexistent digest", func() {
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchImagesByDigest(searchConfig, "d1g35t")
So(err, ShouldBeNil)
So(len(buff.String()), ShouldEqual, 0)
})
})
Convey("Test image by name nonexistent name", func() {
err := client.SearchImageByName(searchConfig, "repo777")
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "no repository found")
})
})
}
func TestServerResponseGQLWithoutPermissions(t *testing.T) {
Convey("Test accessing a blobs folder without having permissions fails fast", t, func() {
port := test.GetFreePort()
conf := config.New()
conf.HTTP.Port = port
dir := t.TempDir()
srcStorageCtlr := ociutils.GetDefaultStoreController(dir, zlog.NewLogger("debug", ""))
err := WriteImageToFileSystem(CreateDefaultImage(), "zot-test", "0.0.1", srcStorageCtlr)
So(err, ShouldBeNil)
err = os.Chmod(path.Join(dir, "zot-test", "blobs"), 0o000)
if err != nil {
panic(err)
}
defer func() {
err = os.Chmod(path.Join(dir, "zot-test", "blobs"), 0o777)
if err != nil {
panic(err)
}
}()
conf.Storage.RootDirectory = dir
defaultVal := true
searchConfig := &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
}
conf.Extensions = &extconf.ExtensionConfig{
Search: searchConfig,
}
ctlr := api.NewController(conf)
if err := ctlr.Init(); err != nil {
So(err, ShouldNotBeNil)
}
})
}
func TestDisplayIndex(t *testing.T) {
Convey("Init Basic Server, No GQL", t, func() {
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
Convey("No GQL", func() {
defaultVal := false
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
runDisplayIndexTests(baseURL)
})
Convey("With GQL", func() {
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
runDisplayIndexTests(baseURL)
})
})
}
func runDisplayIndexTests(baseURL string) {
space := regexp.MustCompile(`\s+`)
searchConfig := getTestSearchConfig(baseURL, client.NewSearchService())
Convey("Test Image Index", func() {
uploadTestMultiarch(baseURL)
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err := client.SearchAllImages(searchConfig)
So(err, ShouldBeNil)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE
// repo multi-arch * 4780eafe false 1.5kB
// linux/amd64 02e0ac42 false 644B
// windows/arm64/v6 5e09b7f9 false 444B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo multi-arch * 4780eafe false 1.5kB ")
So(actual, ShouldContainSubstring, "linux/amd64 02e0ac42 false 644B ")
So(actual, ShouldContainSubstring, "windows/arm64/v6 5e09b7f9 false 506B")
})
Convey("Test Image Index Verbose", func() {
uploadTestMultiarch(baseURL)
buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
searchConfig.Verbose = true
err := client.SearchAllImages(searchConfig)
So(err, ShouldBeNil)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE
// repo multi-arch * 4780eafe false 1.5kB
// linux/amd64 02e0ac42 58cc9abe false 644B
// cbb5b121 4B
// a00291e8 4B
// windows/arm64/v6 5e09b7f9 5132a1cd false 506B
// 7d08ce29 4B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE")
So(actual, ShouldContainSubstring, "repo multi-arch * 4780eafe false 1.5kB")
So(actual, ShouldContainSubstring, "linux/amd64 02e0ac42 58cc9abe false 644B")
So(actual, ShouldContainSubstring, "cbb5b121 4B")
So(actual, ShouldContainSubstring, "a00291e8 4B")
So(actual, ShouldContainSubstring, "windows/arm64/v6 5e09b7f9 5132a1cd false 506B")
So(actual, ShouldContainSubstring, "7d08ce29 4B")
})
}
func TestImagesSortFlag(t *testing.T) {
rootDir := t.TempDir()
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{
BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
CVE: nil,
},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = rootDir
image1 := CreateImageWith().DefaultLayers().
ImageConfig(ispec.Image{Created: DateRef(2010, 1, 1, 1, 1, 1, 0, time.UTC)}).Build()
image2 := CreateImageWith().DefaultLayers().
ImageConfig(ispec.Image{Created: DateRef(2020, 1, 1, 1, 1, 1, 0, time.UTC)}).Build()
storeController := ociutils.GetDefaultStoreController(rootDir, ctlr.Log)
err := WriteImageToFileSystem(image1, "a-repo", "tag1", storeController)
if err != nil {
t.FailNow()
}
err = WriteImageToFileSystem(image2, "b-repo", "tag2", storeController)
if err != nil {
t.FailNow()
}
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()
Convey("Sorting", t, func() {
args := []string{"list", "--sort-by", "alpha-asc", "--url", baseURL}
cmd := client.NewImageCommand(client.NewSearchService())
buff := bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err := cmd.Execute()
So(err, ShouldBeNil)
str := buff.String()
So(strings.Index(str, "a-repo"), ShouldBeLessThan, strings.Index(str, "b-repo"))
args = []string{"list", "--sort-by", "alpha-dsc", "--url", baseURL}
buff = bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
So(err, ShouldBeNil)
str = buff.String()
So(strings.Index(str, "b-repo"), ShouldBeLessThan, strings.Index(str, "a-repo"))
args = []string{"list", "--sort-by", "update-time", "--url", baseURL}
buff = bytes.NewBufferString("")
cmd.SetOut(buff)
cmd.SetErr(buff)
cmd.SetArgs(args)
err = cmd.Execute()
str = buff.String()
So(err, ShouldBeNil)
So(strings.Index(str, "b-repo"), ShouldBeLessThan, strings.Index(str, "a-repo"))
})
}
func uploadTestMultiarch(baseURL string) {
// ------- Define Image1
layer11 := []byte{11, 12, 13, 14}
layer12 := []byte{16, 17, 18, 19}
image1 := CreateImageWith().
LayerBlobs([][]byte{
layer11,
layer12,
}).
ImageConfig(
ispec.Image{
Platform: ispec.Platform{OS: "linux", Architecture: "amd64"},
},
).Build()
// ------ Define Image2
layer21 := []byte{21, 22, 23, 24}
image2 := CreateImageWith().
LayerBlobs([][]byte{
layer21,
}).
ImageConfig(
ispec.Image{
Platform: ispec.Platform{OS: "windows", Architecture: "arm64", Variant: "v6"},
},
).Build()
// ------- Upload The multiarch image
multiarch := CreateMultiarchWith().Images([]Image{image1, image2}).Build()
err := UploadMultiarchImage(multiarch, baseURL, "repo", "multi-arch")
So(err, ShouldBeNil)
}
func uploadManifest(url string) error {
// create and upload a blob/layer
resp, _ := resty.R().Post(url + "/v2/repo7/blobs/uploads/")
loc := test.Location(url, resp)
content := []byte("this is a blob5")
digest := godigest.FromBytes(content)
_, _ = resty.R().SetQueryParam("digest", digest.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(loc)
// create config
createdTime := time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC)
config := ispec.Image{
Created: &createdTime,
Platform: ispec.Platform{
Architecture: "amd64",
OS: "linux",
},
RootFS: ispec.RootFS{
Type: "layers",
DiffIDs: []godigest.Digest{},
},
Author: "some author",
}
cblob, err := json.MarshalIndent(&config, "", "\t")
if err != nil {
return err
}
cdigest := godigest.FromBytes(cblob)
// upload image config blob
resp, _ = resty.R().Post(url + "/v2/repo7/blobs/uploads/")
loc = test.Location(url, resp)
_, _ = resty.R().
SetContentLength(true).
SetHeader("Content-Length", fmt.Sprintf("%d", len(cblob))).
SetHeader("Content-Type", "application/octet-stream").
SetQueryParam("digest", cdigest.String()).
SetBody(cblob).
Put(loc)
// create a manifest
manifest := ispec.Manifest{
Config: ispec.Descriptor{
MediaType: "application/vnd.oci.image.config.v1+json",
Digest: cdigest,
Size: int64(len(cblob)),
},
Layers: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest,
Size: int64(len(content)),
},
},
}
manifest.SchemaVersion = 2
content, err = json.Marshal(manifest)
if err != nil {
return err
}
_, _ = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(content).Put(url + "/v2/repo7/manifests/test:1.0")
content = []byte("this is a blob5")
digest = godigest.FromBytes(content)
// create a manifest with same blob but a different tag
manifest = ispec.Manifest{
Config: ispec.Descriptor{
MediaType: "application/vnd.oci.image.config.v1+json",
Digest: cdigest,
Size: int64(len(cblob)),
},
Layers: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest,
Size: int64(len(content)),
},
},
}
manifest.SchemaVersion = 2
content, err = json.Marshal(manifest)
if err != nil {
return err
}
_, _ = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(content).Put(url + "/v2/repo7/manifests/test:2.0")
return nil
}
func uploadManifestDerivedBase(url string) error {
// create a blob/layer
_, _ = resty.R().Post(url + "/v2/repo7/blobs/uploads/")
content1 := []byte("this is a blob5.0")
content2 := []byte("this is a blob5.1")
content3 := []byte("this is a blob5.2")
digest1 := godigest.FromBytes(content1)
digest2 := godigest.FromBytes(content2)
digest3 := godigest.FromBytes(content3)
_, _ = resty.R().SetQueryParam("digest", digest1.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content1).Post(url + "/v2/repo7/blobs/uploads/")
_, _ = resty.R().SetQueryParam("digest", digest2.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content2).Post(url + "/v2/repo7/blobs/uploads/")
_, _ = resty.R().SetQueryParam("digest", digest3.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content3).Post(url + "/v2/repo7/blobs/uploads/")
// create config
createdTime := time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC)
config := ispec.Image{
Created: &createdTime,
Platform: ispec.Platform{
Architecture: "amd64",
OS: "linux",
},
RootFS: ispec.RootFS{
Type: "layers",
DiffIDs: []godigest.Digest{},
},
Author: "some author",
}
cblob, err := json.MarshalIndent(&config, "", "\t")
if err != nil {
return err
}
cdigest := godigest.FromBytes(cblob)
// upload image config blob
resp, _ := resty.R().Post(url + "/v2/repo7/blobs/uploads/")
loc := test.Location(url, resp)
_, _ = resty.R().
SetContentLength(true).
SetHeader("Content-Length", fmt.Sprintf("%d", len(cblob))).
SetHeader("Content-Type", "application/octet-stream").
SetQueryParam("digest", cdigest.String()).
SetBody(cblob).
Put(loc)
// create a manifest
manifest := ispec.Manifest{
Config: ispec.Descriptor{
MediaType: "application/vnd.oci.image.config.v1+json",
Digest: cdigest,
Size: int64(len(cblob)),
},
Layers: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest1,
Size: int64(len(content1)),
}, {
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest2,
Size: int64(len(content2)),
}, {
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest3,
Size: int64(len(content3)),
},
},
}
manifest.SchemaVersion = 2
content, err := json.Marshal(manifest)
if err != nil {
return err
}
_, _ = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(content).Put(url + "/v2/repo7/manifests/test:1.0")
content1 = []byte("this is a blob5.0")
digest1 = godigest.FromBytes(content1)
// create a manifest with one common layer blob
manifest = ispec.Manifest{
Config: ispec.Descriptor{
MediaType: "application/vnd.oci.image.config.v1+json",
Digest: cdigest,
Size: int64(len(cblob)),
},
Layers: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest1,
Size: int64(len(content1)),
},
},
}
manifest.SchemaVersion = 2
content, err = json.Marshal(manifest)
if err != nil {
return err
}
_, _ = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(content).Put(url + "/v2/repo7/manifests/test:2.0")
return nil
}
func getTestSearchConfig(url string, searchService client.SearchService) client.SearchConfig {
var (
user string
outputFormat string
verbose bool
debug bool
verifyTLS bool
)
return client.SearchConfig{
SearchService: searchService,
SortBy: "alpha-asc",
ServURL: url,
User: user,
OutputFormat: outputFormat,
Verbose: verbose,
Debug: debug,
VerifyTLS: verifyTLS,
ResultWriter: nil,
}
}