Skip to content

Commit 3c86f2b

Browse files
committed
manifest: Add unit tests for manifest inspect command
Add tests for tag/digest references and verbose modes Signed-off-by: ChengyuZhu6 <[email protected]>
1 parent 4e9ec42 commit 3c86f2b

File tree

5 files changed

+276
-24
lines changed

5 files changed

+276
-24
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package manifest
18+
19+
import (
20+
"encoding/json"
21+
"testing"
22+
23+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
24+
"gotest.tools/v3/assert"
25+
26+
"github.com/containerd/nerdctl/mod/tigron/test"
27+
"github.com/containerd/nerdctl/mod/tigron/tig"
28+
29+
"github.com/containerd/nerdctl/v2/pkg/manifesttypes"
30+
"github.com/containerd/nerdctl/v2/pkg/testutil"
31+
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
32+
)
33+
34+
const (
35+
testImageName = "alpine"
36+
testPlatform = "linux/amd64"
37+
)
38+
39+
type testData struct {
40+
imageName string
41+
platform string
42+
imageRef string
43+
manifestDigest string
44+
configDigest string
45+
rawData string
46+
}
47+
48+
func newTestData(imageName, platform string) *testData {
49+
return &testData{
50+
imageName: imageName,
51+
platform: platform,
52+
imageRef: testutil.GetTestImage(imageName),
53+
manifestDigest: testutil.GetTestImageManifestDigest(imageName, platform),
54+
configDigest: testutil.GetTestImageConfigDigest(imageName, platform),
55+
rawData: testutil.GetTestImageRaw(imageName, platform),
56+
}
57+
}
58+
59+
func (td *testData) imageWithDigest() string {
60+
return testutil.GetTestImageWithoutTag(td.imageName) + "@" + td.manifestDigest
61+
}
62+
63+
func (td *testData) isAmd64Platform(platform *ocispec.Platform) bool {
64+
return platform != nil &&
65+
platform.Architecture == "amd64" &&
66+
platform.OS == "linux"
67+
}
68+
69+
func TestManifestInspect(t *testing.T) {
70+
testCase := nerdtest.Setup()
71+
td := newTestData(testImageName, testPlatform)
72+
73+
testCase.SubTests = []*test.Case{
74+
{
75+
Description: "tag-non-verbose",
76+
Command: test.Command("manifest", "inspect", td.imageRef),
77+
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
78+
var manifest manifesttypes.DockerManifestListStruct
79+
assert.NilError(t, json.Unmarshal([]byte(stdout), &manifest))
80+
81+
assert.Equal(t, manifest.SchemaVersion, testutil.GetTestImageSchemaVersion(td.imageName))
82+
assert.Equal(t, manifest.MediaType, testutil.GetTestImageMediaType(td.imageName))
83+
assert.Assert(t, len(manifest.Manifests) > 0)
84+
85+
var foundManifest *ocispec.Descriptor
86+
for _, m := range manifest.Manifests {
87+
if td.isAmd64Platform(m.Platform) {
88+
foundManifest = &m
89+
break
90+
}
91+
}
92+
assert.Assert(t, foundManifest != nil, "should find amd64 platform manifest")
93+
assert.Equal(t, foundManifest.Digest.String(), td.manifestDigest)
94+
assert.Equal(t, foundManifest.MediaType, testutil.GetTestImagePlatformMediaType(td.imageName, td.platform))
95+
}),
96+
},
97+
{
98+
Description: "tag-verbose",
99+
Command: test.Command("manifest", "inspect", td.imageRef, "--verbose"),
100+
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
101+
var entries []manifesttypes.DockerManifestEntry
102+
assert.NilError(t, json.Unmarshal([]byte(stdout), &entries))
103+
assert.Assert(t, len(entries) > 0)
104+
105+
var foundEntry *manifesttypes.DockerManifestEntry
106+
for _, e := range entries {
107+
if td.isAmd64Platform(e.Descriptor.Platform) {
108+
foundEntry = &e
109+
break
110+
}
111+
}
112+
assert.Assert(t, foundEntry != nil, "should find amd64 platform entry")
113+
114+
expectedRef := td.imageRef + "@" + td.manifestDigest
115+
assert.Equal(t, foundEntry.Ref, expectedRef)
116+
assert.Equal(t, foundEntry.Descriptor.Digest.String(), td.manifestDigest)
117+
assert.Equal(t, foundEntry.Descriptor.MediaType, testutil.GetTestImagePlatformMediaType(td.imageName, td.platform))
118+
assert.Equal(t, foundEntry.Raw, td.rawData)
119+
}),
120+
},
121+
{
122+
Description: "digest-non-verbose",
123+
Command: test.Command("manifest", "inspect", td.imageWithDigest()),
124+
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
125+
var manifest manifesttypes.DockerManifestStruct
126+
assert.NilError(t, json.Unmarshal([]byte(stdout), &manifest))
127+
128+
assert.Equal(t, manifest.SchemaVersion, testutil.GetTestImageSchemaVersion(td.imageName))
129+
assert.Equal(t, manifest.MediaType, testutil.GetTestImagePlatformMediaType(td.imageName, td.platform))
130+
assert.Equal(t, manifest.Config.Digest.String(), td.configDigest)
131+
}),
132+
},
133+
{
134+
Description: "digest-verbose",
135+
Command: test.Command("manifest", "inspect", td.imageWithDigest(), "--verbose"),
136+
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
137+
var entry manifesttypes.DockerManifestEntry
138+
assert.NilError(t, json.Unmarshal([]byte(stdout), &entry))
139+
140+
assert.Equal(t, entry.Ref, td.imageWithDigest())
141+
assert.Equal(t, entry.Descriptor.Digest.String(), td.manifestDigest)
142+
assert.Equal(t, entry.Descriptor.MediaType, testutil.GetTestImagePlatformMediaType(td.imageName, td.platform))
143+
assert.Equal(t, entry.Raw, td.rawData)
144+
}),
145+
},
146+
}
147+
148+
testCase.Run(t)
149+
}

cmd/nerdctl/manifest/manifest_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package manifest
18+
19+
import (
20+
"testing"
21+
22+
"github.com/containerd/nerdctl/v2/pkg/testutil"
23+
)
24+
25+
func TestMain(m *testing.M) {
26+
testutil.M(m)
27+
}

pkg/testutil/images.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@
55
alpine:
66
ref: "ghcr.io/stargz-containers/alpine"
77
tag: "3.13-org"
8+
schemaversion: 2
9+
mediatype: "application/vnd.docker.distribution.manifest.list.v2+json"
810
digest: "sha256:ec14c7992a97fc11425907e908340c6c3d6ff602f5f13d899e6b7027c9b4133a"
911
variants: ["linux/amd64", "linux/arm64"]
12+
manifests:
13+
linux/amd64:
14+
mediatype: "application/vnd.docker.distribution.manifest.v2+json"
15+
manifest: "sha256:e103c1b4bf019dc290bcc7aca538dc2bf7a9d0fc836e186f5fa34945c5168310"
16+
config: "sha256:49f356fa4513676c5e22e3a8404aad6c7262cc7aaed15341458265320786c58c"
17+
raw: "ewogICAic2NoZW1hVmVyc2lvbiI6IDIsCiAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsCiAgICJjb25maWciOiB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5jb250YWluZXIuaW1hZ2UudjEranNvbiIsCiAgICAgICJzaXplIjogMTQ3MiwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6NDlmMzU2ZmE0NTEzNjc2YzVlMjJlM2E4NDA0YWFkNmM3MjYyY2M3YWFlZDE1MzQxNDU4MjY1MzIwNzg2YzU4YyIKICAgfSwKICAgImxheWVycyI6IFsKICAgICAgewogICAgICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLAogICAgICAgICAic2l6ZSI6IDI4MTE5NDcsCiAgICAgICAgICJkaWdlc3QiOiAic2hhMjU2OmNhM2NkNDJhN2M5NTI1ZjZjZTNkNjRjMWE3MDk4MjYxM2E4MjM1ZjBjYzA1N2VjOTI0NDA1MjkyMTg1M2VmMTUiCiAgICAgIH0KICAgXQp9"
1018

1119
busybox:
1220
ref: "ghcr.io/containerd/busybox"

pkg/testutil/images_linux.go

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,98 @@ var rawImagesList string
2929

3030
var testImagesOnce sync.Once
3131

32+
type manifestInfo struct {
33+
Config string `yaml:"config,omitempty"`
34+
Manifest string `yaml:"manifest,omitempty"`
35+
MediaType string `yaml:"mediatype,omitempty"`
36+
Raw string `yaml:"raw,omitempty"`
37+
}
38+
3239
type TestImage struct {
33-
Ref string `yaml:"ref"`
34-
Tag string `yaml:"tag,omitempty"`
35-
Digest string `yaml:"digest,omitempty"`
36-
Variants []string `yaml:"variants,omitempty"`
40+
Ref string `yaml:"ref"`
41+
Tag string `yaml:"tag,omitempty"`
42+
SchemaVersion int `yaml:"schemaversion,omitempty"`
43+
MediaType string `yaml:"mediatype,omitempty"`
44+
Digest string `yaml:"digest,omitempty"`
45+
Variants []string `yaml:"variants,omitempty"`
46+
Manifests map[string]manifestInfo `yaml:"manifests,omitempty"`
3747
}
3848

3949
var testImages map[string]TestImage
4050

41-
func getImage(key string) string {
51+
// internal helper to lookup TestImage by key, panics if not found
52+
func lookup(key string) TestImage {
4253
testImagesOnce.Do(func() {
4354
if err := yaml.Unmarshal([]byte(rawImagesList), &testImages); err != nil {
4455
fmt.Printf("Error unmarshaling test images YAML file: %v\n", err)
4556
panic("testing is broken")
4657
}
4758
})
48-
49-
var im TestImage
50-
var ok bool
51-
52-
if im, ok = testImages[key]; !ok {
59+
im, ok := testImages[key]
60+
if !ok {
5361
fmt.Printf("Image %s was not found in images list\n", key)
5462
panic("testing is broken")
5563
}
64+
return im
65+
}
5666

67+
func GetTestImage(key string) string {
68+
im := lookup(key)
5769
return im.Ref + ":" + im.Tag
5870
}
71+
72+
func GetTestImageWithoutTag(key string) string {
73+
im := lookup(key)
74+
return im.Ref
75+
}
76+
77+
func GetTestImageConfigDigest(key, platform string) string {
78+
im := lookup(key)
79+
pd, ok := im.Manifests[platform]
80+
if !ok {
81+
panic(fmt.Sprintf("platform %s not found for image %s", platform, key))
82+
}
83+
return pd.Config
84+
}
85+
86+
func GetTestImageManifestDigest(key, platform string) string {
87+
im := lookup(key)
88+
pd, ok := im.Manifests[platform]
89+
if !ok {
90+
panic(fmt.Sprintf("platform %s not found for image %s", platform, key))
91+
}
92+
return pd.Manifest
93+
}
94+
95+
func GetTestImageDigest(key string) string {
96+
im := lookup(key)
97+
return im.Digest
98+
}
99+
100+
func GetTestImageMediaType(key string) string {
101+
im := lookup(key)
102+
return im.MediaType
103+
}
104+
105+
func GetTestImageSchemaVersion(key string) int {
106+
im := lookup(key)
107+
return im.SchemaVersion
108+
}
109+
110+
func GetTestImagePlatformMediaType(key, platform string) string {
111+
im := lookup(key)
112+
pd, ok := im.Manifests[platform]
113+
if !ok {
114+
panic(fmt.Sprintf("platform %s not found for image %s", platform, key))
115+
}
116+
return pd.MediaType
117+
}
118+
119+
func GetTestImageRaw(key, platform string) string {
120+
im := lookup(key)
121+
pd, ok := im.Manifests[platform]
122+
if !ok {
123+
panic(fmt.Sprintf("platform %s not found for image %s", platform, key))
124+
}
125+
return pd.Raw
126+
}

pkg/testutil/testutil_linux.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,23 @@
1717
package testutil
1818

1919
var (
20-
AlpineImage = getImage("alpine")
21-
BusyboxImage = getImage("busybox")
22-
DockerAuthImage = getImage("docker_auth")
23-
FluentdImage = getImage("fluentd")
24-
GolangImage = getImage("golang")
25-
KuboImage = getImage("kubo")
26-
MariaDBImage = getImage("mariadb")
27-
NginxAlpineImage = getImage("nginx")
28-
RegistryImageStable = getImage("registry")
29-
SystemdImage = getImage("stargz")
30-
WordpressImage = getImage("wordpress")
20+
AlpineImage = GetTestImage("alpine")
21+
BusyboxImage = GetTestImage("busybox")
22+
DockerAuthImage = GetTestImage("docker_auth")
23+
FluentdImage = GetTestImage("fluentd")
24+
GolangImage = GetTestImage("golang")
25+
KuboImage = GetTestImage("kubo")
26+
MariaDBImage = GetTestImage("mariadb")
27+
NginxAlpineImage = GetTestImage("nginx")
28+
RegistryImageStable = GetTestImage("registry")
29+
SystemdImage = GetTestImage("stargz")
30+
WordpressImage = GetTestImage("wordpress")
3131

3232
CommonImage = AlpineImage
3333

34-
FedoraESGZImage = getImage("fedora_esgz") // eStargz
35-
FfmpegSociImage = getImage("ffmpeg_soci") // SOCI
36-
UbuntuImage = getImage("ubuntu") // Large enough for testing soci index creation
34+
FedoraESGZImage = GetTestImage("fedora_esgz") // eStargz
35+
FfmpegSociImage = GetTestImage("ffmpeg_soci") // SOCI
36+
UbuntuImage = GetTestImage("ubuntu") // Large enough for testing soci index creation
3737
)
3838

3939
const (

0 commit comments

Comments
 (0)