1 package ocischema
2
3 import (
4 "bytes"
5 "encoding/json"
6 "reflect"
7 "testing"
8
9 "github.com/docker/distribution"
10 "github.com/docker/distribution/manifest"
11 "github.com/docker/distribution/manifest/manifestlist"
12
13 v1 "github.com/opencontainers/image-spec/specs-go/v1"
14 )
15
16 var expectedManifestSerialization = []byte(`{
17 "schemaVersion": 2,
18 "mediaType": "application/vnd.oci.image.manifest.v1+json",
19 "config": {
20 "mediaType": "application/vnd.oci.image.config.v1+json",
21 "size": 985,
22 "digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
23 "annotations": {
24 "apple": "orange"
25 }
26 },
27 "layers": [
28 {
29 "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
30 "size": 153263,
31 "digest": "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
32 "annotations": {
33 "lettuce": "wrap"
34 }
35 }
36 ],
37 "annotations": {
38 "hot": "potato"
39 }
40 }`)
41
42 func makeTestManifest(mediaType string) Manifest {
43 return Manifest{
44 Versioned: manifest.Versioned{
45 SchemaVersion: 2,
46 MediaType: mediaType,
47 },
48 Config: distribution.Descriptor{
49 Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
50 Size: 985,
51 MediaType: v1.MediaTypeImageConfig,
52 Annotations: map[string]string{"apple": "orange"},
53 },
54 Layers: []distribution.Descriptor{
55 {
56 Digest: "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
57 Size: 153263,
58 MediaType: v1.MediaTypeImageLayerGzip,
59 Annotations: map[string]string{"lettuce": "wrap"},
60 },
61 },
62 Annotations: map[string]string{"hot": "potato"},
63 }
64 }
65
66 func TestManifest(t *testing.T) {
67 manifest := makeTestManifest(v1.MediaTypeImageManifest)
68
69 deserialized, err := FromStruct(manifest)
70 if err != nil {
71 t.Fatalf("error creating DeserializedManifest: %v", err)
72 }
73
74 mediaType, canonical, _ := deserialized.Payload()
75
76 if mediaType != v1.MediaTypeImageManifest {
77 t.Fatalf("unexpected media type: %s", mediaType)
78 }
79
80
81
82 p, err := json.MarshalIndent(&manifest, "", " ")
83 if err != nil {
84 t.Fatalf("error marshaling manifest: %v", err)
85 }
86 if !bytes.Equal(p, canonical) {
87 t.Fatalf("manifest bytes not equal: %q != %q", string(canonical), string(p))
88 }
89
90
91 if !bytes.Equal(expectedManifestSerialization, canonical) {
92 t.Fatalf("manifest bytes not equal: %q != %q", string(canonical), string(expectedManifestSerialization))
93 }
94
95 var unmarshalled DeserializedManifest
96 if err := json.Unmarshal(deserialized.canonical, &unmarshalled); err != nil {
97 t.Fatalf("error unmarshaling manifest: %v", err)
98 }
99
100 if !reflect.DeepEqual(&unmarshalled, deserialized) {
101 t.Fatalf("manifests are different after unmarshaling: %v != %v", unmarshalled, *deserialized)
102 }
103 if deserialized.Annotations["hot"] != "potato" {
104 t.Fatalf("unexpected annotation in manifest: %s", deserialized.Annotations["hot"])
105 }
106
107 target := deserialized.Target()
108 if target.Digest != "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b" {
109 t.Fatalf("unexpected digest in target: %s", target.Digest.String())
110 }
111 if target.MediaType != v1.MediaTypeImageConfig {
112 t.Fatalf("unexpected media type in target: %s", target.MediaType)
113 }
114 if target.Size != 985 {
115 t.Fatalf("unexpected size in target: %d", target.Size)
116 }
117 if target.Annotations["apple"] != "orange" {
118 t.Fatalf("unexpected annotation in target: %s", target.Annotations["apple"])
119 }
120
121 references := deserialized.References()
122 if len(references) != 2 {
123 t.Fatalf("unexpected number of references: %d", len(references))
124 }
125
126 if !reflect.DeepEqual(references[0], target) {
127 t.Fatalf("first reference should be target: %v != %v", references[0], target)
128 }
129
130
131 if references[1].Digest != "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b" {
132 t.Fatalf("unexpected digest in reference: %s", references[0].Digest.String())
133 }
134 if references[1].MediaType != v1.MediaTypeImageLayerGzip {
135 t.Fatalf("unexpected media type in reference: %s", references[0].MediaType)
136 }
137 if references[1].Size != 153263 {
138 t.Fatalf("unexpected size in reference: %d", references[0].Size)
139 }
140 if references[1].Annotations["lettuce"] != "wrap" {
141 t.Fatalf("unexpected annotation in reference: %s", references[1].Annotations["lettuce"])
142 }
143 }
144
145 func mediaTypeTest(t *testing.T, mediaType string, shouldError bool) {
146 manifest := makeTestManifest(mediaType)
147
148 deserialized, err := FromStruct(manifest)
149 if err != nil {
150 t.Fatalf("error creating DeserializedManifest: %v", err)
151 }
152
153 unmarshalled, descriptor, err := distribution.UnmarshalManifest(
154 v1.MediaTypeImageManifest,
155 deserialized.canonical)
156
157 if shouldError {
158 if err == nil {
159 t.Fatalf("bad content type should have produced error")
160 }
161 } else {
162 if err != nil {
163 t.Fatalf("error unmarshaling manifest, %v", err)
164 }
165
166 asManifest := unmarshalled.(*DeserializedManifest)
167 if asManifest.MediaType != mediaType {
168 t.Fatalf("Bad media type '%v' as unmarshalled", asManifest.MediaType)
169 }
170
171 if descriptor.MediaType != v1.MediaTypeImageManifest {
172 t.Fatalf("Bad media type '%v' for descriptor", descriptor.MediaType)
173 }
174
175 unmarshalledMediaType, _, _ := unmarshalled.Payload()
176 if unmarshalledMediaType != v1.MediaTypeImageManifest {
177 t.Fatalf("Bad media type '%v' for payload", unmarshalledMediaType)
178 }
179 }
180 }
181
182 func TestMediaTypes(t *testing.T) {
183 mediaTypeTest(t, "", false)
184 mediaTypeTest(t, v1.MediaTypeImageManifest, false)
185 mediaTypeTest(t, v1.MediaTypeImageManifest+"XXX", true)
186 }
187
188 func TestValidateManifest(t *testing.T) {
189 manifest := Manifest{
190 Config: distribution.Descriptor{Size: 1},
191 Layers: []distribution.Descriptor{{Size: 2}},
192 }
193 index := manifestlist.ManifestList{
194 Manifests: []manifestlist.ManifestDescriptor{
195 {Descriptor: distribution.Descriptor{Size: 3}},
196 },
197 }
198 t.Run("valid", func(t *testing.T) {
199 b, err := json.Marshal(manifest)
200 if err != nil {
201 t.Fatal("unexpected error marshaling manifest", err)
202 }
203 if err := validateManifest(b); err != nil {
204 t.Error("manifest should be valid", err)
205 }
206 })
207 t.Run("invalid", func(t *testing.T) {
208 b, err := json.Marshal(index)
209 if err != nil {
210 t.Fatal("unexpected error marshaling index", err)
211 }
212 if err := validateManifest(b); err == nil {
213 t.Error("index should not be valid")
214 }
215 })
216 }
217
View as plain text