Skip to content

Commit e85687e

Browse files
committed
unit test for layout validation
Signed-off-by: xiekeyang <[email protected]>
1 parent 721ba58 commit e85687e

File tree

1 file changed

+331
-0
lines changed

1 file changed

+331
-0
lines changed

image/image_test.go

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
// Copyright 2016 The Linux Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package image
16+
17+
import (
18+
"archive/tar"
19+
"bytes"
20+
"compress/gzip"
21+
"crypto/sha256"
22+
"fmt"
23+
"io"
24+
"io/ioutil"
25+
"os"
26+
"path/filepath"
27+
"strconv"
28+
"strings"
29+
"testing"
30+
31+
"github.com/opencontainers/image-spec/specs-go/v1"
32+
)
33+
34+
const (
35+
refTag = "latest"
36+
37+
layoutStr = `{"imageLayoutVersion": "1.0.0"}`
38+
39+
configStr = `{
40+
"created": "2015-10-31T22:22:56.015925234Z",
41+
"author": "Alyssa P. Hacker <[email protected]>",
42+
"architecture": "amd64",
43+
"os": "linux",
44+
"config": {
45+
"User": "alice",
46+
"Memory": 2048,
47+
"MemorySwap": 4096,
48+
"CpuShares": 8,
49+
"ExposedPorts": {
50+
"8080/tcp": {}
51+
},
52+
"Env": [
53+
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
54+
"FOO=oci_is_a",
55+
"BAR=well_written_spec"
56+
],
57+
"Entrypoint": [
58+
"/bin/my-app-binary"
59+
],
60+
"Cmd": [
61+
"--foreground",
62+
"--config",
63+
"/etc/my-app.d/default.cfg"
64+
],
65+
"Volumes": {
66+
"/var/job-result-data": {},
67+
"/var/log/my-app-logs": {}
68+
},
69+
"WorkingDir": "/home/alice"
70+
},
71+
"rootfs": {
72+
"diff_ids": [
73+
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
74+
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
75+
],
76+
"type": "layers"
77+
},
78+
"history": [
79+
{
80+
"created": "2015-10-31T22:22:54.690851953Z",
81+
"created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
82+
},
83+
{
84+
"created": "2015-10-31T22:22:55.613815829Z",
85+
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
86+
"empty_layer": true
87+
}
88+
]
89+
}
90+
`
91+
)
92+
93+
var (
94+
refStr = `{"digest":"<manifest_digest>","mediaType":"application/vnd.oci.image.manifest.v1+json","size":<manifest_size>}`
95+
96+
manifestStr = `{
97+
"annotations": null,
98+
"config": {
99+
"digest": "<config_digest>",
100+
"mediaType": "application/vnd.oci.image.config.v1+json",
101+
"size": <config_size>
102+
},
103+
"layers": [
104+
{
105+
"digest": "<layer_digest>",
106+
"mediaType": "application/vnd.oci.image.layer.tar+gzip",
107+
"size": <layer_size>
108+
}
109+
],
110+
"mediaType": "application/vnd.oci.image.manifest.v1+json",
111+
"schemaVersion": 2
112+
}
113+
`
114+
)
115+
116+
type tarContent struct {
117+
header *tar.Header
118+
b []byte
119+
}
120+
121+
type imageLayout struct {
122+
rootDir string
123+
layout string
124+
ref string
125+
manifest string
126+
config string
127+
tarList []tarContent
128+
}
129+
130+
func TestValidateLayout(t *testing.T) {
131+
root, err := ioutil.TempDir("", "oci-test")
132+
if err != nil {
133+
t.Fatal(err)
134+
}
135+
defer os.RemoveAll(root)
136+
137+
il := imageLayout{
138+
rootDir: root,
139+
layout: layoutStr,
140+
ref: refTag,
141+
manifest: manifestStr,
142+
config: configStr,
143+
tarList: []tarContent{
144+
tarContent{&tar.Header{Name: "test", Size: 4, Mode: 0600}, []byte("test")},
145+
},
146+
}
147+
148+
// create image layout bundle
149+
err = createImageLayoutBundle(il)
150+
if err != nil {
151+
t.Fatal(err)
152+
}
153+
154+
err = ValidateLayout(root, []string{refTag}, nil)
155+
if err != nil {
156+
t.Fatal(err)
157+
}
158+
}
159+
160+
func createImageLayoutBundle(il imageLayout) error {
161+
err := os.MkdirAll(filepath.Join(il.rootDir, "blobs", "sha256"), 0700)
162+
if err != nil {
163+
return err
164+
}
165+
166+
err = os.MkdirAll(filepath.Join(il.rootDir, "refs"), 0700)
167+
if err != nil {
168+
return err
169+
}
170+
171+
// create image layout file
172+
err = createLayoutFile(il.rootDir)
173+
if err != nil {
174+
return err
175+
}
176+
177+
// create image layer blob file.
178+
desc, err := createImageLayerFile(il.rootDir, il.tarList)
179+
if err != nil {
180+
return err
181+
}
182+
il.manifest = strings.Replace(il.manifest, "<layer_digest>", desc.Digest, 1)
183+
il.manifest = strings.Replace(il.manifest, "<layer_size>", strconv.FormatInt(desc.Size, 10), 1)
184+
185+
desc, err = createConfigFile(il.rootDir, il.config)
186+
if err != nil {
187+
return err
188+
}
189+
il.manifest = strings.Replace(il.manifest, "<config_digest>", desc.Digest, 1)
190+
il.manifest = strings.Replace(il.manifest, "<config_size>", strconv.FormatInt(desc.Size, 10), 1)
191+
192+
// create manifest blob file
193+
desc, err = createManifestFile(il.rootDir, il.manifest)
194+
if err != nil {
195+
return err
196+
}
197+
198+
return createRefFile(il.rootDir, il.ref, desc)
199+
}
200+
201+
func createLayoutFile(root string) error {
202+
layoutPath := filepath.Join(root, "oci-layout")
203+
f, err := os.Create(layoutPath)
204+
if err != nil {
205+
return err
206+
}
207+
defer f.Close()
208+
_, err = io.Copy(f, bytes.NewBuffer([]byte(layoutStr)))
209+
return err
210+
}
211+
212+
func createRefFile(root, ref string, mft descriptor) error {
213+
refpath := filepath.Join(root, "refs", ref)
214+
f, err := os.Create(refpath)
215+
if err != nil {
216+
return err
217+
}
218+
defer f.Close()
219+
refStr = strings.Replace(refStr, "<manifest_digest>", mft.Digest, -1)
220+
refStr = strings.Replace(refStr, "<manifest_size>", strconv.FormatInt(mft.Size, 10), -1)
221+
_, err = io.Copy(f, bytes.NewBuffer([]byte(refStr)))
222+
return err
223+
}
224+
225+
func createManifestFile(root, str string) (descriptor, error) {
226+
name := filepath.Join(root, "blobs", "sha256", "test-manifest")
227+
f, err := os.Create(name)
228+
if err != nil {
229+
return descriptor{}, err
230+
}
231+
defer f.Close()
232+
233+
_, err = io.Copy(f, bytes.NewBuffer([]byte(str)))
234+
if err != nil {
235+
return descriptor{}, err
236+
}
237+
238+
return createHashedBlob(name)
239+
}
240+
241+
func createConfigFile(root, config string) (descriptor, error) {
242+
name := filepath.Join(root, "blobs", "sha256", "test-config")
243+
f, err := os.Create(name)
244+
if err != nil {
245+
return descriptor{}, err
246+
}
247+
defer f.Close()
248+
249+
_, err = io.Copy(f, bytes.NewBuffer([]byte(config)))
250+
if err != nil {
251+
return descriptor{}, err
252+
}
253+
254+
return createHashedBlob(name)
255+
}
256+
257+
func createImageLayerFile(root string, list []tarContent) (descriptor, error) {
258+
name := filepath.Join(root, "blobs", "sha256", "test-layer")
259+
err := createTarBlob(name, list)
260+
if err != nil {
261+
return descriptor{}, err
262+
}
263+
264+
desc, err := createHashedBlob(name)
265+
if err != nil {
266+
return descriptor{}, err
267+
}
268+
269+
desc.MediaType = v1.MediaTypeImageLayer
270+
return desc, nil
271+
}
272+
273+
func createTarBlob(name string, list []tarContent) error {
274+
file, err := os.Create(name)
275+
if err != nil {
276+
return err
277+
}
278+
defer file.Close()
279+
gzipWriter := gzip.NewWriter(file)
280+
defer gzipWriter.Close()
281+
tarWriter := tar.NewWriter(gzipWriter)
282+
defer tarWriter.Close()
283+
284+
for _, content := range list {
285+
if err = tarWriter.WriteHeader(content.header); err != nil {
286+
return err
287+
}
288+
if _, err = io.Copy(tarWriter, bytes.NewReader(content.b)); err != nil {
289+
return err
290+
}
291+
}
292+
return nil
293+
}
294+
295+
func createHashedBlob(name string) (descriptor, error) {
296+
desc, err := newDescriptor(name)
297+
if err != nil {
298+
return descriptor{}, err
299+
}
300+
301+
// Rename the file to hashed-digest name.
302+
err = os.Rename(name, filepath.Join(filepath.Dir(name), desc.Digest))
303+
if err != nil {
304+
return descriptor{}, err
305+
}
306+
307+
//Normalize the hashed digest.
308+
desc.Digest = "sha256:" + desc.Digest
309+
310+
return desc, nil
311+
}
312+
313+
func newDescriptor(name string) (descriptor, error) {
314+
file, err := os.Open(name)
315+
if err != nil {
316+
return descriptor{}, err
317+
}
318+
defer file.Close()
319+
320+
// generate sha256 hash
321+
hash := sha256.New()
322+
size, err := io.Copy(hash, file)
323+
if err != nil {
324+
return descriptor{}, err
325+
}
326+
327+
return descriptor{
328+
Digest: fmt.Sprintf("%x", hash.Sum(nil)),
329+
Size: size,
330+
}, nil
331+
}

0 commit comments

Comments
 (0)