Skip to content

Commit

Permalink
Merge pull request #82 from s-urbaniak/layout
Browse files Browse the repository at this point in the history
layout: implement unpacking and validation
  • Loading branch information
jonboulle committed May 31, 2016
2 parents d484a5f + d1874b6 commit 3468157
Show file tree
Hide file tree
Showing 10 changed files with 792 additions and 99 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
code-of-conduct.md
oci-image-tool
oci-validate-examples
/oci-image-tool
/oci-validate-examples
output
103 changes: 103 additions & 0 deletions cmd/oci-image-tool/autodetect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// 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 main

import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"os"

"github.com/opencontainers/image-spec/schema"
"github.com/pkg/errors"
)

// supported autodetection types
const (
typeImageLayout = "imageLayout"
typeImage = "image"
typeManifest = "manifest"
typeManifestList = "manifestList"
typeConfig = "config"
)

// autodetect detects the validation type for the given path
// or an error if the validation type could not be resolved.
func autodetect(path string) (string, error) {
fi, err := os.Stat(path)
if err != nil {
return "", errors.Wrapf(err, "unable to access path") // err from os.Stat includes path name
}

if fi.IsDir() {
return typeImageLayout, nil
}

f, err := os.Open(path)
if err != nil {
return "", errors.Wrap(err, "unable to open file") // os.Open includes the filename
}
defer f.Close()

buf, err := ioutil.ReadAll(io.LimitReader(f, 512)) // read some initial bytes to detect content
if err != nil {
return "", errors.Wrap(err, "unable to read")
}

mimeType := http.DetectContentType(buf)

switch mimeType {
case "application/x-gzip":
return typeImage, nil

case "application/octet-stream":
return typeImage, nil

case "text/plain; charset=utf-8":
// might be a JSON file, will be handled below

default:
return "", errors.New("unknown file type")
}

if _, err := f.Seek(0, os.SEEK_SET); err != nil {
return "", errors.Wrap(err, "unable to seek")
}

header := struct {
SchemaVersion int `json:"schemaVersion"`
MediaType string `json:"mediaType"`
Config interface{} `json:"config"`
}{}

if err := json.NewDecoder(f).Decode(&header); err != nil {
return "", errors.Wrap(err, "unable to parse JSON")
}

switch {
case header.MediaType == string(schema.MediaTypeManifest):
return typeManifest, nil

case header.MediaType == string(schema.MediaTypeManifestList):
return typeManifestList, nil

case header.MediaType == "" && header.SchemaVersion == 0 && header.Config != nil:
// config files don't have mediaType/schemaVersion header
return typeConfig, nil
}

return "", errors.New("unknown media type")
}
2 changes: 2 additions & 0 deletions cmd/oci-image-tool/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ func main() {
stderr := log.New(os.Stderr, "", 0)

cmd.AddCommand(newValidateCmd(stdout, stderr))
cmd.AddCommand(newUnpackCmd(stdout, stderr))

if err := cmd.Execute(); err != nil {
stderr.Println(err)
os.Exit(1)
Expand Down
102 changes: 102 additions & 0 deletions cmd/oci-image-tool/unpack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// 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 main

import (
"fmt"
"log"
"os"
"strings"

"github.com/opencontainers/image-spec/image"
"github.com/spf13/cobra"
)

// supported unpack types
var unpackTypes = []string{
typeImageLayout,
typeImage,
}

type unpackCmd struct {
stdout *log.Logger
stderr *log.Logger
typ string // the type to validate, can be empty string
ref string
}

func newUnpackCmd(stdout, stderr *log.Logger) *cobra.Command {
v := &unpackCmd{
stdout: stdout,
stderr: stderr,
}

cmd := &cobra.Command{
Use: "unpack [src] [dest]",
Short: "Unpack an image or image source layout",
Long: `Unpack the OCI image .tar file or OCI image layout directory present at [src] to the destination directory [dest].`,
Run: v.Run,
}

cmd.Flags().StringVar(
&v.typ, "type", "",
fmt.Sprintf(
`Type of the file to unpack. If unset, oci-image-tool will try to auto-detect the type. One of "%s"`,
strings.Join(unpackTypes, ","),
),
)

cmd.Flags().StringVar(
&v.ref, "ref", "v1.0",
`The ref pointing to the manifest to be unpacked. This must be present in the "refs" subdirectory of the image.`,
)

return cmd
}

func (v *unpackCmd) Run(cmd *cobra.Command, args []string) {
if len(args) != 2 {
v.stderr.Print("both src and dest must be provided")
if err := cmd.Usage(); err != nil {
v.stderr.Println(err)
}
os.Exit(1)
}

if v.typ == "" {
typ, err := autodetect(args[0])
if err != nil {
v.stderr.Printf("%q: autodetection failed: %v", args[0], err)
os.Exit(1)
}
v.typ = typ
}

var err error
switch v.typ {
case typeImageLayout:
err = image.UnpackLayout(args[0], args[1], v.ref)

case typeImage:
err = image.Unpack(args[0], args[1], v.ref)
}

if err != nil {
v.stderr.Printf("unpacking failed: %v", err)
os.Exit(1)
}

os.Exit(0)
}
Loading

0 comments on commit 3468157

Please sign in to comment.