Skip to content

Commit

Permalink
image: Refactor to use cas/ref engines instead of walkers
Browse files Browse the repository at this point in the history
The validation/unpacking code doesn't really care what the reference
and CAS implemenations are.  And the new generic interfaces in
image/refs and image/cas will scale better as we add new backends than
the walker interface.

The old tar/directory distinction between image and imageLayout is
gone.  The new CAS/refs engines don't support directory backends yet
(I plan on adding them once the engine framework lands), but the new
framework will handle tar/directory/... detection inside
layout.NewEngine (and possibly inside a new (cas|refs).NewEngine when
we grow engine types that aren't based on image-layout).

Also replace the old methods like:

  func (d *descriptor) validateContent(r io.Reader) error

with functions like:

  validateContent(ctx context.Context, descriptor *specs.Descriptor, r io.Reader) error

to avoid local types that duplicate the image-spec types.  This saves
an extra instantiation for folks who want to validate (or whatever) a
specs.Descriptor they have obtained elsewhere.

I'd prefer casLayout and refsLayout for the imported packages, but
Stephen doesn't want camelCase for package names [1].

[1]: opencontainers/image-spec#159 (comment)

Signed-off-by: W. Trevor King <wking@tremily.us>
  • Loading branch information
wking committed Sep 16, 2016
1 parent 2d7a567 commit 48ae1f2
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 452 deletions.
9 changes: 4 additions & 5 deletions cmd/oci-create-runtime-bundle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import (

"github.com/opencontainers/image-tools/image"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)

// supported bundle types
var bundleTypes = []string{
image.TypeImageLayout,
image.TypeImage,
}

Expand Down Expand Up @@ -93,6 +93,8 @@ func (v *bundleCmd) Run(cmd *cobra.Command, args []string) {
os.Exit(1)
}

ctx := context.Background()

if _, err := os.Stat(args[1]); os.IsNotExist(err) {
v.stderr.Printf("destination path %s does not exist", args[1])
os.Exit(1)
Expand All @@ -109,11 +111,8 @@ func (v *bundleCmd) Run(cmd *cobra.Command, args []string) {

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

case image.TypeImage:
err = image.CreateRuntimeBundle(args[0], args[1], v.ref, v.root)
err = image.CreateRuntimeBundle(ctx, args[0], args[1], v.ref, v.root)
}

if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ runtime-spec-compatible `dest/config.json`.
A directory representing the root filesystem of the container in the OCI runtime bundle. It is strongly recommended to keep the default value. (default "rootfs")

**--type**
Type of the file to unpack. If unset, oci-create-runtime-bundle will try to auto-detect the type. One of "imageLayout,image"
Type of the file to unpack. If unset, oci-create-runtime-bundle will try to auto-detect the type. One of "image"

# EXAMPLES
```
Expand Down
14 changes: 7 additions & 7 deletions cmd/oci-image-validate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import (
"github.com/opencontainers/image-tools/image"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)

// supported validation types
var validateTypes = []string{
image.TypeImageLayout,
image.TypeImage,
image.TypeManifest,
image.TypeManifestList,
Expand Down Expand Up @@ -75,7 +75,7 @@ func newValidateCmd(stdout, stderr *log.Logger) *cobra.Command {

cmd.Flags().StringSliceVar(
&v.refs, "ref", nil,
`A set of refs pointing to the manifests to be validated. Each reference must be present in the "refs" subdirectory of the image. Only applicable if type is image or imageLayout.`,
`A set of refs pointing to the manifests to be validated. Each reference must be present in the "refs" subdirectory of the image. Only applicable if type is image.`,
)

return cmd
Expand All @@ -90,9 +90,11 @@ func (v *validateCmd) Run(cmd *cobra.Command, args []string) {
os.Exit(1)
}

ctx := context.Background()

var exitcode int
for _, arg := range args {
err := v.validatePath(arg)
err := v.validatePath(ctx, arg)

if err == nil {
v.stdout.Printf("%s: OK", arg)
Expand Down Expand Up @@ -122,7 +124,7 @@ func (v *validateCmd) Run(cmd *cobra.Command, args []string) {
os.Exit(exitcode)
}

func (v *validateCmd) validatePath(name string) error {
func (v *validateCmd) validatePath(ctx context.Context, name string) error {
var (
err error
typ = v.typ
Expand All @@ -135,10 +137,8 @@ func (v *validateCmd) validatePath(name string) error {
}

switch typ {
case image.TypeImageLayout:
return image.ValidateLayout(name, v.refs, v.stdout)
case image.TypeImage:
return image.Validate(name, v.refs, v.stdout)
return image.Validate(ctx, name, v.refs, v.stdout)
}

f, err := os.Open(name)
Expand Down
6 changes: 3 additions & 3 deletions cmd/oci-image-validate/oci-image-validate.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ oci-image-validate \- Validate one or more image files
Can be specified multiple times to validate multiple references.
`NAME` must be present in the `refs` subdirectory of the image.
Defaults to `v1.0`.
Only applicable if type is image or imageLayout.
Only applicable if type is image.

**--type**
Type of the file to validate. If unset, oci-image-validate will try to auto-detect the type. One of "imageLayout,image,manifest,manifestList,config"
Type of the file to validate. If unset, oci-image-validate will try to auto-detect the type. One of "image,manifest,manifestList,config"

# EXAMPLES
```
$ skopeo copy docker://busybox oci:busybox-oci
$ oci-image-validate --type imageLayout --ref latest busybox-oci
$ oci-image-validate --type image --ref latest busybox-oci
busybox-oci: OK
```

Expand Down
13 changes: 6 additions & 7 deletions cmd/oci-unpack/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import (

"github.com/opencontainers/image-tools/image"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)

// supported unpack types
var unpackTypes = []string{
image.TypeImageLayout,
image.TypeImage,
}

Expand Down Expand Up @@ -56,8 +56,8 @@ func newUnpackCmd(stdout, stderr *log.Logger) *cobra.Command {

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].`,
Short: "Unpack an image",
Long: `Unpack the OCI image present at [src] to the destination directory [dest].`,
Run: v.Run,
}

Expand Down Expand Up @@ -86,6 +86,8 @@ func (v *unpackCmd) Run(cmd *cobra.Command, args []string) {
os.Exit(1)
}

ctx := context.Background()

if v.typ == "" {
typ, err := image.Autodetect(args[0])
if err != nil {
Expand All @@ -97,11 +99,8 @@ func (v *unpackCmd) Run(cmd *cobra.Command, args []string) {

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

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

if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/oci-unpack/oci-unpack.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ oci-unpack \- Unpack an image or image source layout
The ref pointing to the manifest to be unpacked. This must be present in the "refs" subdirectory of the image. (default "v1.0")

**--type**
Type of the file to unpack. If unset, oci-unpack will try to auto-detect the type. One of "imageLayout,image"
Type of the file to unpack. If unset, oci-unpack will try to auto-detect the type. One of "image"

# EXAMPLES
```
Expand Down
3 changes: 1 addition & 2 deletions image/autodetect.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (

// supported autodetection types
const (
TypeImageLayout = "imageLayout"
TypeImage = "image"
TypeManifest = "manifest"
TypeManifestList = "manifestList"
Expand All @@ -43,7 +42,7 @@ func Autodetect(path string) (string, error) {
}

if fi.IsDir() {
return TypeImageLayout, nil
return TypeImage, nil
}

f, err := os.Open(path)
Expand Down
70 changes: 35 additions & 35 deletions image/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,58 +18,58 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/opencontainers/image-spec/schema"
imagespecs "github.com/opencontainers/image-spec/specs-go"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/image-tools/image/cas"
runtimespecs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"golang.org/x/net/context"
)

type config v1.Image
func findConfig(ctx context.Context, engine cas.Engine, descriptor *imagespecs.Descriptor) (config *v1.Image, err error) {
err = validateMediaType(descriptor.MediaType, []string{v1.MediaTypeImageConfig})
if err != nil {
return nil, errors.Wrap(err, "invalid config media type")
}

func findConfig(w walker, d *descriptor) (*config, error) {
var c config
cpath := filepath.Join("blobs", d.algo(), d.hash())
err = validateDescriptor(ctx, engine, descriptor)
if err != nil {
return nil, errors.Wrap(err, "invalid config descriptor")
}

switch err := w.walk(func(path string, info os.FileInfo, r io.Reader) error {
if info.IsDir() || filepath.Clean(path) != cpath {
return nil
}
buf, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrapf(err, "%s: error reading config", path)
}
reader, err := engine.Get(ctx, descriptor.Digest)
if err != nil {
return nil, errors.Wrapf(err, "failed to fetch %s", descriptor.Digest)
}

if err := schema.MediaTypeImageConfig.Validate(bytes.NewReader(buf)); err != nil {
return errors.Wrapf(err, "%s: config validation failed", path)
}
buf, err := ioutil.ReadAll(reader)
if err != nil {
return nil, errors.Wrapf(err, "%s: error reading manifest", descriptor.Digest)
}

if err := json.Unmarshal(buf, &c); err != nil {
return err
}
return errEOW
}); err {
case nil:
return nil, fmt.Errorf("%s: config not found", cpath)
case errEOW:
return &c, nil
default:
if err := schema.MediaTypeImageConfig.Validate(bytes.NewReader(buf)); err != nil {
return nil, errors.Wrapf(err, "%s: config validation failed", descriptor.Digest)
}

var c v1.Image
if err := json.Unmarshal(buf, &c); err != nil {
return nil, err
}

return &c, nil
}

func (c *config) runtimeSpec(rootfs string) (*specs.Spec, error) {
func runtimeSpec(c *v1.Image, rootfs string) (*runtimespecs.Spec, error) {
if c.OS != "linux" {
return nil, fmt.Errorf("%s: unsupported OS", c.OS)
}

var s specs.Spec
var s runtimespecs.Spec
s.Version = "0.5.0"
// we should at least apply the default spec, otherwise this is totally useless
s.Process.Terminal = true
Expand Down Expand Up @@ -112,12 +112,12 @@ func (c *config) runtimeSpec(rootfs string) (*specs.Spec, error) {
swap := uint64(c.Config.MemorySwap)
shares := uint64(c.Config.CPUShares)

s.Linux.Resources = &specs.Resources{
CPU: &specs.CPU{
s.Linux.Resources = &runtimespecs.Resources{
CPU: &runtimespecs.CPU{
Shares: &shares,
},

Memory: &specs.Memory{
Memory: &runtimespecs.Memory{
Limit: &mem,
Reservation: &mem,
Swap: &swap,
Expand All @@ -127,7 +127,7 @@ func (c *config) runtimeSpec(rootfs string) (*specs.Spec, error) {
for vol := range c.Config.Volumes {
s.Mounts = append(
s.Mounts,
specs.Mount{
runtimespecs.Mount{
Destination: vol,
Type: "bind",
Options: []string{"rbind"},
Expand Down
Loading

0 comments on commit 48ae1f2

Please sign in to comment.