Skip to content

Commit 3468157

Browse files
committed
Merge pull request #82 from s-urbaniak/layout
layout: implement unpacking and validation
2 parents d484a5f + d1874b6 commit 3468157

File tree

10 files changed

+792
-99
lines changed

10 files changed

+792
-99
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
code-of-conduct.md
2-
oci-image-tool
3-
oci-validate-examples
2+
/oci-image-tool
3+
/oci-validate-examples
44
output

cmd/oci-image-tool/autodetect.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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 main
16+
17+
import (
18+
"encoding/json"
19+
"io"
20+
"io/ioutil"
21+
"net/http"
22+
"os"
23+
24+
"github.com/opencontainers/image-spec/schema"
25+
"github.com/pkg/errors"
26+
)
27+
28+
// supported autodetection types
29+
const (
30+
typeImageLayout = "imageLayout"
31+
typeImage = "image"
32+
typeManifest = "manifest"
33+
typeManifestList = "manifestList"
34+
typeConfig = "config"
35+
)
36+
37+
// autodetect detects the validation type for the given path
38+
// or an error if the validation type could not be resolved.
39+
func autodetect(path string) (string, error) {
40+
fi, err := os.Stat(path)
41+
if err != nil {
42+
return "", errors.Wrapf(err, "unable to access path") // err from os.Stat includes path name
43+
}
44+
45+
if fi.IsDir() {
46+
return typeImageLayout, nil
47+
}
48+
49+
f, err := os.Open(path)
50+
if err != nil {
51+
return "", errors.Wrap(err, "unable to open file") // os.Open includes the filename
52+
}
53+
defer f.Close()
54+
55+
buf, err := ioutil.ReadAll(io.LimitReader(f, 512)) // read some initial bytes to detect content
56+
if err != nil {
57+
return "", errors.Wrap(err, "unable to read")
58+
}
59+
60+
mimeType := http.DetectContentType(buf)
61+
62+
switch mimeType {
63+
case "application/x-gzip":
64+
return typeImage, nil
65+
66+
case "application/octet-stream":
67+
return typeImage, nil
68+
69+
case "text/plain; charset=utf-8":
70+
// might be a JSON file, will be handled below
71+
72+
default:
73+
return "", errors.New("unknown file type")
74+
}
75+
76+
if _, err := f.Seek(0, os.SEEK_SET); err != nil {
77+
return "", errors.Wrap(err, "unable to seek")
78+
}
79+
80+
header := struct {
81+
SchemaVersion int `json:"schemaVersion"`
82+
MediaType string `json:"mediaType"`
83+
Config interface{} `json:"config"`
84+
}{}
85+
86+
if err := json.NewDecoder(f).Decode(&header); err != nil {
87+
return "", errors.Wrap(err, "unable to parse JSON")
88+
}
89+
90+
switch {
91+
case header.MediaType == string(schema.MediaTypeManifest):
92+
return typeManifest, nil
93+
94+
case header.MediaType == string(schema.MediaTypeManifestList):
95+
return typeManifestList, nil
96+
97+
case header.MediaType == "" && header.SchemaVersion == 0 && header.Config != nil:
98+
// config files don't have mediaType/schemaVersion header
99+
return typeConfig, nil
100+
}
101+
102+
return "", errors.New("unknown media type")
103+
}

cmd/oci-image-tool/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ func main() {
3131
stderr := log.New(os.Stderr, "", 0)
3232

3333
cmd.AddCommand(newValidateCmd(stdout, stderr))
34+
cmd.AddCommand(newUnpackCmd(stdout, stderr))
35+
3436
if err := cmd.Execute(); err != nil {
3537
stderr.Println(err)
3638
os.Exit(1)

cmd/oci-image-tool/unpack.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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 main
16+
17+
import (
18+
"fmt"
19+
"log"
20+
"os"
21+
"strings"
22+
23+
"github.com/opencontainers/image-spec/image"
24+
"github.com/spf13/cobra"
25+
)
26+
27+
// supported unpack types
28+
var unpackTypes = []string{
29+
typeImageLayout,
30+
typeImage,
31+
}
32+
33+
type unpackCmd struct {
34+
stdout *log.Logger
35+
stderr *log.Logger
36+
typ string // the type to validate, can be empty string
37+
ref string
38+
}
39+
40+
func newUnpackCmd(stdout, stderr *log.Logger) *cobra.Command {
41+
v := &unpackCmd{
42+
stdout: stdout,
43+
stderr: stderr,
44+
}
45+
46+
cmd := &cobra.Command{
47+
Use: "unpack [src] [dest]",
48+
Short: "Unpack an image or image source layout",
49+
Long: `Unpack the OCI image .tar file or OCI image layout directory present at [src] to the destination directory [dest].`,
50+
Run: v.Run,
51+
}
52+
53+
cmd.Flags().StringVar(
54+
&v.typ, "type", "",
55+
fmt.Sprintf(
56+
`Type of the file to unpack. If unset, oci-image-tool will try to auto-detect the type. One of "%s"`,
57+
strings.Join(unpackTypes, ","),
58+
),
59+
)
60+
61+
cmd.Flags().StringVar(
62+
&v.ref, "ref", "v1.0",
63+
`The ref pointing to the manifest to be unpacked. This must be present in the "refs" subdirectory of the image.`,
64+
)
65+
66+
return cmd
67+
}
68+
69+
func (v *unpackCmd) Run(cmd *cobra.Command, args []string) {
70+
if len(args) != 2 {
71+
v.stderr.Print("both src and dest must be provided")
72+
if err := cmd.Usage(); err != nil {
73+
v.stderr.Println(err)
74+
}
75+
os.Exit(1)
76+
}
77+
78+
if v.typ == "" {
79+
typ, err := autodetect(args[0])
80+
if err != nil {
81+
v.stderr.Printf("%q: autodetection failed: %v", args[0], err)
82+
os.Exit(1)
83+
}
84+
v.typ = typ
85+
}
86+
87+
var err error
88+
switch v.typ {
89+
case typeImageLayout:
90+
err = image.UnpackLayout(args[0], args[1], v.ref)
91+
92+
case typeImage:
93+
err = image.Unpack(args[0], args[1], v.ref)
94+
}
95+
96+
if err != nil {
97+
v.stderr.Printf("unpacking failed: %v", err)
98+
os.Exit(1)
99+
}
100+
101+
os.Exit(0)
102+
}

0 commit comments

Comments
 (0)