0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-30 22:34:13 -05:00

fix(cli): make sure all needed ImageSummary properties are requested from the server (#1618)

Resolves #1597

Fix missing properties in some calls OS/Arch/IsSigned/LastUpdated.
Since the properties were missing from the graphql requests, zli was showing default values.

Update the tests to generate a tets image with the created date different from
the go detault value for time.Time{}. The tests are now checking a non-default
value in the responses client side, in order to avoid missing issues like this in the future.

Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
This commit is contained in:
Andrei Aaron 2023-07-14 09:22:14 +03:00 committed by GitHub
parent e3ac50b2a2
commit 74f769d2c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 208 additions and 129 deletions

View file

@ -757,7 +757,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
str = strings.TrimSpace(str) str = strings.TrimSpace(str)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(str, ShouldEqual, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 40d1f749 false 605B") So(str, ShouldEqual, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 linux/amd64 40d1f749 false 605B")
}) })
Convey("Test images by CVE ID - GQL - invalid CVE ID", t, func() { Convey("Test images by CVE ID - GQL - invalid CVE ID", t, func() {
@ -873,7 +873,7 @@ func TestServerCVEResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(strings.TrimSpace(str), ShouldEqual, So(strings.TrimSpace(str), ShouldEqual,
"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 40d1f749 false 605B") "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE zot-cve-test 0.0.1 linux/amd64 40d1f749 false 605B")
}) })
Convey("Test CVE by name and CVE ID - GQL - invalid name and CVE ID", t, func() { Convey("Test CVE by name and CVE ID - GQL - invalid name and CVE ID", t, func() {

View file

@ -473,7 +473,7 @@ func TestDerivedImageList(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:1.0 2694fdb0 false 824B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 9d9461ed false 860B")
}) })
Convey("Test derived images list fails", func() { Convey("Test derived images list fails", func() {
@ -546,7 +546,7 @@ func TestBaseImageList(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 3fc80493 false 494B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 214e4bed false 530B")
}) })
Convey("Test base images list fail", func() { Convey("Test base images list fail", func() {
@ -881,27 +881,27 @@ func TestOutputFormatGQL(t *testing.T) {
err := cmd.Execute() err := cmd.Execute()
So(err, ShouldBeNil) So(err, ShouldBeNil)
expectedStr := `{"repoName":"repo7","tag":"test:1.0",` + expectedStr := `{"repoName":"repo7","tag":"test:1.0",` +
`"digest":"sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731",` + `"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"mediaType":"application/vnd.oci.image.manifest.v1+json",` + `"mediaType":"application/vnd.oci.image.manifest.v1+json",` +
`"manifests":[{"digest":"sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731",` + `"manifests":[{"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"configDigest":"sha256:3a1d2d0cc867c06f01285a134f898e2f0b184036de70f331d4dd627f6f887a36",` + `"configDigest":"sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e",` +
`"lastUpdated":"0001-01-01T00:00:00Z","size":"492","platform":{"os":"linux","arch":"amd64",` + `"lastUpdated":"2023-01-01T12:00:00Z","size":"528","platform":{"os":"linux","arch":"amd64",` +
`"variant":""},"isSigned":false,"downloadCount":0,"layers":[{"size":"15","digest":` + `"variant":""},"isSigned":false,"downloadCount":0,"layers":[{"size":"15","digest":` +
`"sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6","score":0}],` + `"sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6","score":0}],` +
`"history":null,"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"artifactType":""}],` + `"history":null,"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"artifactType":""}],` +
`"size":"492","downloadCount":0,"lastUpdated":"0001-01-01T00:00:00Z","description":"","isSigned":false,` + `"size":"528","downloadCount":0,"lastUpdated":"2023-01-01T12:00:00Z","description":"","isSigned":false,` +
`"licenses":"","labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",` + `"licenses":"","labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",` +
`"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null}` + "\n" + `"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null}` + "\n" +
`{"repoName":"repo7","tag":"test:2.0",` + `{"repoName":"repo7","tag":"test:2.0",` +
`"digest":"sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731",` + `"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"mediaType":"application/vnd.oci.image.manifest.v1+json",` + `"mediaType":"application/vnd.oci.image.manifest.v1+json",` +
`"manifests":[{"digest":"sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731",` + `"manifests":[{"digest":"sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06",` +
`"configDigest":"sha256:3a1d2d0cc867c06f01285a134f898e2f0b184036de70f331d4dd627f6f887a36",` + `"configDigest":"sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e",` +
`"lastUpdated":"0001-01-01T00:00:00Z","size":"492","platform":{"os":"linux","arch":"amd64",` + `"lastUpdated":"2023-01-01T12:00:00Z","size":"528","platform":{"os":"linux","arch":"amd64",` +
`"variant":""},"isSigned":false,"downloadCount":0,"layers":[{"size":"15","digest":` + `"variant":""},"isSigned":false,"downloadCount":0,"layers":[{"size":"15","digest":` +
`"sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6","score":0}],` + `"sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6","score":0}],` +
`"history":null,"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"artifactType":""}],` + `"history":null,"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"artifactType":""}],` +
`"size":"492","downloadCount":0,"lastUpdated":"0001-01-01T00:00:00Z","description":"","isSigned":false,` + `"size":"528","downloadCount":0,"lastUpdated":"2023-01-01T12:00:00Z","description":"","isSigned":false,` +
`"licenses":"","labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",` + `"licenses":"","labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",` +
`"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null}` + "\n" `"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null}` + "\n"
// Output is supposed to be in json lines format, keep all spaces as is for verification // Output is supposed to be in json lines format, keep all spaces as is for verification
@ -923,27 +923,27 @@ func TestOutputFormatGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`) space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
expectedStr := `--- reponame: repo7 tag: test:1.0 ` + expectedStr := `--- reponame: repo7 tag: test:1.0 ` +
`digest: sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731 ` + `digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` + `mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
`digest: sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731 ` + `digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`configdigest: sha256:3a1d2d0cc867c06f01285a134f898e2f0b184036de70f331d4dd627f6f887a36 ` + `configdigest: sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e ` +
`lastupdated: 0001-01-01T00:00:00Z size: "492" platform: os: linux arch: amd64 variant: "" ` + `lastupdated: 2023-01-01T12:00:00Z size: "528" platform: os: linux arch: amd64 variant: "" ` +
`issigned: false downloadcount: 0 layers: - size: "15" ` + `issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` + `digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" ` + `history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" ` +
`size: "492" downloadcount: 0 lastupdated: 0001-01-01T00:00:00Z description: "" ` + `size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` + `issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] ` + `authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] ` +
`--- reponame: repo7 tag: test:2.0 ` + `--- reponame: repo7 tag: test:2.0 ` +
`digest: sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731 ` + `digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` + `mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
`digest: sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731 ` + `digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`configdigest: sha256:3a1d2d0cc867c06f01285a134f898e2f0b184036de70f331d4dd627f6f887a36 ` + `configdigest: sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e ` +
`lastupdated: 0001-01-01T00:00:00Z size: "492" platform: os: linux arch: amd64 variant: "" ` + `lastupdated: 2023-01-01T12:00:00Z size: "528" platform: os: linux arch: amd64 variant: "" ` +
`issigned: false downloadcount: 0 layers: - size: "15" ` + `issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` + `digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" ` + `history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" ` +
`size: "492" downloadcount: 0 lastupdated: 0001-01-01T00:00:00Z description: "" ` + `size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` + `issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: []` `authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: []`
So(strings.TrimSpace(str), ShouldEqual, expectedStr) So(strings.TrimSpace(str), ShouldEqual, expectedStr)
@ -964,27 +964,27 @@ func TestOutputFormatGQL(t *testing.T) {
space := regexp.MustCompile(`\s+`) space := regexp.MustCompile(`\s+`)
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
expectedStr := `--- reponame: repo7 tag: test:1.0 ` + expectedStr := `--- reponame: repo7 tag: test:1.0 ` +
`digest: sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731 ` + `digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` + `mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
`digest: sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731 ` + `digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`configdigest: sha256:3a1d2d0cc867c06f01285a134f898e2f0b184036de70f331d4dd627f6f887a36 ` + `configdigest: sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e ` +
`lastupdated: 0001-01-01T00:00:00Z size: "492" platform: os: linux arch: amd64 variant: "" ` + `lastupdated: 2023-01-01T12:00:00Z size: "528" platform: os: linux arch: amd64 variant: "" ` +
`issigned: false downloadcount: 0 layers: - size: "15" ` + `issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` + `digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" ` + `history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" ` +
`size: "492" downloadcount: 0 lastupdated: 0001-01-01T00:00:00Z description: "" ` + `size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` + `issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] ` + `authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: [] ` +
`--- reponame: repo7 tag: test:2.0 ` + `--- reponame: repo7 tag: test:2.0 ` +
`digest: sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731 ` + `digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` + `mediatype: application/vnd.oci.image.manifest.v1+json manifests: - ` +
`digest: sha256:883fc0c54b8cb0c991399f950dae19bcc3561ba4f6ca4d3fb0ca446f2de03731 ` + `digest: sha256:51e18f508fd7125b0831ff9a22ba74cd79f0b934e77661ff72cfb54896951a06 ` +
`configdigest: sha256:3a1d2d0cc867c06f01285a134f898e2f0b184036de70f331d4dd627f6f887a36 ` + `configdigest: sha256:d14faead7d60053bad0d62e5ceb0031df28037d8c636d7911179b2f874ee004e ` +
`lastupdated: 0001-01-01T00:00:00Z size: "492" platform: os: linux arch: amd64 variant: "" ` + `lastupdated: 2023-01-01T12:00:00Z size: "528" platform: os: linux arch: amd64 variant: "" ` +
`issigned: false downloadcount: 0 layers: - size: "15" ` + `issigned: false downloadcount: 0 layers: - size: "15" ` +
`digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` + `digest: sha256:b8781e8844f5b7bf6f2f8fa343de18ec471c3b278027355bc34c120585ff04f6 score: 0 ` +
`history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" ` + `history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" ` +
`size: "492" downloadcount: 0 lastupdated: 0001-01-01T00:00:00Z description: "" ` + `size: "528" downloadcount: 0 lastupdated: 2023-01-01T12:00:00Z description: "" ` +
`issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` + `issigned: false licenses: "" labels: "" title: "" source: "" documentation: "" ` +
`authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: []` `authors: "" vendor: "" vulnerabilities: maxseverity: "" count: 0 referrers: []`
So(strings.TrimSpace(str), ShouldEqual, expectedStr) So(strings.TrimSpace(str), ShouldEqual, expectedStr)
@ -1043,8 +1043,8 @@ func TestServerResponseGQL(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
Convey("Test all images invalid output format", func() { Convey("Test all images invalid output format", func() {
args := []string{"imagetest", "-o", "random"} args := []string{"imagetest", "-o", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url)) configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
@ -1076,13 +1076,13 @@ func TestServerResponseGQL(t *testing.T) {
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ): // Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE // REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE
// repo7 test:2.0 linux/amd64 a0ca253b b8781e88 false 492B // repo7 test:2.0 linux/amd64 51e18f50 d14faead false 528B
// b8781e88 15B // b8781e88 15B
// repo7 test:1.0 linux/amd64 a0ca253b b8781e88 false 492B // repo7 test:1.0 linux/amd64 51e18f50 d14faead false 528B
// b8781e88 15B // b8781e88 15B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE") So(actual, ShouldContainSubstring, "REPOSITORY 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:2.0 linux/amd64 51e18f50 d14faead false 528B b8781e88 15B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 3a1d2d0c false 492B 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() { Convey("Test all images with debug flag", func() {
@ -1101,8 +1101,8 @@ func TestServerResponseGQL(t *testing.T) {
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "GET") So(actual, ShouldContainSubstring, "GET")
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
}) })
Convey("Test image by name config url", func() { Convey("Test image by name config url", func() {
@ -1120,8 +1120,8 @@ func TestServerResponseGQL(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
Convey("with shorthand", func() { Convey("with shorthand", func() {
args := []string{"imagetest", "-n", "repo7"} args := []string{"imagetest", "-n", "repo7"}
@ -1138,8 +1138,8 @@ func TestServerResponseGQL(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
}) })
Convey("invalid output format", func() { Convey("invalid output format", func() {
@ -1158,7 +1158,7 @@ func TestServerResponseGQL(t *testing.T) {
}) })
Convey("Test image by digest", func() { Convey("Test image by digest", func() {
args := []string{"imagetest", "--digest", "883fc0c5"} args := []string{"imagetest", "--digest", "51e18f50"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url)) configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService)) cmd := NewImageCommand(new(searchService))
@ -1176,11 +1176,11 @@ func TestServerResponseGQL(t *testing.T) {
// repo7 test:2.0 a0ca253b 15B // repo7 test:2.0 a0ca253b 15B
// repo7 test:1.0 a0ca253b 15B // repo7 test:1.0 a0ca253b 15B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
Convey("with shorthand", func() { Convey("with shorthand", func() {
args := []string{"imagetest", "-d", "883fc0c5"} args := []string{"imagetest", "-d", "51e18f50"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url)) configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService)) cmd := NewImageCommand(new(searchService))
@ -1194,8 +1194,8 @@ func TestServerResponseGQL(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
}) })
Convey("nonexistent digest", func() { Convey("nonexistent digest", func() {
@ -1213,7 +1213,7 @@ func TestServerResponseGQL(t *testing.T) {
}) })
Convey("invalid output format", func() { Convey("invalid output format", func() {
args := []string{"imagetest", "--digest", "883fc0c5", "-o", "random"} args := []string{"imagetest", "--digest", "51e18f50", "-o", "random"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url)) configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := NewImageCommand(new(searchService)) cmd := NewImageCommand(new(searchService))
@ -1304,8 +1304,8 @@ func TestServerResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
}) })
Convey("Test all images verbose", func() { Convey("Test all images verbose", func() {
@ -1324,13 +1324,13 @@ func TestServerResponse(t *testing.T) {
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ): // Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE // REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE
// repo7 test:2.0 linux/amd64 a0ca253b b8781e88 false 492B // repo7 test:2.0 linux/amd64 51e18f50 d14faead false 528B
// b8781e88 15B // b8781e88 15B
// repo7 test:1.0 linux/amd64 a0ca253b b8781e88 false 492B // repo7 test:1.0 linux/amd64 51e18f50 d14faead false 528B
// b8781e88 15B // b8781e88 15B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE") So(actual, ShouldContainSubstring, "REPOSITORY 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:2.0 linux/amd64 51e18f50 d14faead false 528B b8781e88 15B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 3a1d2d0c false 492B b8781e88 15B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 d14faead false 528B b8781e88 15B")
}) })
Convey("Test image by name", func() { Convey("Test image by name", func() {
@ -1348,12 +1348,12 @@ func TestServerResponse(t *testing.T) {
str := space.ReplaceAllString(buff.String(), " ") str := space.ReplaceAllString(buff.String(), " ")
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
}) })
Convey("Test image by digest", func() { Convey("Test image by digest", func() {
args := []string{"imagetest", "--digest", "883fc0c5"} args := []string{"imagetest", "--digest", "51e18f50"}
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url)) configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, url))
defer os.Remove(configPath) defer os.Remove(configPath)
cmd := MockNewImageCommand(new(searchService)) cmd := MockNewImageCommand(new(searchService))
@ -1368,11 +1368,11 @@ func TestServerResponse(t *testing.T) {
actual := strings.TrimSpace(str) actual := strings.TrimSpace(str)
// Actual cli output should be something similar to (order of images may differ): // Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST SIZE // REPOSITORY TAG OS/ARCH DIGEST SIZE
// repo7 test:2.0 linux/amd64 a0ca253b 492B // repo7 test:2.0 linux/amd64 51e18f50 528B
// repo7 test:1.0 linux/amd64 a0ca253b 492B // repo7 test:1.0 linux/amd64 51e18f50 528B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:2.0 linux/amd64 51e18f50 false 528B")
So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 883fc0c5 false 492B") So(actual, ShouldContainSubstring, "repo7 test:1.0 linux/amd64 51e18f50 false 528B")
Convey("nonexistent digest", func() { Convey("nonexistent digest", func() {
args := []string{"imagetest", "--digest", "d1g35t"} args := []string{"imagetest", "--digest", "d1g35t"}
@ -1504,8 +1504,8 @@ func runDisplayIndexTests(baseURL string) {
// Actual cli output should be something similar to (order of images may differ): // Actual cli output should be something similar to (order of images may differ):
// REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE // REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE
// repo multi-arch * 0f844b3e false 1.5kB // repo multi-arch * 0f844b3e false 1.5kB
// linux/amd64 97b0d65c false 634B // linux/amd64 2ab1a275 false 634B
// windows/arm64/v6 dcfa3a9c false 444B // windows/arm64/v6 55fdd23a false 444B
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo multi-arch * 0f844b3e false 1.5kB ") So(actual, ShouldContainSubstring, "repo multi-arch * 0f844b3e false 1.5kB ")
So(actual, ShouldContainSubstring, "linux/amd64 2ab1a275 false 634B ") So(actual, ShouldContainSubstring, "linux/amd64 2ab1a275 false 634B ")
@ -1682,7 +1682,7 @@ func MockSearchImage(searchConfig searchConfig) error {
} }
func uploadManifest(url string) error { func uploadManifest(url string) error {
// create a blob/layer // create and upload a blob/layer
resp, _ := resty.R().Post(url + "/v2/repo7/blobs/uploads/") resp, _ := resty.R().Post(url + "/v2/repo7/blobs/uploads/")
loc := test.Location(url, resp) loc := test.Location(url, resp)
@ -1691,10 +1691,32 @@ func uploadManifest(url string) error {
_, _ = resty.R().SetQueryParam("digest", digest.String()). _, _ = resty.R().SetQueryParam("digest", digest.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(loc) 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 // upload image config blob
resp, _ = resty.R().Post(url + "/v2/repo7/blobs/uploads/") resp, _ = resty.R().Post(url + "/v2/repo7/blobs/uploads/")
loc = test.Location(url, resp) loc = test.Location(url, resp)
cblob, cdigest := test.GetImageConfig()
_, _ = resty.R(). _, _ = resty.R().
SetContentLength(true). SetContentLength(true).
@ -1721,7 +1743,7 @@ func uploadManifest(url string) error {
} }
manifest.SchemaVersion = 2 manifest.SchemaVersion = 2
content, err := json.Marshal(manifest) content, err = json.Marshal(manifest)
if err != nil { if err != nil {
return err return err
} }
@ -1775,10 +1797,32 @@ func uploadManifestDerivedBase(url string) error {
_, _ = resty.R().SetQueryParam("digest", digest3.String()). _, _ = resty.R().SetQueryParam("digest", digest3.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content3).Post(url + "/v2/repo7/blobs/uploads/") 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 // upload image config blob
resp, _ := resty.R().Post(url + "/v2/repo7/blobs/uploads/") resp, _ := resty.R().Post(url + "/v2/repo7/blobs/uploads/")
loc := test.Location(url, resp) loc := test.Location(url, resp)
cblob, cdigest := test.GetImageConfig()
_, _ = resty.R(). _, _ = resty.R().
SetContentLength(true). SetContentLength(true).

View file

@ -86,21 +86,21 @@ func (service searchService) getDerivedImageListGQL(ctx context.Context, config
{ {
DerivedImageList(image:"%s", requestedPage: {sortBy: ALPHABETIC_ASC}){ DerivedImageList(image:"%s", requestedPage: {sortBy: ALPHABETIC_ASC}){
Results{ Results{
RepoName, RepoName Tag
Tag, Digest
Digest, MediaType
MediaType,
Manifests { Manifests {
Digest, Digest
ConfigDigest, ConfigDigest
Layers {Size Digest},
LastUpdated,
IsSigned,
Size Size
}, Platform {Os Arch}
LastUpdated, IsSigned
IsSigned, Layers {Size Digest}
LastUpdated
}
LastUpdated
Size Size
IsSigned
} }
} }
}`, derivedImage) }`, derivedImage)
@ -154,13 +154,16 @@ func (service searchService) globalSearchGQL(ctx context.Context, config searchC
MediaType MediaType
Digest Digest
Size Size
IsSigned
LastUpdated
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Platform {Os Arch} Platform {Os Arch}
Size Size
IsSigned IsSigned
Layers {Digest Size} Layers {Size Digest}
LastUpdated
} }
} }
Repos { Repos {
@ -191,21 +194,21 @@ func (service searchService) getBaseImageListGQL(ctx context.Context, config sea
{ {
BaseImageList(image:"%s", requestedPage: {sortBy: ALPHABETIC_ASC}){ BaseImageList(image:"%s", requestedPage: {sortBy: ALPHABETIC_ASC}){
Results{ Results{
RepoName, RepoName Tag
Tag, Digest
Digest, MediaType
MediaType,
Manifests { Manifests {
Digest, Digest
ConfigDigest, ConfigDigest
Layers {Size Digest},
LastUpdated,
IsSigned,
Size Size
}, Platform {Os Arch}
LastUpdated, IsSigned
IsSigned, Layers {Size Digest}
LastUpdated
}
LastUpdated
Size Size
IsSigned
} }
} }
}`, baseImage) }`, baseImage)
@ -227,18 +230,20 @@ func (service searchService) getImagesGQL(ctx context.Context, config searchConf
{ {
ImageList(repo: "%s", requestedPage: {sortBy: ALPHABETIC_ASC}) { ImageList(repo: "%s", requestedPage: {sortBy: ALPHABETIC_ASC}) {
Results { Results {
RepoName Tag RepoName Tag
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch} Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
} LastUpdated
Size }
LastUpdated
Size
IsSigned IsSigned
} }
} }
@ -262,17 +267,20 @@ func (service searchService) getImagesByDigestGQL(ctx context.Context, config se
{ {
ImageListForDigest(id: "%s", requestedPage: {sortBy: ALPHABETIC_ASC}) { ImageListForDigest(id: "%s", requestedPage: {sortBy: ALPHABETIC_ASC}) {
Results { Results {
RepoName Tag RepoName Tag
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
} LastUpdated
Size }
LastUpdated
Size
IsSigned IsSigned
} }
} }
@ -296,17 +304,20 @@ func (service searchService) getImagesByCveIDGQL(ctx context.Context, config sea
{ {
ImageListForCVE(id: "%s", requestedPage: {sortBy: ALPHABETIC_ASC}) { ImageListForCVE(id: "%s", requestedPage: {sortBy: ALPHABETIC_ASC}) {
Results { Results {
RepoName Tag RepoName Tag
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
} LastUpdated
Size }
LastUpdated
Size
IsSigned IsSigned
} }
} }
@ -353,13 +364,17 @@ func (service searchService) getTagsForCVEGQL(ctx context.Context, config search
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
} LastUpdated
}
LastUpdated
Size Size
IsSigned
} }
} }
}`, }`,
@ -382,17 +397,21 @@ func (service searchService) getFixedTagsForCVEGQL(ctx context.Context, config s
{ {
ImageListWithCVEFixed(id: "%s", image: "%s") { ImageListWithCVEFixed(id: "%s", image: "%s") {
Results { Results {
RepoName Tag RepoName Tag
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
} LastUpdated
Size }
LastUpdated
Size
IsSigned
} }
} }
}`, }`,
@ -567,17 +586,21 @@ func (service searchService) getImagesByCveID(ctx context.Context, config search
`{ `{
ImageListForCVE(id: "%s") { ImageListForCVE(id: "%s") {
Results { Results {
RepoName Tag RepoName Tag
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
} LastUpdated
Size }
LastUpdated
Size
IsSigned
} }
} }
}`, }`,
@ -636,17 +659,21 @@ func (service searchService) getImagesByDigest(ctx context.Context, config searc
`{ `{
ImageListForDigest(id: "%s") { ImageListForDigest(id: "%s") {
Results { Results {
RepoName Tag RepoName Tag
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
LastUpdated
} }
Size LastUpdated
Size
IsSigned
} }
} }
}`, }`,
@ -705,17 +732,21 @@ func (service searchService) getImageByNameAndCVEID(ctx context.Context, config
`{ `{
ImageListForCVE(id: "%s") { ImageListForCVE(id: "%s") {
Results { Results {
RepoName Tag RepoName Tag
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
LastUpdated
} }
Size LastUpdated
Size
IsSigned
} }
} }
}`, }`,
@ -832,17 +863,21 @@ func (service searchService) getFixedTagsForCVE(ctx context.Context, config sear
{ {
ImageListWithCVEFixed (id: "%s", image: "%s") { ImageListWithCVEFixed (id: "%s", image: "%s") {
Results { Results {
RepoName Tag RepoName Tag
Digest Digest
MediaType MediaType
Manifests { Manifests {
Digest Digest
ConfigDigest ConfigDigest
Size Size
Platform {Os Arch}
IsSigned IsSigned
Layers {Size Digest} Layers {Size Digest}
} LastUpdated
Size }
LastUpdated
Size
IsSigned
} }
} }
}`, cvid, imageName) }`, cvid, imageName)