From a95183f5cf39a055ece071f82e314c7e3b8f3c56 Mon Sep 17 00:00:00 2001 From: xiekeyang Date: Wed, 14 Sep 2016 18:06:34 +0800 Subject: [PATCH 1/2] build createTarFile function It is reused on many place, so should be built a function. Signed-off-by: xiekeyang --- image/manifest_test.go | 93 ++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/image/manifest_test.go b/image/manifest_test.go index 350ef347d..8a11e9d88 100644 --- a/image/manifest_test.go +++ b/image/manifest_test.go @@ -28,27 +28,25 @@ import ( "testing" ) +type tarContent struct { + header *tar.Header + b []byte +} + func TestUnpackLayerDuplicateEntries(t *testing.T) { tmp1, err := ioutil.TempDir("", "test-dup") if err != nil { t.Fatal(err) } tarfile := filepath.Join(tmp1, "test.tar") - f, err := os.Create(tarfile) + + _, err = createTarFile(tarfile, []tarContent{ + tarContent{&tar.Header{Name: "test", Size: 4, Mode: 0600}, []byte("test")}, + tarContent{&tar.Header{Name: "test", Size: 5, Mode: 0600}, []byte("test1")}, + }) if err != nil { t.Fatal(err) } - defer f.Close() - defer os.RemoveAll(tmp1) - gw := gzip.NewWriter(f) - tw := tar.NewWriter(gw) - - tw.WriteHeader(&tar.Header{Name: "test", Size: 4, Mode: 0600}) - io.Copy(tw, bytes.NewReader([]byte("test"))) - tw.WriteHeader(&tar.Header{Name: "test", Size: 5, Mode: 0600}) - io.Copy(tw, bytes.NewReader([]byte("test1"))) - tw.Close() - gw.Close() r, err := os.Open(tarfile) if err != nil { @@ -76,32 +74,15 @@ func TestUnpackLayer(t *testing.T) { t.Fatal(err) } tarfile := filepath.Join(tmp1, "blobs", "sha256", "test.tar") - f, err := os.Create(tarfile) - if err != nil { - t.Fatal(err) - } - gw := gzip.NewWriter(f) - tw := tar.NewWriter(gw) - - tw.WriteHeader(&tar.Header{Name: "test", Size: 4, Mode: 0600}) - io.Copy(tw, bytes.NewReader([]byte("test"))) - tw.Close() - gw.Close() - f.Close() - - // generate sha256 hash - h := sha256.New() - file, err := os.Open(tarfile) + desc, err := createTarFile(tarfile, []tarContent{ + tarContent{&tar.Header{Name: "test", Size: 4, Mode: 0600}, []byte("test")}, + }) if err != nil { t.Fatal(err) } - defer file.Close() - _, err = io.Copy(h, file) - if err != nil { - t.Fatal(err) - } - err = os.Rename(tarfile, filepath.Join(tmp1, "blobs", "sha256", fmt.Sprintf("%x", h.Sum(nil)))) + + err = os.Rename(tarfile, filepath.Join(tmp1, "blobs", "sha256", desc.Digest)) if err != nil { t.Fatal(err) } @@ -109,7 +90,7 @@ func TestUnpackLayer(t *testing.T) { testManifest := manifest{ Layers: []descriptor{descriptor{ MediaType: "application/vnd.oci.image.layer.tar+gzip", - Digest: fmt.Sprintf("sha256:%s", fmt.Sprintf("%x", h.Sum(nil))), + Digest: fmt.Sprintf("sha256:%s", desc.Digest), }}, } err = testManifest.unpack(newPathWalker(tmp1), filepath.Join(tmp1, "rootfs")) @@ -122,3 +103,45 @@ func TestUnpackLayer(t *testing.T) { t.Fatal(err) } } + +func createTarFile(name string, list []tarContent) (descriptor, error) { + f, err := os.Create(name) + if err != nil { + return descriptor{}, err + } + gw := gzip.NewWriter(f) + tw := tar.NewWriter(gw) + + for _, content := range list { + if err = tw.WriteHeader(content.header); err != nil { + tw.Close() + gw.Close() + f.Close() + return descriptor{}, err + } + if _, err = io.Copy(tw, bytes.NewReader(content.b)); err != nil { + tw.Close() + gw.Close() + f.Close() + return descriptor{}, err + } + } + tw.Close() + gw.Close() + f.Close() + + // generate sha256 hash + h := sha256.New() + file, err := os.Open(name) + if err != nil { + return descriptor{}, err + } + defer file.Close() + + size, err := io.Copy(h, file) + if err != nil { + return descriptor{}, err + } + + return descriptor{Digest: fmt.Sprintf("%x", h.Sum(nil)), Size: size}, nil +} From 18b24da7b064bcb1f5e398ff94c939eca2811c3e Mon Sep 17 00:00:00 2001 From: xiekeyang Date: Wed, 14 Sep 2016 18:07:12 +0800 Subject: [PATCH 2/2] add unit test for ValidateLayout function Signed-off-by: xiekeyang --- image/image_test.go | 259 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 image/image_test.go diff --git a/image/image_test.go b/image/image_test.go new file mode 100644 index 000000000..42a8efaad --- /dev/null +++ b/image/image_test.go @@ -0,0 +1,259 @@ +// Copyright 2016 The Linux Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package image + +import ( + "archive/tar" + "bytes" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "testing" +) + +const ( + layoutStr = `{"imageLayoutVersion": "1.0.0"}` + + configStr = `{ + "created": "2015-10-31T22:22:56.015925234Z", + "author": "Alyssa P. Hacker ", + "architecture": "amd64", + "os": "linux", + "config": { + "User": "alice", + "Memory": 2048, + "MemorySwap": 4096, + "CpuShares": 8, + "ExposedPorts": { + "8080/tcp": {} + }, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "FOO=oci_is_a", + "BAR=well_written_spec" + ], + "Entrypoint": [ + "/bin/my-app-binary" + ], + "Cmd": [ + "--foreground", + "--config", + "/etc/my-app.d/default.cfg" + ], + "Volumes": { + "/var/job-result-data": {}, + "/var/log/my-app-logs": {} + }, + "WorkingDir": "/home/alice" + }, + "rootfs": { + "diff_ids": [ + "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1", + "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + ], + "type": "layers" + }, + "history": [ + { + "created": "2015-10-31T22:22:54.690851953Z", + "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /" + }, + { + "created": "2015-10-31T22:22:55.613815829Z", + "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]", + "empty_layer": true + } + ] +} +` +) + +var ( + refStr = `{"digest":"","mediaType":"application/vnd.oci.image.manifest.v1+json","size":}` + + manifestStr = `{ + "annotations": null, + "config": { + "digest": "", + "mediaType": "application/vnd.oci.image.config.v1+json", + "size": + }, + "layers": [ + { + "digest": "", + "mediaType": "application/vnd.oci.image.layer.tar+gzip", + "size": + } + ], + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "schemaVersion": 2 +} + ` +) + +func TestValidateLayout(t *testing.T) { + root, err := ioutil.TempDir("", "oci-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + err = os.MkdirAll(filepath.Join(root, "blobs", "sha256"), 0700) + if err != nil { + t.Fatal(err) + } + + err = os.MkdirAll(filepath.Join(root, "refs"), 0700) + if err != nil { + t.Fatal(err) + } + + desc, err := createLayerFile(root) + if err != nil { + t.Fatal(err) + } + manifestStr = strings.Replace(manifestStr, "", desc.Digest, 1) + manifestStr = strings.Replace(manifestStr, "", strconv.FormatInt(desc.Size, 10), 1) + + desc, err = createConfigTestFile(root) + if err != nil { + t.Fatal(err) + } + manifestStr = strings.Replace(manifestStr, "", desc.Digest, 1) + manifestStr = strings.Replace(manifestStr, "", strconv.FormatInt(desc.Size, 10), 1) + + mft, err := createManifestFile(root, manifestStr) + if err != nil { + t.Fatal(err) + } + + err = createRefFile(root, mft) + if err != nil { + t.Fatal(err) + } + + err = createLayoutFile(root) + if err != nil { + t.Fatal(err) + } + + err = ValidateLayout(root, []string{"latest"}, nil) + if err != nil { + t.Fatal(err) + } +} + +func createLayerFile(root string) (descriptor, error) { + layerPath := filepath.Join(root, "blobs", "sha256", "test.tar") + + desc, err := createTarFile(layerPath, []tarContent{ + tarContent{&tar.Header{Name: "test", Size: 4, Mode: 0600}, []byte("test")}, + }) + if err != nil { + return descriptor{}, err + } + + err = os.Rename(layerPath, filepath.Join(root, "blobs", "sha256", desc.Digest)) + if err != nil { + return descriptor{}, err + } + + return descriptor{Digest: "sha256:" + desc.Digest, Size: desc.Size}, nil +} + +func createConfigTestFile(root string) (descriptor, error) { + oldpath := filepath.Join(root, "blobs", "sha256", "test-config") + f, err := os.Create(oldpath) + if err != nil { + return descriptor{}, err + } + defer f.Close() + + _, err = io.Copy(f, bytes.NewBuffer([]byte(configStr))) + if err != nil { + return descriptor{}, err + } + + // generate sha256 hash + h := sha256.New() + size, err := io.Copy(h, bytes.NewBuffer([]byte(configStr))) + if err != nil { + return descriptor{}, err + } + digest := fmt.Sprintf("%x", h.Sum(nil)) + + err = os.Rename(oldpath, filepath.Join(root, "blobs", "sha256", digest)) + if err != nil { + return descriptor{}, err + } + return descriptor{Digest: "sha256:" + digest, Size: size}, nil +} + +func createManifestFile(root, str string) (descriptor, error) { + oldpath := filepath.Join(root, "blobs", "sha256", "test-manifest") + f, err := os.Create(oldpath) + if err != nil { + return descriptor{}, err + } + defer f.Close() + + _, err = io.Copy(f, bytes.NewBuffer([]byte(str))) + if err != nil { + return descriptor{}, err + } + + // generate sha256 hash + h := sha256.New() + size, err := io.Copy(h, bytes.NewBuffer([]byte(str))) + if err != nil { + return descriptor{}, err + } + digest := fmt.Sprintf("%x", h.Sum(nil)) + + err = os.Rename(oldpath, filepath.Join(root, "blobs", "sha256", digest)) + if err != nil { + return descriptor{}, err + } + return descriptor{Digest: "sha256:" + digest, Size: size}, nil +} + +func createRefFile(root string, mft descriptor) error { + refpath := filepath.Join(root, "refs", "latest") + f, err := os.Create(refpath) + if err != nil { + return err + } + defer f.Close() + refStr = strings.Replace(refStr, "", mft.Digest, -1) + refStr = strings.Replace(refStr, "", strconv.FormatInt(mft.Size, 10), -1) + _, err = io.Copy(f, bytes.NewBuffer([]byte(refStr))) + return err +} + +func createLayoutFile(root string) error { + layoutPath := filepath.Join(root, "oci-layout") + f, err := os.Create(layoutPath) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(f, bytes.NewBuffer([]byte(layoutStr))) + return err +}