0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-03-18 02:22:53 -05:00

feat(cli): updated display format for multiarch images (#1268)

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
This commit is contained in:
LaurentiuNiculae 2023-03-21 19:16:00 +02:00 committed by GitHub
parent 0036d6dd09
commit 21b7c69fd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 734 additions and 184 deletions

View file

@ -326,12 +326,14 @@ func fetchImageIndexStruct(ctx context.Context, job *httpJob) (*imageStruct, err
isNotationSigned(ctx, job.imageName, indexDigest, job.config, job.username, job.password)
return &imageStruct{
verbose: *job.config.verbose,
RepoName: job.imageName,
Tag: job.tagName,
Digest: indexDigest,
MediaType: ispec.MediaTypeImageIndex,
Manifests: manifestList,
Size: strconv.FormatInt(imageSize, 10),
IsSigned: isIndexSigned,
Manifests: manifestList,
verbose: *job.config.verbose,
}, nil
}
@ -351,14 +353,16 @@ func fetchImageManifestStruct(ctx context.Context, job *httpJob) (*imageStruct,
}
return &imageStruct{
verbose: *job.config.verbose,
RepoName: job.imageName,
Tag: job.tagName,
Size: manifest.Size,
IsSigned: manifest.IsSigned,
RepoName: job.imageName,
Tag: job.tagName,
Digest: manifest.Digest,
MediaType: ispec.MediaTypeImageManifest,
Manifests: []manifestStruct{
manifest,
},
Size: manifest.Size,
IsSigned: manifest.IsSigned,
verbose: *job.config.verbose,
}, nil
}

View file

@ -13,6 +13,7 @@ import (
"testing"
"github.com/gorilla/mux"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey"
@ -264,6 +265,7 @@ func TestDoHTTPRequest(t *testing.T) {
vars := mux.Vars(req)
if vars["reference"] == "indexRef" {
writer.Header().Add("docker-content-digest", godigest.FromString("t").String())
_, err := writer.Write([]byte(`
{
"manifests": [
@ -592,6 +594,7 @@ func TestDoJobErrors(t *testing.T) {
Route: "/v2/{name}/manifests/{reference}",
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", ispec.MediaTypeImageIndex)
w.Header().Add("docker-content-digest", godigest.FromString("t").String())
_, err := w.Write([]byte(""))
if err != nil {
@ -606,6 +609,8 @@ func TestDoJobErrors(t *testing.T) {
vars := mux.Vars(req)
if vars["reference"] == "indexRef" {
writer.Header().Add("docker-content-digest", godigest.FromString("t").String())
_, err := writer.Write([]byte(`{"manifests": [{"digest": "manifestRef"}]}`))
if err != nil {
return
@ -613,6 +618,8 @@ func TestDoJobErrors(t *testing.T) {
}
if vars["reference"] == "manifestRef" {
writer.Header().Add("docker-content-digest", godigest.FromString("t").String())
_, err := writer.Write([]byte(`{"config": {"digest": "confDigest"}}`))
if err != nil {
return

View file

@ -183,7 +183,7 @@ func TestSearchCVECmd(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE dummyImageName tag 6e2f80bf os/arch false 123kB")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
})
Convey("Test CVE by name and CVE ID - using shorthand", t, func() {
@ -200,7 +200,7 @@ func TestSearchCVECmd(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE dummyImageName tag 6e2f80bf os/arch false 123kB")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
})
Convey("Test CVE by image name - in text format", t, func() {
@ -283,7 +283,7 @@ func TestSearchCVECmd(t *testing.T) {
err := cveCmd.Execute()
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE anImage tag 6e2f80bf os/arch false 123kB") //nolint:lll
So(strings.TrimSpace(str), ShouldEqual, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB") //nolint:lll
So(err, ShouldBeNil)
})
@ -328,7 +328,7 @@ func TestSearchCVECmd(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldEqual, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE fixedImage tag 6e2f80bf os/arch false 123kB") //nolint:lll
So(strings.TrimSpace(str), ShouldEqual, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE fixedImage tag os/arch 6e2f80bf false 123kB") //nolint:lll
})
Convey("Test fixed tags by and image name CVE ID - invalid image name", t, func() {
@ -713,7 +713,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldEqual, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE zot-cve-test 0.0.1 82836dd7 N/A false 548B")
So(str, ShouldEqual, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 82836dd7 false 548B")
})
Convey("Test images by CVE ID - GQL - invalid CVE ID", t, func() {
@ -730,7 +730,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldNotContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(str, ShouldNotContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test images by CVE ID - GQL - invalid output format", t, func() {
@ -778,7 +778,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(strings.TrimSpace(str), ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test fixed tags by image name and CVE ID - GQL - random image", t, func() {
@ -795,7 +795,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldNotBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(strings.TrimSpace(str), ShouldNotContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test fixed tags by image name and CVE ID - GQL - invalid image", t, func() {
@ -812,7 +812,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldNotBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(strings.TrimSpace(str), ShouldNotContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test CVE by name and CVE ID - GQL - positive", t, func() {
@ -829,7 +829,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE zot-cve-test 0.0.1 82836dd7 N/A false 548B")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 82836dd7 false 548B")
})
Convey("Test CVE by name and CVE ID - GQL - invalid name and CVE ID", t, func() {
@ -845,7 +845,7 @@ func TestServerCVEResponse(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "IMAGE NAME TAG SIGNED SIZE")
So(strings.TrimSpace(str), ShouldNotContainSubstring, "IMAGE NAME TAG OS/ARCH SIGNED SIZE")
})
Convey("Test CVE by name and CVE ID - GQL - invalid output format", t, func() {
@ -907,7 +907,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldEqual, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE zot-cve-test 0.0.1 82836dd7 linux/amd64 false 548B")
So(str, ShouldEqual, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 linux/amd64 82836dd7 false 548B")
})
Convey("Test images by CVE ID - invalid CVE ID", t, func() {
@ -924,7 +924,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(str, ShouldNotContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(str, ShouldNotContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test fixed tags by and image name CVE ID - positive", t, func() {
@ -958,7 +958,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(strings.TrimSpace(str), ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test fixed tags by and image name CVE ID - invalid image", t, func() {
@ -975,7 +975,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str)
So(err, ShouldNotBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(strings.TrimSpace(str), ShouldNotContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
})
Convey("Test CVE by name and CVE ID - positive", t, func() {
@ -992,7 +992,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE zot-cve-test 0.0.1 82836dd7 linux/amd64 false 548B")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 linux/amd64 82836dd7 false 548B")
})
Convey("Test CVE by name and CVE ID - invalid name and CVE ID", t, func() {
@ -1009,7 +1009,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldNotContainSubstring,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
})
}

View file

@ -186,7 +186,7 @@ func TestSearchImageCmd(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE dummyImageName tag 6e2f80bf os/arch false 123kB")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
So(err, ShouldBeNil)
})
@ -203,7 +203,7 @@ func TestSearchImageCmd(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE dummyImageName tag 6e2f80bf os/arch false 123kB")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
So(err, ShouldBeNil)
Convey("using shorthand", func() {
args := []string{"imagetest", "-n", "dummyImageName", "--url", "someUrlImage"}
@ -219,7 +219,7 @@ func TestSearchImageCmd(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE dummyImageName tag 6e2f80bf os/arch false 123kB")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
So(err, ShouldBeNil)
})
})
@ -237,7 +237,7 @@ func TestSearchImageCmd(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE anImage tag 6e2f80bf os/arch false 123kB")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB")
So(err, ShouldBeNil)
Convey("invalid URL format", func() {
@ -330,8 +330,8 @@ func TestSignature(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:1.0 6742241d linux/amd64 true 447B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 6742241d true 447B")
t.Log("Test getting all images using rest calls to get catalog and individual manifests")
cmd = MockNewImageCommand(new(searchService))
@ -343,8 +343,8 @@ func TestSignature(t *testing.T) {
So(err, ShouldBeNil)
str = space.ReplaceAllString(buff.String(), " ")
actual = strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:1.0 6742241d linux/amd64 true 447B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 6742241d true 447B")
err = os.Chdir(currentWorkingDir)
So(err, ShouldBeNil)
@ -407,8 +407,8 @@ func TestSignature(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 6742241d linux/amd64 true 447B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 6742241d true 447B")
t.Log("Test getting all images using rest calls to get catalog and individual manifests")
cmd = MockNewImageCommand(new(searchService))
@ -420,8 +420,8 @@ func TestSignature(t *testing.T) {
So(err, ShouldBeNil)
str = space.ReplaceAllString(buff.String(), " ")
actual = strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 6742241d linux/amd64 true 447B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 6742241d true 447B")
err = os.Chdir(currentWorkingDir)
So(err, ShouldBeNil)
@ -469,8 +469,8 @@ func TestDerivedImageList(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:1.0 2694fdb0 N/A false 824B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:1.0 2694fdb0 false 824B")
})
Convey("Test derived images list fails", func() {
@ -542,8 +542,8 @@ func TestBaseImageList(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 3fc80493 N/A false 494B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 3fc80493 false 494B")
})
Convey("Test base images list fail", func() {
@ -732,7 +732,7 @@ func TestOutputFormat(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
So(strings.TrimSpace(str), ShouldEqual,
"IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE dummyImageName tag 6e2f80bf os/arch false 123kB")
"IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
So(err, ShouldBeNil)
})
@ -756,7 +756,8 @@ func TestOutputFormat(t *testing.T) {
`"layers": [ { "size": "0", "digest": "sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6" } ], `+ //nolint:lll
`"platform": { "os": "os", "arch": "arch", "variant": "" }, `+
`"size": "123445", "isSigned": false } ], `+
`"size": "123445", "isSigned": false }`)
`"size": "123445", "digest": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", `+
`"mediaType": "application/vnd.oci.image.manifest.v1+json", "isSigned": false }`)
So(err, ShouldBeNil)
})
@ -782,7 +783,8 @@ func TestOutputFormat(t *testing.T) {
`layers: - size: 0 digest: sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6 `+
`platform: os: os arch: arch variant: "" `+
`size: "123445" issigned: false `+
`size: "123445" issigned: false`,
`size: "123445" digest: sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 `+
`mediatype: application/vnd.oci.image.manifest.v1+json issigned: false`,
)
So(err, ShouldBeNil)
@ -811,7 +813,8 @@ func TestOutputFormat(t *testing.T) {
`layers: - size: 0 digest: sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6 `+
`platform: os: os arch: arch variant: "" `+
`size: "123445" issigned: false `+
`size: "123445" issigned: false`,
`size: "123445" digest: sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 `+
`mediatype: application/vnd.oci.image.manifest.v1+json issigned: false`,
)
So(err, ShouldBeNil)
})
@ -867,9 +870,9 @@ func TestServerResponseGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B")
Convey("Test all images invalid output format", func() {
args := []string{"imagetest", "-o", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
@ -900,14 +903,14 @@ func TestServerResponseGQL(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// IMAGE NAME TAG DIGEST CONFIG OS/ARCH SIGNED LAYERS SIZE
// repo7 test:2.0 a0ca253b b8781e88 linux/amd64 false 492B
// b8781e88 15B
// repo7 test:1.0 a0ca253b b8781e88 linux/amd64 false 492B
// b8781e88 15B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST CONFIG OS/ARCH SIGNED LAYERS SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 3a1d2d0c linux/amd64 false 492B b8781e88 15B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 3a1d2d0c linux/amd64 false 492B b8781e88 15B")
// IMAGE NAME TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE
// repo7 test:2.0 linux/amd64 a0ca253b b8781e88 false 492B
// b8781e88 15B
// repo7 test:1.0 linux/amd64 a0ca253b b8781e88 false 492B
// b8781e88 15B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 3a1d2d0c false 492B b8781e88 15B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 3a1d2d0c false 492B b8781e88 15B")
})
Convey("Test all images with debug flag", func() {
@ -925,9 +928,9 @@ func TestServerResponseGQL(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "GET")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B")
})
Convey("Test image by name config url", func() {
@ -944,9 +947,9 @@ func TestServerResponseGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B")
Convey("with shorthand", func() {
args := []string{"imagetest", "-n", "repo7"}
@ -962,9 +965,9 @@ func TestServerResponseGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B")
})
Convey("invalid output format", func() {
@ -997,12 +1000,12 @@ func TestServerResponseGQL(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// IMAGE NAME TAG DIGEST OS/ARCH SIZE
// repo7 test:2.0 a0ca253b N/A 15B
// repo7 test:1.0 a0ca253b N/A 15B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 N/A false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 N/A false 492B")
// IMAGE NAME TAG OS/ARCH DIGEST SIZE
// repo7 test:2.0 a0ca253b 15B
// repo7 test:1.0 a0ca253b 15B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 false 492B")
Convey("with shorthand", func() {
args := []string{"imagetest", "-d", "883fc0c5"}
@ -1018,9 +1021,9 @@ func TestServerResponseGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 N/A false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 N/A false 492B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 false 492B")
})
Convey("nonexistent digest", func() {
@ -1128,9 +1131,9 @@ func TestServerResponse(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B")
})
Convey("Test all images verbose", func() {
@ -1148,14 +1151,14 @@ func TestServerResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// IMAGE NAME TAG DIGEST CONFIG OS/ARCH SIGNED LAYERS SIZE
// repo7 test:2.0 a0ca253b b8781e88 linux/amd64 false 492B
// linux/amd64 b8781e88 15B
// repo7 test:1.0 a0ca253b b8781e88 linux/amd64 false 492B
// linux/amd64 b8781e88 15B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST CONFIG OS/ARCH SIGNED LAYERS SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 3a1d2d0c linux/amd64 false 492B b8781e88 15B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 3a1d2d0c linux/amd64 false 492B b8781e88 15B")
// IMAGE NAME TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE
// repo7 test:2.0 linux/amd64 a0ca253b b8781e88 false 492B
// b8781e88 15B
// repo7 test:1.0 linux/amd64 a0ca253b b8781e88 false 492B
// b8781e88 15B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 3a1d2d0c false 492B b8781e88 15B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 3a1d2d0c false 492B b8781e88 15B")
})
Convey("Test image by name", func() {
@ -1172,9 +1175,9 @@ func TestServerResponse(t *testing.T) {
space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B")
})
Convey("Test image by digest", func() {
@ -1192,12 +1195,12 @@ func TestServerResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ):
// IMAGE NAME TAG DIGEST OS/ARCH SIZE
// repo7 test:2.0 a0ca253b linux/amd64 492B
// repo7 test:1.0 a0ca253b linux/amd64 492B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG DIGEST OS/ARCH SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 linux/amd64 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 linux/amd64 false 492B")
// IMAGE NAME TAG OS/ARCH DIGEST SIZE
// repo7 test:2.0 linux/amd64 a0ca253b 492B
// repo7 test:1.0 linux/amd64 a0ca253b 492B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B")
Convey("nonexistent digest", func() {
args := []string{"imagetest", "--digest", "d1g35t"}
@ -1269,6 +1272,153 @@ func TestServerResponseGQLWithoutPermissions(t *testing.T) {
})
}
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) {
Convey("Test Image Index", func() {
uploadTestMultiarch(baseURL)
args := []string{"imagetest"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := MockNewImageCommand(new(searchService))
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):
// IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE
// repo multi-arch * 46b78b06 false 1.4kB
// linux/amd64 97b0d65c false 577B
// windows/arm64/v6 dcfa3a9c false 444B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo multi-arch * 46b78b06 false 1.4kB ")
So(actual, ShouldContainSubstring, "linux/amd64 97b0d65c false 577B ")
So(actual, ShouldContainSubstring, "windows/arm64/v6 dcfa3a9c false 444B")
})
Convey("Test Image Index Verbose", func() {
uploadTestMultiarch(baseURL)
args := []string{"imagetest", "--verbose"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`,
baseURL))
defer os.Remove(configPath)
cmd := MockNewImageCommand(new(searchService))
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):
// IMAGE NAME TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE
// repo multi-arch * 46b78b06 false 1.4kB
// linux/amd64 97b0d65c 58cc9abe false 577B
// cbb5b121 4B
// a00291e8 4B
// windows/arm64/v6 dcfa3a9c 5132a1cd false 444B
// 7d08ce29 4B
So(actual, ShouldContainSubstring, "IMAGE NAME TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE")
So(actual, ShouldContainSubstring, "repo multi-arch * 46b78b06 false 1.4kB")
So(actual, ShouldContainSubstring, "linux/amd64 97b0d65c 58cc9abe false 577B")
So(actual, ShouldContainSubstring, "cbb5b121 4B")
So(actual, ShouldContainSubstring, "a00291e8 4B")
So(actual, ShouldContainSubstring, "windows/arm64/v6 dcfa3a9c 5132a1cd false 444B")
So(actual, ShouldContainSubstring, "7d08ce29 4B")
})
}
func uploadTestMultiarch(baseURL string) {
// ------- Define Image1
layer11 := []byte{11, 12, 13, 14}
layer12 := []byte{16, 17, 18, 19}
image1, err := test.GetImageWithComponents(
ispec.Image{
Platform: ispec.Platform{
OS: "linux",
Architecture: "amd64",
},
},
[][]byte{
layer11,
layer12,
},
)
So(err, ShouldBeNil)
// ------ Define Image2
layer21 := []byte{21, 22, 23, 24}
image2, err := test.GetImageWithComponents(
ispec.Image{
Platform: ispec.Platform{
OS: "windows",
Architecture: "arm64",
Variant: "v6",
},
},
[][]byte{
layer21,
},
)
So(err, ShouldBeNil)
// ------- Upload The multiarch image
multiarch := test.GetMultiarchImageForImages("multi-arch", []test.Image{image1, image2})
err = test.UploadMultiarchImage(multiarch, baseURL, "repo")
So(err, ShouldBeNil)
}
func MockNewImageCommand(searchService SearchService) *cobra.Command {
searchImageParams := make(map[string]*string)
@ -1596,8 +1746,10 @@ func (service mockService) getImagesGQL(ctx context.Context, config searchConfig
imageListGQLResponse := &imageListStructGQL{}
imageListGQLResponse.Data.Results = []imageStruct{
{
RepoName: "dummyImageName",
Tag: "tag",
RepoName: "dummyImageName",
Tag: "tag",
MediaType: ispec.MediaTypeImageManifest,
Digest: godigest.FromString("test").String(),
Manifests: []manifestStruct{
{
Digest: godigest.FromString("Digest").String(),
@ -1619,8 +1771,10 @@ func (service mockService) getImagesByDigestGQL(ctx context.Context, config sear
imageListGQLResponse := &imageListStructForDigestGQL{}
imageListGQLResponse.Data.Results = []imageStruct{
{
RepoName: "randomimageName",
Tag: "tag",
RepoName: "randomimageName",
Tag: "tag",
MediaType: ispec.MediaTypeImageManifest,
Digest: godigest.FromString("test").String(),
Manifests: []manifestStruct{
{
Digest: godigest.FromString("Digest").String(),
@ -1745,6 +1899,8 @@ func (service mockService) getAllImages(ctx context.Context, config searchConfig
image := &imageStruct{}
image.RepoName = "randomimageName"
image.Tag = "tag"
image.Digest = godigest.FromString("test").String()
image.MediaType = ispec.MediaTypeImageManifest
image.Manifests = []manifestStruct{
{
Digest: godigest.FromString("Digest").String(),
@ -1775,6 +1931,8 @@ func (service mockService) getImageByName(ctx context.Context, config searchConf
image := &imageStruct{}
image.RepoName = imageName
image.Tag = "tag"
image.Digest = godigest.FromString("test").String()
image.MediaType = ispec.MediaTypeImageManifest
image.Manifests = []manifestStruct{
{
Digest: godigest.FromString("Digest").String(),

View file

@ -17,6 +17,7 @@ import (
jsoniter "github.com/json-iterator/go"
"github.com/olekukonko/tablewriter"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"gopkg.in/yaml.v2"
zotErrors "zotregistry.io/zot/errors"
@ -74,11 +75,14 @@ func (service searchService) getDerivedImageListGQL(ctx context.Context, config
Results{
RepoName,
Tag,
Digest,
MediaType,
Manifests {
Digest,
ConfigDigest,
Layers {Size Digest},
LastUpdated,
IsSigned,
Size
},
LastUpdated,
@ -107,11 +111,14 @@ func (service searchService) getBaseImageListGQL(ctx context.Context, config sea
Results{
RepoName,
Tag,
Digest,
MediaType,
Manifests {
Digest,
ConfigDigest,
Layers {Size Digest},
LastUpdated,
IsSigned,
Size
},
LastUpdated,
@ -139,11 +146,14 @@ func (service searchService) getImagesGQL(ctx context.Context, config searchConf
ImageList(repo: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
Platform {Os Arch}
IsSigned
Layers {Size Digest}
}
Size
@ -171,12 +181,15 @@ func (service searchService) getImagesByDigestGQL(ctx context.Context, config se
ImageListForDigest(id: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
IsSigned
Layers {Size Digest}
}
}
Size
IsSigned
}
@ -202,12 +215,15 @@ func (service searchService) getImagesByCveIDGQL(ctx context.Context, config sea
ImageListForCVE(id: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
IsSigned
Layers {Size Digest}
}
}
Size
IsSigned
}
@ -252,10 +268,13 @@ func (service searchService) getTagsForCVEGQL(ctx context.Context, config search
ImageListForCVE(id: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
IsSigned
Layers {Size Digest}
}
Size
@ -282,12 +301,15 @@ func (service searchService) getFixedTagsForCVEGQL(ctx context.Context, config s
ImageListWithCVEFixed(id: "%s", image: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
IsSigned
Layers {Size Digest}
}
}
Size
}
}
@ -426,12 +448,15 @@ func (service searchService) getImagesByCveID(ctx context.Context, config search
ImageListForCVE(id: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
IsSigned
Layers {Size Digest}
}
}
Size
}
}
@ -492,12 +517,15 @@ func (service searchService) getImagesByDigest(ctx context.Context, config searc
ImageListForDigest(id: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
IsSigned
Layers {Size Digest}
}
}
Size
}
}
@ -558,12 +586,15 @@ func (service searchService) getImageByNameAndCVEID(ctx context.Context, config
ImageListForCVE(id: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
IsSigned
Layers {Size Digest}
}
}
Size
}
}
@ -682,12 +713,15 @@ func (service searchService) getFixedTagsForCVE(ctx context.Context, config sear
ImageListWithCVEFixed (id: "%s", image: "%s") {
Results {
RepoName Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
Size
IsSigned
Layers {Size Digest}
}
}
Size
}
}
@ -984,8 +1018,10 @@ type imageStruct struct {
Tag string `json:"tag"`
Manifests []manifestStruct
Size string `json:"size"`
Digest string `json:"digest"`
MediaType string `json:"mediaType"`
IsSigned bool `json:"isSigned"`
verbose bool
IsSigned bool `json:"isSigned"`
}
type manifestStruct struct {
@ -1106,69 +1142,9 @@ func (img imageStruct) stringPlainText(maxImgNameLen, maxTagLen, maxPlatformLen
tagName += offset
}
for i := range img.Manifests {
manifestDigest, err := godigest.Parse(img.Manifests[i].Digest)
if err != nil {
return "", fmt.Errorf("error parsing manifest digest %s: %w", img.Manifests[i].Digest, err)
}
configDigest, err := godigest.Parse(img.Manifests[i].ConfigDigest)
if err != nil {
return "", fmt.Errorf("error parsing config digest %s: %w", img.Manifests[i].ConfigDigest, err)
}
platform := getPlatformStr(img.Manifests[i].Platform)
if maxPlatformLen > len(platform) {
offset = strings.Repeat(" ", maxPlatformLen-len(platform))
platform += offset
}
minifestDigestStr := ellipsize(manifestDigest.Encoded(), digestWidth, "")
configDigestStr := ellipsize(configDigest.Encoded(), configWidth, "")
imgSize, _ := strconv.ParseUint(img.Manifests[i].Size, 10, 64)
size := ellipsize(strings.ReplaceAll(humanize.Bytes(imgSize), " ", ""), sizeWidth, ellipsis)
isSigned := img.IsSigned
row := make([]string, 8) //nolint:gomnd
row[colImageNameIndex] = imageName
row[colTagIndex] = tagName
row[colDigestIndex] = minifestDigestStr
row[colPlatformIndex] = platform
row[colSizeIndex] = size
row[colIsSignedIndex] = strconv.FormatBool(isSigned)
if img.verbose {
row[colConfigIndex] = configDigestStr
row[colLayersIndex] = ""
}
table.Append(row)
if img.verbose {
for _, entry := range img.Manifests[i].Layers {
layerSize := entry.Size
size := ellipsize(strings.ReplaceAll(humanize.Bytes(uint64(layerSize)), " ", ""), sizeWidth, ellipsis)
layerDigest, err := godigest.Parse(entry.Digest)
if err != nil {
return "", fmt.Errorf("error parsing layer digest %s: %w", entry.Digest, err)
}
layerDigestStr := ellipsize(layerDigest.Encoded(), digestWidth, "")
layerRow := make([]string, 8) //nolint:gomnd
layerRow[colImageNameIndex] = ""
layerRow[colTagIndex] = ""
layerRow[colDigestIndex] = ""
layerRow[colPlatformIndex] = ""
layerRow[colSizeIndex] = size
layerRow[colConfigIndex] = ""
layerRow[colLayersIndex] = layerDigestStr
table.Append(layerRow)
}
}
err := addImageToTable(table, &img, maxPlatformLen, imageName, tagName)
if err != nil {
return "", err
}
table.Render()
@ -1176,9 +1152,125 @@ func (img imageStruct) stringPlainText(maxImgNameLen, maxTagLen, maxPlatformLen
return builder.String(), nil
}
func addImageToTable(table *tablewriter.Table, img *imageStruct, maxPlatformLen int,
imageName, tagName string,
) error {
switch img.MediaType {
case ispec.MediaTypeImageManifest:
return addManifestToTable(table, imageName, tagName, &img.Manifests[0], maxPlatformLen, img.verbose)
case ispec.MediaTypeImageIndex:
return addImageIndexToTable(table, img, maxPlatformLen, imageName, tagName)
}
return nil
}
func addImageIndexToTable(table *tablewriter.Table, img *imageStruct, maxPlatformLen int,
imageName, tagName string,
) error {
indexDigest, err := godigest.Parse(img.Digest)
if err != nil {
return fmt.Errorf("error parsing index digest %s: %w", indexDigest, err)
}
row := make([]string, rowWidth)
row[colImageNameIndex] = imageName
row[colTagIndex] = tagName
row[colDigestIndex] = ellipsize(indexDigest.Encoded(), digestWidth, "")
row[colPlatformIndex] = "*"
imgSize, _ := strconv.ParseUint(img.Size, 10, 64)
row[colSizeIndex] = ellipsize(strings.ReplaceAll(humanize.Bytes(imgSize), " ", ""), sizeWidth, ellipsis)
row[colIsSignedIndex] = strconv.FormatBool(img.IsSigned)
if img.verbose {
row[colConfigIndex] = ""
row[colLayersIndex] = ""
}
table.Append(row)
for i := range img.Manifests {
err := addManifestToTable(table, "", "", &img.Manifests[i], maxPlatformLen, img.verbose)
if err != nil {
return err
}
}
return nil
}
func addManifestToTable(table *tablewriter.Table, imageName, tagName string, manifest *manifestStruct,
maxPlatformLen int, verbose bool,
) error {
manifestDigest, err := godigest.Parse(manifest.Digest)
if err != nil {
return fmt.Errorf("error parsing manifest digest %s: %w", manifest.Digest, err)
}
configDigest, err := godigest.Parse(manifest.ConfigDigest)
if err != nil {
return fmt.Errorf("error parsing config digest %s: %w", manifest.ConfigDigest, err)
}
platform := getPlatformStr(manifest.Platform)
if maxPlatformLen > len(platform) {
offset := strings.Repeat(" ", maxPlatformLen-len(platform))
platform += offset
}
minifestDigestStr := ellipsize(manifestDigest.Encoded(), digestWidth, "")
configDigestStr := ellipsize(configDigest.Encoded(), configWidth, "")
imgSize, _ := strconv.ParseUint(manifest.Size, 10, 64)
size := ellipsize(strings.ReplaceAll(humanize.Bytes(imgSize), " ", ""), sizeWidth, ellipsis)
isSigned := manifest.IsSigned
row := make([]string, 8) //nolint:gomnd
row[colImageNameIndex] = imageName
row[colTagIndex] = tagName
row[colDigestIndex] = minifestDigestStr
row[colPlatformIndex] = platform
row[colSizeIndex] = size
row[colIsSignedIndex] = strconv.FormatBool(isSigned)
if verbose {
row[colConfigIndex] = configDigestStr
row[colLayersIndex] = ""
}
table.Append(row)
if verbose {
for _, entry := range manifest.Layers {
layerSize := entry.Size
size := ellipsize(strings.ReplaceAll(humanize.Bytes(uint64(layerSize)), " ", ""), sizeWidth, ellipsis)
layerDigest, err := godigest.Parse(entry.Digest)
if err != nil {
return fmt.Errorf("error parsing layer digest %s: %w", entry.Digest, err)
}
layerDigestStr := ellipsize(layerDigest.Encoded(), digestWidth, "")
layerRow := make([]string, 8) //nolint:gomnd
layerRow[colImageNameIndex] = ""
layerRow[colTagIndex] = ""
layerRow[colDigestIndex] = ""
layerRow[colPlatformIndex] = ""
layerRow[colSizeIndex] = size
layerRow[colConfigIndex] = ""
layerRow[colLayersIndex] = layerDigestStr
table.Append(layerRow)
}
}
return nil
}
func getPlatformStr(platf platform) string {
if platf.Arch == "" && platf.Os == "" {
return "N/A"
return ""
}
platform := platf.Os
@ -1332,15 +1424,6 @@ const (
layersWidth = 8
ellipsis = "..."
colImageNameIndex = 0
colTagIndex = 1
colDigestIndex = 2
colConfigIndex = 3
colPlatformIndex = 4
colIsSignedIndex = 5
colLayersIndex = 6
colSizeIndex = 7
cveIDWidth = 16
cveSeverityWidth = 8
cveTitleWidth = 48
@ -1351,3 +1434,16 @@ const (
defaultOutoutFormat = "text"
)
const (
colImageNameIndex = iota
colTagIndex
colPlatformIndex
colDigestIndex
colConfigIndex
colIsSignedIndex
colLayersIndex
colSizeIndex
rowWidth
)

View file

@ -6544,6 +6544,8 @@ func TestImageSummary(t *testing.T) {
Image(image:"%s:%s"){
RepoName
Tag
Digest
MediaType
Manifests {
Digest
ConfigDigest
@ -6569,6 +6571,8 @@ func TestImageSummary(t *testing.T) {
Image(image:"%s"){
RepoName,
Tag,
Digest,
MediaType,
Manifests {
Digest
ConfigDigest
@ -6679,6 +6683,8 @@ func TestImageSummary(t *testing.T) {
imgSummary := imgSummaryResponse.SingleImageSummary.ImageSummary
So(imgSummary.RepoName, ShouldContainSubstring, repoName)
So(imgSummary.Tag, ShouldContainSubstring, tagTarget)
So(imgSummary.Digest, ShouldContainSubstring, manifestDigest.Encoded())
So(imgSummary.MediaType, ShouldContainSubstring, ispec.MediaTypeImageManifest)
So(imgSummary.Manifests[0].ConfigDigest, ShouldContainSubstring, image.Manifest.Config.Digest.Encoded())
So(imgSummary.Manifests[0].Digest, ShouldContainSubstring, manifestDigest.Encoded())
So(len(imgSummary.Manifests[0].Layers), ShouldEqual, 1)

View file

@ -22,6 +22,8 @@ type RepoSummary struct {
type ImageSummary struct {
RepoName string `json:"repoName"`
Tag string `json:"tag"`
Digest string `json:"digest"`
MediaType string `json:"mediaType"`
Manifests []ManifestSummary `json:"manifests"`
Size string `json:"size"`
DownloadCount int `json:"downloadCount"`

View file

@ -132,8 +132,16 @@ func TestConvertErrors(t *testing.T) {
Convey("ImageManifest2ImageSummary", t, func() {
ctx := graphql.WithResponseContext(context.Background(),
graphql.DefaultErrorPresenter, graphql.DefaultRecover)
configBlob, err := json.Marshal(ispec.Image{
Platform: ispec.Platform{
OS: "os",
Architecture: "arch",
Variant: "var",
},
})
So(err, ShouldBeNil)
_, _, err := convert.ImageManifest2ImageSummary(
_, _, err = convert.ImageManifest2ImageSummary(
ctx,
"repo",
"tag",
@ -142,7 +150,7 @@ func TestConvertErrors(t *testing.T) {
repodb.RepoMetadata{},
repodb.ManifestMetadata{
ManifestBlob: []byte("{}"),
ConfigBlob: []byte("{}"),
ConfigBlob: configBlob,
},
mocks.CveInfoMock{
GetCVESummaryForImageFn: func(repo, reference string,
@ -168,6 +176,12 @@ func TestConvertErrors(t *testing.T) {
MediaType: ispec.MediaTypeImageManifest,
},
false,
repodb.RepoMetadata{
Tags: map[string]repodb.Descriptor{},
Statistics: map[string]repodb.DescriptorStatistics{},
Signatures: map[string]repodb.ManifestSignatures{},
Referrers: map[string][]repodb.ReferrerInfo{},
},
repodb.ManifestMetadata{
ManifestBlob: []byte("{}"),
ConfigBlob: []byte("bad json"),
@ -187,6 +201,7 @@ func TestConvertErrors(t *testing.T) {
Platform: ispec.Platform{
OS: "os",
Architecture: "arch",
Variant: "var",
},
})
So(err, ShouldBeNil)
@ -200,6 +215,12 @@ func TestConvertErrors(t *testing.T) {
MediaType: ispec.MediaTypeImageManifest,
},
false,
repodb.RepoMetadata{
Tags: map[string]repodb.Descriptor{},
Statistics: map[string]repodb.DescriptorStatistics{},
Signatures: map[string]repodb.ManifestSignatures{"dig": {"cosine": []repodb.SignatureInfo{{}}}},
Referrers: map[string][]repodb.ReferrerInfo{},
},
repodb.ManifestMetadata{
ManifestBlob: []byte("{}"),
ConfigBlob: configBlob,
@ -245,6 +266,33 @@ func TestConvertErrors(t *testing.T) {
}, log.NewLogger("debug", ""),
)
So(len(imageSummaries), ShouldEqual, 0)
// cveInfo present no error
_, imageSummaries = convert.RepoMeta2ExpandedRepoInfo(
ctx,
repodb.RepoMetadata{
Tags: map[string]repodb.Descriptor{
"tag1": {Digest: "dig", MediaType: ispec.MediaTypeImageManifest},
},
},
map[string]repodb.ManifestMetadata{
"dig": {
ManifestBlob: []byte("{}"),
ConfigBlob: []byte("{}"),
},
},
map[string]repodb.IndexData{},
convert.SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageFn: func(repo, reference string,
) (cveinfo.ImageCVESummary, error) {
return cveinfo.ImageCVESummary{}, ErrTestError
},
}, log.NewLogger("debug", ""),
)
So(len(imageSummaries), ShouldEqual, 1)
})
}

View file

@ -189,11 +189,14 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
maxSeverity string
manifestSummaries = make([]*gql_generated.ManifestSummary, 0, len(indexContent.Manifests))
indexBlobs = make(map[string]int64, 0)
indexDigestStr = indexDigest.String()
indexMediaType = ispec.MediaTypeImageIndex
)
for _, descriptor := range indexContent.Manifests {
manifestSummary, manifestBlobs, err := ImageManifest2ManifestSummary(ctx, repo, tag, descriptor, false,
manifestMetaMap[descriptor.Digest.String()], repoMeta.Referrers[descriptor.Digest.String()], cveInfo)
repoMeta, manifestMetaMap[descriptor.Digest.String()], repoMeta.Referrers[descriptor.Digest.String()], cveInfo)
if err != nil {
return &gql_generated.ImageSummary{}, map[string]int64{}, err
}
@ -244,6 +247,8 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
indexSummary := gql_generated.ImageSummary{
RepoName: &repo,
Tag: &tag,
Digest: &indexDigestStr,
MediaType: &indexMediaType,
Manifests: manifestSummaries,
LastUpdated: &indexLastUpdated,
IsSigned: &isSigned,
@ -272,6 +277,7 @@ func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest go
var (
manifestContent ispec.Manifest
manifestDigest = digest.String()
mediaType = ispec.MediaTypeImageManifest
)
err := json.Unmarshal(manifestMeta.ManifestBlob, &manifestContent)
@ -349,14 +355,17 @@ func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest go
}
imageSummary := gql_generated.ImageSummary{
RepoName: &repoName,
Tag: &tag,
RepoName: &repoName,
Tag: &tag,
Digest: &manifestDigest,
MediaType: &mediaType,
Manifests: []*gql_generated.ManifestSummary{
{
Digest: &manifestDigest,
ConfigDigest: &configDigest,
LastUpdated: &imageLastUpdated,
Size: &imageSize,
IsSigned: &isSigned,
Platform: &platform,
DownloadCount: &downloadCount,
Layers: getLayersSummaries(manifestContent),
@ -424,7 +433,8 @@ func getAnnotationsFromMap(annotationsMap map[string]string) []*gql_generated.An
}
func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descriptor ispec.Descriptor,
skipCVE bool, manifestMeta repodb.ManifestMetadata, referrersInfo []repodb.ReferrerInfo, cveInfo cveinfo.CveInfo,
skipCVE bool, repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata, referrersInfo []repodb.ReferrerInfo,
cveInfo cveinfo.CveInfo,
) (*gql_generated.ManifestSummary, map[string]int64, error) {
var (
manifestContent ispec.Manifest
@ -456,6 +466,7 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
configSize = manifestContent.Config.Size
imageLastUpdated = common.GetImageLastUpdated(configContent)
downloadCount = manifestMeta.DownloadCount
isSigned = false
)
opSys := configContent.OS
@ -492,6 +503,12 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
}
}
for _, signatures := range repoMeta.Signatures[manifestDigestStr] {
if len(signatures) > 0 {
isSigned = true
}
}
manifestSummary := gql_generated.ManifestSummary{
Digest: &manifestDigestStr,
ConfigDigest: &configDigest,
@ -501,6 +518,7 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
DownloadCount: &downloadCount,
Layers: getLayersSummaries(manifestContent),
History: historyEntries,
IsSigned: &isSigned,
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,

View file

@ -79,6 +79,7 @@ type ComplexityRoot struct {
ImageSummary struct {
Authors func(childComplexity int) int
Description func(childComplexity int) int
Digest func(childComplexity int) int
Documentation func(childComplexity int) int
DownloadCount func(childComplexity int) int
IsSigned func(childComplexity int) int
@ -86,6 +87,7 @@ type ComplexityRoot struct {
LastUpdated func(childComplexity int) int
Licenses func(childComplexity int) int
Manifests func(childComplexity int) int
MediaType func(childComplexity int) int
Referrers func(childComplexity int) int
RepoName func(childComplexity int) int
Score func(childComplexity int) int
@ -118,6 +120,7 @@ type ComplexityRoot struct {
Digest func(childComplexity int) int
DownloadCount func(childComplexity int) int
History func(childComplexity int) int
IsSigned func(childComplexity int) int
LastUpdated func(childComplexity int) int
Layers func(childComplexity int) int
Platform func(childComplexity int) int
@ -372,6 +375,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ImageSummary.Description(childComplexity), true
case "ImageSummary.Digest":
if e.complexity.ImageSummary.Digest == nil {
break
}
return e.complexity.ImageSummary.Digest(childComplexity), true
case "ImageSummary.Documentation":
if e.complexity.ImageSummary.Documentation == nil {
break
@ -421,6 +431,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ImageSummary.Manifests(childComplexity), true
case "ImageSummary.MediaType":
if e.complexity.ImageSummary.MediaType == nil {
break
}
return e.complexity.ImageSummary.MediaType(childComplexity), true
case "ImageSummary.Referrers":
if e.complexity.ImageSummary.Referrers == nil {
break
@ -561,6 +578,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ManifestSummary.History(childComplexity), true
case "ManifestSummary.IsSigned":
if e.complexity.ManifestSummary.IsSigned == nil {
break
}
return e.complexity.ManifestSummary.IsSigned(childComplexity), true
case "ManifestSummary.LastUpdated":
if e.complexity.ManifestSummary.LastUpdated == nil {
break
@ -1131,6 +1155,14 @@ type ImageSummary {
"""
Tag: String
"""
The digest of the descriptor of this image
"""
Digest: String
"""
The media type of the descriptor of this image
"""
MediaType: String
"""
List of manifests for all supported versions of the image for different operating systems and architectures
"""
Manifests: [ManifestSummary]
@ -1217,6 +1249,10 @@ type ManifestSummary {
"""
Size: String
"""
True if the manifest has a signature associated with it, false otherwise
"""
IsSigned: Boolean
"""
OS and architecture supported by this image
"""
Platform: Platform
@ -2587,6 +2623,10 @@ func (ec *executionContext) fieldContext_GlobalSearchResult_Images(ctx context.C
return ec.fieldContext_ImageSummary_RepoName(ctx, field)
case "Tag":
return ec.fieldContext_ImageSummary_Tag(ctx, field)
case "Digest":
return ec.fieldContext_ImageSummary_Digest(ctx, field)
case "MediaType":
return ec.fieldContext_ImageSummary_MediaType(ctx, field)
case "Manifests":
return ec.fieldContext_ImageSummary_Manifests(ctx, field)
case "Size":
@ -3027,6 +3067,88 @@ func (ec *executionContext) fieldContext_ImageSummary_Tag(ctx context.Context, f
return fc, nil
}
func (ec *executionContext) _ImageSummary_Digest(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ImageSummary_Digest(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Digest, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ImageSummary_Digest(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ImageSummary",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ImageSummary_MediaType(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ImageSummary_MediaType(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.MediaType, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ImageSummary_MediaType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ImageSummary",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ImageSummary_Manifests(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ImageSummary_Manifests(ctx, field)
if err != nil {
@ -3071,6 +3193,8 @@ func (ec *executionContext) fieldContext_ImageSummary_Manifests(ctx context.Cont
return ec.fieldContext_ManifestSummary_LastUpdated(ctx, field)
case "Size":
return ec.fieldContext_ManifestSummary_Size(ctx, field)
case "IsSigned":
return ec.fieldContext_ManifestSummary_IsSigned(ctx, field)
case "Platform":
return ec.fieldContext_ManifestSummary_Platform(ctx, field)
case "DownloadCount":
@ -4194,6 +4318,47 @@ func (ec *executionContext) fieldContext_ManifestSummary_Size(ctx context.Contex
return fc, nil
}
func (ec *executionContext) _ManifestSummary_IsSigned(ctx context.Context, field graphql.CollectedField, obj *ManifestSummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ManifestSummary_IsSigned(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.IsSigned, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*bool)
fc.Result = res
return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ManifestSummary_IsSigned(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ManifestSummary",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ManifestSummary_Platform(ctx context.Context, field graphql.CollectedField, obj *ManifestSummary) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ManifestSummary_Platform(ctx, field)
if err != nil {
@ -4779,6 +4944,10 @@ func (ec *executionContext) fieldContext_PaginatedImagesResult_Results(ctx conte
return ec.fieldContext_ImageSummary_RepoName(ctx, field)
case "Tag":
return ec.fieldContext_ImageSummary_Tag(ctx, field)
case "Digest":
return ec.fieldContext_ImageSummary_Digest(ctx, field)
case "MediaType":
return ec.fieldContext_ImageSummary_MediaType(ctx, field)
case "Manifests":
return ec.fieldContext_ImageSummary_Manifests(ctx, field)
case "Size":
@ -5663,6 +5832,10 @@ func (ec *executionContext) fieldContext_Query_Image(ctx context.Context, field
return ec.fieldContext_ImageSummary_RepoName(ctx, field)
case "Tag":
return ec.fieldContext_ImageSummary_Tag(ctx, field)
case "Digest":
return ec.fieldContext_ImageSummary_Digest(ctx, field)
case "MediaType":
return ec.fieldContext_ImageSummary_MediaType(ctx, field)
case "Manifests":
return ec.fieldContext_ImageSummary_Manifests(ctx, field)
case "Size":
@ -6160,6 +6333,10 @@ func (ec *executionContext) fieldContext_RepoInfo_Images(ctx context.Context, fi
return ec.fieldContext_ImageSummary_RepoName(ctx, field)
case "Tag":
return ec.fieldContext_ImageSummary_Tag(ctx, field)
case "Digest":
return ec.fieldContext_ImageSummary_Digest(ctx, field)
case "MediaType":
return ec.fieldContext_ImageSummary_MediaType(ctx, field)
case "Manifests":
return ec.fieldContext_ImageSummary_Manifests(ctx, field)
case "Size":
@ -6556,6 +6733,10 @@ func (ec *executionContext) fieldContext_RepoSummary_NewestImage(ctx context.Con
return ec.fieldContext_ImageSummary_RepoName(ctx, field)
case "Tag":
return ec.fieldContext_ImageSummary_Tag(ctx, field)
case "Digest":
return ec.fieldContext_ImageSummary_Digest(ctx, field)
case "MediaType":
return ec.fieldContext_ImageSummary_MediaType(ctx, field)
case "Manifests":
return ec.fieldContext_ImageSummary_Manifests(ctx, field)
case "Size":
@ -8827,6 +9008,14 @@ func (ec *executionContext) _ImageSummary(ctx context.Context, sel ast.Selection
out.Values[i] = ec._ImageSummary_Tag(ctx, field, obj)
case "Digest":
out.Values[i] = ec._ImageSummary_Digest(ctx, field, obj)
case "MediaType":
out.Values[i] = ec._ImageSummary_MediaType(ctx, field, obj)
case "Manifests":
out.Values[i] = ec._ImageSummary_Manifests(ctx, field, obj)
@ -9019,6 +9208,10 @@ func (ec *executionContext) _ManifestSummary(ctx context.Context, sel ast.Select
out.Values[i] = ec._ManifestSummary_Size(ctx, field, obj)
case "IsSigned":
out.Values[i] = ec._ManifestSummary_IsSigned(ctx, field, obj)
case "Platform":
out.Values[i] = ec._ManifestSummary_Platform(ctx, field, obj)

View file

@ -91,6 +91,10 @@ type ImageSummary struct {
RepoName *string `json:"RepoName"`
// Tag identifying the image within the repository
Tag *string `json:"Tag"`
// The digest of the descriptor of this image
Digest *string `json:"Digest"`
// The media type of the descriptor of this image
MediaType *string `json:"MediaType"`
// List of manifests for all supported versions of the image for different operating systems and architectures
Manifests []*ManifestSummary `json:"Manifests"`
// Total size of the files associated with all images (manifest, config, layers)
@ -162,6 +166,8 @@ type ManifestSummary struct {
LastUpdated *time.Time `json:"LastUpdated"`
// Total size of the files associated with this manifest (manifest, config, layers)
Size *string `json:"Size"`
// True if the manifest has a signature associated with it, false otherwise
IsSigned *bool `json:"IsSigned"`
// OS and architecture supported by this image
Platform *Platform `json:"Platform"`
// Total numer of image manifest downloads from this repository

View file

@ -124,6 +124,14 @@ type ImageSummary {
"""
Tag: String
"""
The digest of the descriptor of this image
"""
Digest: String
"""
The media type of the descriptor of this image
"""
MediaType: String
"""
List of manifests for all supported versions of the image for different operating systems and architectures
"""
Manifests: [ManifestSummary]
@ -210,6 +218,10 @@ type ManifestSummary {
"""
Size: String
"""
True if the manifest has a signature associated with it, false otherwise
"""
IsSigned: Boolean
"""
OS and architecture supported by this image
"""
Platform: Platform