update vendor

Signed-off-by: Mike Brown <brownwm@us.ibm.com>
This commit is contained in:
Mike Brown 2017-09-13 18:12:37 -05:00
parent 5b44da76e8
commit 8310fb4bfd
33 changed files with 2500 additions and 308 deletions

View File

@ -1,5 +1,5 @@
RUNC_VERSION=e775f0fba3ea329b8b766451c892c41a3d49594d RUNC_VERSION=593914b8bd5448a93f7c3e4902a03408b6d5c0ce
CNI_VERSION=v0.6.0 CNI_VERSION=v0.6.0
CONTAINERD_VERSION=v1.0.0-beta.0 CONTAINERD_VERSION=9f28040426990a8a640019b2a69b4cacb6991c01
CRITEST_VERSION=d452f7fe9ef7ccc5ec63a8306cf838510cb83441 CRITEST_VERSION=3028b837818133b4e83758cf9aee867f0a3ff6d2
KUBERNETES_VERSION=11a836078d0c78a4253a77a3ff6f4a555c4121f9 KUBERNETES_VERSION=11a836078d0c78a4253a77a3ff6f4a555c4121f9

View File

@ -1,11 +1,11 @@
github.com/blang/semver v3.1.0 github.com/blang/semver v3.1.0
github.com/boltdb/bolt v1.3.0-58-ge9cf4fa github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/containerd/containerd v1.0.0-beta.0 github.com/containerd/containerd 9f28040426990a8a640019b2a69b4cacb6991c01
github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
github.com/containerd/cgroups 5933ab4dc4f7caa3a73a1dc141bd11f42b5c9163 github.com/containerd/cgroups 5933ab4dc4f7caa3a73a1dc141bd11f42b5c9163
github.com/coreos/go-systemd d2196463941895ee908e13531a23a39feb9e1243 github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
github.com/containernetworking/cni v0.6.0 github.com/containernetworking/cni v0.6.0
github.com/containernetworking/plugins v0.6.0 github.com/containernetworking/plugins v0.6.0
github.com/cri-o/ocicni 73f1309d6bc5c3eac78c1382408921cd771ff22e github.com/cri-o/ocicni 73f1309d6bc5c3eac78c1382408921cd771ff22e
@ -27,6 +27,8 @@ github.com/go-openapi/jsonreference 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
github.com/go-openapi/spec 6aced65f8501fe1217321abf0749d354824ba2ff github.com/go-openapi/spec 6aced65f8501fe1217321abf0749d354824ba2ff
github.com/go-openapi/swag 1d0bd113de87027671077d3c71eb3ac5d7dbba72 github.com/go-openapi/swag 1d0bd113de87027671077d3c71eb3ac5d7dbba72
github.com/godbus/dbus 97646858c46433e4afb3432ad28c12e968efa298 github.com/godbus/dbus 97646858c46433e4afb3432ad28c12e968efa298
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f
github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342 github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342
github.com/mailru/easyjson d5b7844b561a7bc640052f1b935f7b800330d7e0 github.com/mailru/easyjson d5b7844b561a7bc640052f1b935f7b800330d7e0
github.com/Microsoft/go-winio v0.4.4 github.com/Microsoft/go-winio v0.4.4
@ -34,7 +36,7 @@ github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
github.com/opencontainers/image-spec v1.0.0 github.com/opencontainers/image-spec v1.0.0
github.com/opencontainers/runc e775f0fba3ea329b8b766451c892c41a3d49594d github.com/opencontainers/runc e775f0fba3ea329b8b766451c892c41a3d49594d
github.com/opencontainers/runtime-spec v1.0.0 github.com/opencontainers/runtime-spec v1.0.0
github.com/opencontainers/runtime-tools e29f3ca4eb806a582ee1a1864c7b0563bd64c19b github.com/opencontainers/runtime-tools 6073aff4ac61897f75895123f7e24135204a404d
github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206 github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206
github.com/pkg/errors v0.8.0 github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib v1.0.0

View File

@ -5,6 +5,7 @@ import (
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/typeurl" "github.com/containerd/containerd/typeurl"
"github.com/gogo/protobuf/types" "github.com/gogo/protobuf/types"
"github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/identity"
@ -79,7 +80,7 @@ func WithSnapshot(id string) NewContainerOpts {
// root filesystem in read-write mode // root filesystem in read-write mode
func WithNewSnapshot(id string, i Image) NewContainerOpts { func WithNewSnapshot(id string, i Image) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error { return func(ctx context.Context, client *Client, c *containers.Container) error {
diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore()) diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Format(platforms.Default()))
if err != nil { if err != nil {
return err return err
} }
@ -108,7 +109,7 @@ func WithSnapshotCleanup(ctx context.Context, client *Client, c containers.Conta
// root filesystem in read-only mode // root filesystem in read-only mode
func WithNewSnapshotView(id string, i Image) NewContainerOpts { func WithNewSnapshotView(id string, i Image) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error { return func(ctx context.Context, client *Client, c *containers.Container) error {
diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore()) diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Format(platforms.Default()))
if err != nil { if err != nil {
return err return err
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
protobuf "github.com/gogo/protobuf/types" protobuf "github.com/gogo/protobuf/types"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
@ -38,7 +39,7 @@ func WithCheckpoint(desc v1.Descriptor, snapshotKey string) NewContainerOpts {
fk := m fk := m
rw = &fk rw = &fk
case images.MediaTypeDockerSchema2Manifest: case images.MediaTypeDockerSchema2Manifest:
config, err := images.Config(ctx, store, m) config, err := images.Config(ctx, store, m, platforms.Format(platforms.Default()))
if err != nil { if err != nil {
return err return err
} }

View File

@ -26,7 +26,7 @@ var (
ErrAlreadyExists = errors.New("already exists") ErrAlreadyExists = errors.New("already exists")
ErrFailedPrecondition = errors.New("failed precondition") ErrFailedPrecondition = errors.New("failed precondition")
ErrUnavailable = errors.New("unavailable") ErrUnavailable = errors.New("unavailable")
ErrNotSupported = errors.New("not supported") // represents not supported and unimplemented ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented
) )
func IsInvalidArgument(err error) bool { func IsInvalidArgument(err error) bool {
@ -54,6 +54,6 @@ func IsUnavailable(err error) bool {
return errors.Cause(err) == ErrUnavailable return errors.Cause(err) == ErrUnavailable
} }
func IsNotSupported(err error) bool { func IsNotImplemented(err error) bool {
return errors.Cause(err) == ErrNotSupported return errors.Cause(err) == ErrNotImplemented
} }

View File

@ -38,7 +38,7 @@ func ToGRPC(err error) error {
return grpc.Errorf(codes.FailedPrecondition, err.Error()) return grpc.Errorf(codes.FailedPrecondition, err.Error())
case IsUnavailable(err): case IsUnavailable(err):
return grpc.Errorf(codes.Unavailable, err.Error()) return grpc.Errorf(codes.Unavailable, err.Error())
case IsNotSupported(err): case IsNotImplemented(err):
return grpc.Errorf(codes.Unimplemented, err.Error()) return grpc.Errorf(codes.Unimplemented, err.Error())
} }
@ -72,7 +72,7 @@ func FromGRPC(err error) error {
case codes.FailedPrecondition: case codes.FailedPrecondition:
cls = ErrFailedPrecondition cls = ErrFailedPrecondition
case codes.Unimplemented: case codes.Unimplemented:
cls = ErrNotSupported cls = ErrNotImplemented
default: default:
cls = ErrUnknown cls = ErrUnknown
} }

View File

@ -2,10 +2,9 @@ package containerd
import ( import (
"context" "context"
"encoding/json"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/rootfs" "github.com/containerd/containerd/rootfs"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@ -46,7 +45,7 @@ func (i *image) Target() ocispec.Descriptor {
func (i *image) RootFS(ctx context.Context) ([]digest.Digest, error) { func (i *image) RootFS(ctx context.Context) ([]digest.Digest, error) {
provider := i.client.ContentStore() provider := i.client.ContentStore()
return i.i.RootFS(ctx, provider) return i.i.RootFS(ctx, provider, platforms.Format(platforms.Default()))
} }
func (i *image) Size(ctx context.Context) (int64, error) { func (i *image) Size(ctx context.Context) (int64, error) {
@ -56,11 +55,11 @@ func (i *image) Size(ctx context.Context) (int64, error) {
func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) { func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) {
provider := i.client.ContentStore() provider := i.client.ContentStore()
return i.i.Config(ctx, provider) return i.i.Config(ctx, provider, platforms.Format(platforms.Default()))
} }
func (i *image) Unpack(ctx context.Context, snapshotterName string) error { func (i *image) Unpack(ctx context.Context, snapshotterName string) error {
layers, err := i.getLayers(ctx) layers, err := i.getLayers(ctx, platforms.Format(platforms.Default()))
if err != nil { if err != nil {
return err return err
} }
@ -98,19 +97,15 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error {
return nil return nil
} }
func (i *image) getLayers(ctx context.Context) ([]rootfs.Layer, error) { func (i *image) getLayers(ctx context.Context, platform string) ([]rootfs.Layer, error) {
cs := i.client.ContentStore() cs := i.client.ContentStore()
// TODO: Support manifest list manifest, err := images.Manifest(ctx, cs, i.i.Target, platform)
p, err := content.ReadBlob(ctx, cs, i.i.Target.Digest)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to read manifest blob") return nil, errors.Wrap(err, "")
} }
var manifest ocispec.Manifest
if err := json.Unmarshal(p, &manifest); err != nil { diffIDs, err := i.i.RootFS(ctx, cs, platform)
return nil, errors.Wrap(err, "failed to unmarshal manifest")
}
diffIDs, err := i.i.RootFS(ctx, cs)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to resolve rootfs") return nil, errors.Wrap(err, "failed to resolve rootfs")
} }

View File

@ -67,7 +67,7 @@ func Walk(ctx context.Context, handler Handler, descs ...ocispec.Descriptor) err
children, err := handler.Handle(ctx, desc) children, err := handler.Handle(ctx, desc)
if err != nil { if err != nil {
if errors.Cause(err) == SkipDesc { if errors.Cause(err) == SkipDesc {
return nil // don't traverse the children. continue // don't traverse the children.
} }
return err return err
} }

View File

@ -6,6 +6,8 @@ import (
"time" "time"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/platforms"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -39,16 +41,16 @@ type Store interface {
// //
// The caller can then use the descriptor to resolve and process the // The caller can then use the descriptor to resolve and process the
// configuration of the image. // configuration of the image.
func (image *Image) Config(ctx context.Context, provider content.Provider) (ocispec.Descriptor, error) { func (image *Image) Config(ctx context.Context, provider content.Provider, platform string) (ocispec.Descriptor, error) {
return Config(ctx, provider, image.Target) return Config(ctx, provider, image.Target, platform)
} }
// RootFS returns the unpacked diffids that make up and images rootfs. // RootFS returns the unpacked diffids that make up and images rootfs.
// //
// These are used to verify that a set of layers unpacked to the expected // These are used to verify that a set of layers unpacked to the expected
// values. // values.
func (image *Image) RootFS(ctx context.Context, provider content.Provider) ([]digest.Digest, error) { func (image *Image) RootFS(ctx context.Context, provider content.Provider, platform string) ([]digest.Digest, error) {
desc, err := image.Config(ctx, provider) desc, err := image.Config(ctx, provider, platform)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -67,17 +69,23 @@ func (image *Image) Size(ctx context.Context, provider content.Provider) (int64,
}), ChildrenHandler(provider)), image.Target) }), ChildrenHandler(provider)), image.Target)
} }
// Config resolves the image configuration descriptor using a content provided func Manifest(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform string) (ocispec.Manifest, error) {
// to resolve child resources on the image. var (
// matcher platforms.Matcher
// The caller can then use the descriptor to resolve and process the m *ocispec.Manifest
// configuration of the image. err error
func Config(ctx context.Context, provider content.Provider, image ocispec.Descriptor) (ocispec.Descriptor, error) { )
var configDesc ocispec.Descriptor if platform != "" {
return configDesc, Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { matcher, err = platforms.Parse(platform)
switch image.MediaType { if err != nil {
return ocispec.Manifest{}, err
}
}
if err := Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
switch desc.MediaType {
case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
p, err := content.ReadBlob(ctx, provider, image.Digest) p, err := content.ReadBlob(ctx, provider, desc.Digest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -87,14 +95,108 @@ func Config(ctx context.Context, provider content.Provider, image ocispec.Descri
return nil, err return nil, err
} }
configDesc = manifest.Config if platform != "" {
if desc.Platform != nil && !matcher.Match(*desc.Platform) {
return nil, nil return nil, nil
default:
return nil, errors.New("could not resolve config")
} }
}), image) if desc.Platform == nil {
p, err := content.ReadBlob(ctx, provider, manifest.Config.Digest)
if err != nil {
return nil, err
}
var image ocispec.Image
if err := json.Unmarshal(p, &image); err != nil {
return nil, err
}
if !matcher.Match(platforms.Normalize(ocispec.Platform{OS: image.OS, Architecture: image.Architecture})) {
return nil, nil
}
}
}
m = &manifest
return nil, nil
case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
p, err := content.ReadBlob(ctx, provider, desc.Digest)
if err != nil {
return nil, err
}
var idx ocispec.Index
if err := json.Unmarshal(p, &idx); err != nil {
return nil, err
}
if platform == "" {
return idx.Manifests, nil
}
var descs []ocispec.Descriptor
for _, d := range idx.Manifests {
if d.Platform == nil || matcher.Match(*d.Platform) {
descs = append(descs, d)
}
}
return descs, nil
}
return nil, errors.New("could not resolve manifest")
}), image); err != nil {
return ocispec.Manifest{}, err
}
if m == nil {
return ocispec.Manifest{}, errors.Wrap(errdefs.ErrNotFound, "manifest not found")
}
return *m, nil
}
// Config resolves the image configuration descriptor using a content provided
// to resolve child resources on the image.
//
// The caller can then use the descriptor to resolve and process the
// configuration of the image.
func Config(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform string) (ocispec.Descriptor, error) {
manifest, err := Manifest(ctx, provider, image, platform)
if err != nil {
return ocispec.Descriptor{}, err
}
return manifest.Config, err
}
// Platforms returns one or more platforms supported by the image.
func Platforms(ctx context.Context, provider content.Provider, image ocispec.Descriptor) ([]ocispec.Platform, error) {
var platformSpecs []ocispec.Platform
return platformSpecs, Walk(ctx, Handlers(HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.Platform != nil {
platformSpecs = append(platformSpecs, *desc.Platform)
return nil, SkipDesc
}
switch desc.MediaType {
case MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
p, err := content.ReadBlob(ctx, provider, desc.Digest)
if err != nil {
return nil, err
}
var image ocispec.Image
if err := json.Unmarshal(p, &image); err != nil {
return nil, err
}
platformSpecs = append(platformSpecs,
platforms.Normalize(ocispec.Platform{OS: image.OS, Architecture: image.Architecture}))
}
return nil, nil
}), ChildrenHandler(provider)), image)
} }
// RootFS returns the unpacked diffids that make up and images rootfs. // RootFS returns the unpacked diffids that make up and images rootfs.

View File

@ -0,0 +1,77 @@
package platforms
import (
"runtime"
"strings"
)
// These function are generated from from https://golang.org/src/go/build/syslist.go.
//
// We use switch statements because they are slightly faster than map lookups
// and use a little less memory.
// isKnownOS returns true if we know about the operating system.
//
// The OS value should be normalized before calling this function.
func isKnownOS(os string) bool {
switch os {
case "android", "darwin", "dragonfly", "freebsd", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos":
return true
}
return false
}
// isKnownArch returns true if we know about the architecture.
//
// The arch value should be normalized before being passed to this function.
func isKnownArch(arch string) bool {
switch arch {
case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "s390", "s390x", "sparc", "sparc64":
return true
}
return false
}
func normalizeOS(os string) string {
if os == "" {
return runtime.GOOS
}
os = strings.ToLower(os)
switch os {
case "macos":
os = "darwin"
}
return os
}
// normalizeArch normalizes the architecture.
func normalizeArch(arch, variant string) (string, string) {
arch, variant = strings.ToLower(arch), strings.ToLower(variant)
switch arch {
case "i386":
arch = "386"
variant = ""
case "x86_64", "x86-64":
arch = "amd64"
variant = ""
case "aarch64":
arch = "arm64"
variant = "" // v8 is implied
case "armhf":
arch = "arm"
variant = ""
case "armel":
arch = "arm"
variant = "v6"
case "arm":
switch variant {
case "v7", "7":
variant = "v7"
case "5", "6", "8":
variant = "v" + variant
}
}
return arch, variant
}

View File

@ -0,0 +1,16 @@
package platforms
import (
"runtime"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)
// Default returns the current platform's default platform specification.
func Default() specs.Platform {
return specs.Platform{
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
// TODO(stevvooe): Need to resolve GOARM for arm hosts.
}
}

View File

@ -0,0 +1,236 @@
// Package platforms provides a toolkit for normalizing, matching and
// specifying container platforms.
//
// Centered around OCI platform specifications, we define a string-based
// specifier syntax that can be used for user input. With a specifier, users
// only need to specify the parts of the platform that are relevant to their
// context, providing an operating system or architecture or both.
//
// How do I use this package?
//
// The vast majority of use cases should simply use the match function with
// user input. The first step is to parse a specifier into a matcher:
//
// m, err := Parse("linux")
// if err != nil { ... }
//
// Once you have a matcher, use it to match against the platform declared by a
// component, typically from an image or runtime. Since extracting an images
// platform is a little more involved, we'll use an example against the
// platform default:
//
// if ok := m.Match(Default()); !ok { /* doesn't match */ }
//
// This can be composed in loops for resolving runtimes or used as a filter for
// fetch and select images.
//
// More details of the specifier syntax and platform spec follow.
//
// Declaring Platform Support
//
// Components that have strict platform requirements should use the OCI
// platform specification to declare their support. Typically, this will be
// images and runtimes that should make these declaring which platform they
// support specifically. This looks roughly as follows:
//
// type Platform struct {
// Architecture string
// OS string
// Variant string
// }
//
// Most images and runtimes should at least set Architecture and OS, according
// to their GOARCH and GOOS values, respectively (follow the OCI image
// specification when in doubt). ARM should set variant under certain
// discussions, which are outlined below.
//
// Platform Specifiers
//
// While the OCI platform specifications provide a tool for components to
// specify structured information, user input typically doesn't need the full
// context and much can be inferred. To solve this problem, we introduced
// "specifiers". A specifier has the format
// `<os>|<arch>|<os>/<arch>[/<variant>]`. The user can provide either the
// operating system or the architecture or both.
//
// An example of a common specifier is `linux/amd64`. If the host has a default
// of runtime that matches this, the user can simply provide the component that
// matters. For example, if a image provides amd64 and arm64 support, the
// operating system, `linux` can be inferred, so they only have to provide
// `arm64` or `amd64`. Similar behavior is implemented for operating systems,
// where the architecture may be known but a runtime may support images from
// different operating systems.
//
// Normalization
//
// Because not all users are familiar with the way the Go runtime represents
// platforms, several normalizations have been provided to make this package
// easier to user.
//
// The following are performed for architectures:
//
// Value Normalized
// aarch64 arm64
// armhf arm
// armel arm/v6
// i386 386
// x86_64 amd64
// x86-64 amd64
//
// We also normalize the operating system `macos` to `darwin`.
//
// ARM Support
//
// To qualify ARM architecture, the Variant field is used to qualify the arm
// version. The most common arm version, v7, is represented without the variant
// unless it is explicitly provided. This is treated as equivalent to armhf. A
// previous architecture, armel, will be normalized to arm/v6.
//
// While these normalizations are provided, their support on arm platforms has
// not yet been fully implemented and tested.
package platforms
import (
"regexp"
"runtime"
"strings"
"github.com/containerd/containerd/errdefs"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
var (
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
)
// Matcher matches platforms specifications, provided by an image or runtime.
type Matcher interface {
Spec() specs.Platform
Match(platform specs.Platform) bool
}
type matcher struct {
specs.Platform
}
func (m *matcher) Spec() specs.Platform {
return m.Platform
}
func (m *matcher) Match(platform specs.Platform) bool {
normalized := Normalize(platform)
return m.OS == normalized.OS &&
m.Architecture == normalized.Architecture &&
m.Variant == normalized.Variant
}
func (m *matcher) String() string {
return Format(m.Platform)
}
// Parse parses the platform specifier syntax into a platform declaration.
//
// Platform specifiers are in the format `<os>|<arch>|<os>/<arch>[/<variant>]`.
// The minimum required information for a platform specifier is the operating
// system or architecture. If there is only a single string (no slashes), the
// value will be matched against the known set of operating systems, then fall
// back to the known set of architectures. The missing component will be
// inferred based on the local environment.
//
// Applications should opt to use `Match` over directly parsing specifiers.
func Parse(specifier string) (Matcher, error) {
if strings.Contains(specifier, "*") {
// TODO(stevvooe): need to work out exact wildcard handling
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: wildcards not yet supported", specifier)
}
parts := strings.Split(specifier, "/")
for _, part := range parts {
if !specifierRe.MatchString(part) {
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%q is an invalid component of %q: platform specifier component must match %q", part, specifier, specifierRe.String())
}
}
var p specs.Platform
switch len(parts) {
case 1:
// in this case, we will test that the value might be an OS, then look
// it up. If it is not known, we'll treat it as an architecture. Since
// we have very little information about the platform here, we are
// going to be a little more strict if we don't know about the argument
// value.
p.OS = normalizeOS(parts[0])
if isKnownOS(p.OS) {
// picks a default architecture
p.Architecture = runtime.GOARCH
if p.Architecture == "arm" {
// TODO(stevvooe): Resolve arm variant, if not v6 (default)
return nil, errors.Wrapf(errdefs.ErrNotImplemented, "arm support not fully implemented")
}
return &matcher{p}, nil
}
p.Architecture, p.Variant = normalizeArch(parts[0], "")
if isKnownArch(p.Architecture) {
p.OS = runtime.GOOS
return &matcher{p}, nil
}
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: unknown operating system or architecture", specifier)
case 2:
// In this case, we treat as a regular os/arch pair. We don't care
// about whether or not we know of the platform.
p.OS = normalizeOS(parts[0])
p.Architecture, p.Variant = normalizeArch(parts[1], "")
return &matcher{p}, nil
case 3:
// we have a fully specified variant, this is rare
p.OS = normalizeOS(parts[0])
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
return &matcher{p}, nil
}
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: cannot parse platform specifier", specifier)
}
// Format returns a string specifier from the provided platform specification.
func Format(platform specs.Platform) string {
if platform.OS == "" {
return "unknown"
}
return joinNotEmpty(platform.OS, platform.Architecture, platform.Variant)
}
func joinNotEmpty(s ...string) string {
var ss []string
for _, s := range s {
if s == "" {
continue
}
ss = append(ss, s)
}
return strings.Join(ss, "/")
}
// Normalize validates and translate the platform to the canonical value.
//
// For example, if "Aarch64" is encountered, we change it to "arm64" or if
// "x86_64" is encountered, it becomes "amd64".
func Normalize(platform specs.Platform) specs.Platform {
platform.OS = normalizeOS(platform.OS)
platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant)
// these fields are deprecated, remove them
platform.OSFeatures = nil
platform.OSVersion = ""
return platform
}

View File

@ -74,7 +74,7 @@ func (s *service) Apply(ctx context.Context, er *diffapi.ApplyRequest) (*diffapi
for _, differ := range s.differs { for _, differ := range s.differs {
ocidesc, err = differ.Apply(ctx, desc, mounts) ocidesc, err = differ.Apply(ctx, desc, mounts)
if !errdefs.IsNotSupported(err) { if !errdefs.IsNotImplemented(err) {
break break
} }
} }
@ -99,7 +99,7 @@ func (s *service) Diff(ctx context.Context, dr *diffapi.DiffRequest) (*diffapi.D
for _, differ := range s.differs { for _, differ := range s.differs {
ocidesc, err = differ.DiffMounts(ctx, aMounts, bMounts, dr.MediaType, dr.Ref) ocidesc, err = differ.DiffMounts(ctx, aMounts, bMounts, dr.MediaType, dr.Ref)
if !errdefs.IsNotSupported(err) { if !errdefs.IsNotImplemented(err) {
break break
} }
} }

View File

@ -19,6 +19,7 @@ import (
"github.com/containerd/containerd/fs" "github.com/containerd/containerd/fs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/platforms"
"github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/identity"
"github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runc/libcontainer/user" "github.com/opencontainers/runc/libcontainer/user"
@ -72,7 +73,7 @@ func WithImageConfig(i Image) SpecOpts {
image = i.(*image) image = i.(*image)
store = client.ContentStore() store = client.ContentStore()
) )
ic, err := image.i.Config(ctx, store) ic, err := image.i.Config(ctx, store, platforms.Format(platforms.Default()))
if err != nil { if err != nil {
return err return err
} }
@ -235,7 +236,7 @@ func WithRemappedSnapshotView(id string, i Image, uid, gid uint32) NewContainerO
func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool) NewContainerOpts { func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error { return func(ctx context.Context, client *Client, c *containers.Container) error {
diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore()) diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Format(platforms.Default()))
if err != nil { if err != nil {
return err return err
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/image-spec/specs-go/v1"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
) )
@ -20,7 +21,7 @@ func WithImageConfig(i Image) SpecOpts {
image = i.(*image) image = i.(*image)
store = client.ContentStore() store = client.ContentStore()
) )
ic, err := image.i.Config(ctx, store) ic, err := image.i.Config(ctx, store, platforms.Format(platforms.Default()))
if err != nil { if err != nil {
return err return err
} }

View File

@ -25,7 +25,7 @@ var (
} }
) )
func defaltCaps() []string { func defaultCaps() []string {
return []string{ return []string{
"CAP_CHOWN", "CAP_CHOWN",
"CAP_DAC_OVERRIDE", "CAP_DAC_OVERRIDE",
@ -79,10 +79,10 @@ func createDefaultSpec() (*specs.Spec, error) {
GID: 0, GID: 0,
}, },
Capabilities: &specs.LinuxCapabilities{ Capabilities: &specs.LinuxCapabilities{
Bounding: defaltCaps(), Bounding: defaultCaps(),
Permitted: defaltCaps(), Permitted: defaultCaps(),
Inheritable: defaltCaps(), Inheritable: defaultCaps(),
Effective: defaltCaps(), Effective: defaultCaps(),
}, },
Rlimits: []specs.POSIXRlimit{ Rlimits: []specs.POSIXRlimit{
{ {

View File

@ -15,7 +15,7 @@ github.com/docker/go-units v0.3.1
github.com/gogo/protobuf d2e1ade2d719b78fe5b061b4c18a9f7111b5bdc8 github.com/gogo/protobuf d2e1ade2d719b78fe5b061b4c18a9f7111b5bdc8
github.com/golang/protobuf 5a0f697c9ed9d68fef0116532c6e05cfeae00e55 github.com/golang/protobuf 5a0f697c9ed9d68fef0116532c6e05cfeae00e55
github.com/opencontainers/runtime-spec v1.0.0 github.com/opencontainers/runtime-spec v1.0.0
github.com/opencontainers/runc e775f0fba3ea329b8b766451c892c41a3d49594d github.com/opencontainers/runc 593914b8bd5448a93f7c3e4902a03408b6d5c0ce
github.com/sirupsen/logrus v1.0.0 github.com/sirupsen/logrus v1.0.0
github.com/containerd/btrfs cc52c4dea2ce11a44e6639e561bb5c2af9ada9e3 github.com/containerd/btrfs cc52c4dea2ce11a44e6639e561bb5c2af9ada9e3
github.com/stretchr/testify v1.1.4 github.com/stretchr/testify v1.1.4

354
vendor/github.com/hashicorp/errwrap/LICENSE generated vendored Normal file
View File

@ -0,0 +1,354 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. “Contributor”
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. “Contributor Version”
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributors Contribution.
1.3. “Contribution”
means Covered Software of a particular Contributor.
1.4. “Covered Software”
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. “Incompatible With Secondary Licenses”
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of version
1.1 or earlier of the License, but not also under the terms of a
Secondary License.
1.6. “Executable Form”
means any form of the work other than Source Code Form.
1.7. “Larger Work”
means a work that combines Covered Software with other material, in a separate
file or files, that is not Covered Software.
1.8. “License”
means this document.
1.9. “Licensable”
means having the right to grant, to the maximum extent possible, whether at the
time of the initial grant or subsequently, any and all of the rights conveyed by
this License.
1.10. “Modifications”
means any of the following:
a. any file in Source Code Form that results from an addition to, deletion
from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. “Patent Claims” of a Contributor
means any patent claim(s), including without limitation, method, process,
and apparatus claims, in any patent Licensable by such Contributor that
would be infringed, but for the grant of the License, by the making,
using, selling, offering for sale, having made, import, or transfer of
either its Contributions or its Contributor Version.
1.12. “Secondary License”
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. “Source Code Form”
means the form of the work preferred for making modifications.
1.14. “You” (or “Your”)
means an individual or a legal entity exercising rights under this
License. For legal entities, “You” includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, “control” means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or as
part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its Contributions
or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution become
effective for each Contribution on the date the Contributor first distributes
such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under this
License. No additional rights or licenses will be implied from the distribution
or licensing of Covered Software under this License. Notwithstanding Section
2.1(b) above, no patent license is granted by a Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third partys
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of its
Contributions.
This License does not grant any rights in the trademarks, service marks, or
logos of any Contributor (except as may be necessary to comply with the
notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this License
(see Section 10.2) or under the terms of a Secondary License (if permitted
under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its Contributions
are its original creation(s) or it has sufficient rights to grant the
rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under applicable
copyright doctrines of fair use, fair dealing, or other equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under the
terms of this License. You must inform recipients that the Source Code Form
of the Covered Software is governed by the terms of this License, and how
they can obtain a copy of this License. You may not attempt to alter or
restrict the recipients rights in the Source Code Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this License,
or sublicense it under different terms, provided that the license for
the Executable Form does not attempt to limit or alter the recipients
rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for the
Covered Software. If the Larger Work is a combination of Covered Software
with a work governed by one or more Secondary Licenses, and the Covered
Software is not Incompatible With Secondary Licenses, this License permits
You to additionally distribute such Covered Software under the terms of
such Secondary License(s), so that the recipient of the Larger Work may, at
their option, further distribute the Covered Software under the terms of
either this License or such Secondary License(s).
3.4. Notices
You may not remove or alter the substance of any license notices (including
copyright notices, patent notices, disclaimers of warranty, or limitations
of liability) contained within the Source Code Form of the Covered
Software, except that You may alter any license notices to the extent
required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on behalf
of any Contributor. You must make it absolutely clear that any such
warranty, support, indemnity, or liability obligation is offered by You
alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute, judicial
order, or regulation then You must: (a) comply with the terms of this License
to the maximum extent possible; and (b) describe the limitations and the code
they affect. Such description must be placed in a text file included with all
distributions of the Covered Software under this License. Except to the
extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
if such Contributor fails to notify You of the non-compliance by some
reasonable means prior to 60 days after You have come back into compliance.
Moreover, Your grants from a particular Contributor are reinstated on an
ongoing basis if such Contributor notifies You of the non-compliance by
some reasonable means, this is the first time You have received notice of
non-compliance with this License from such Contributor, and You become
compliant prior to 30 days after Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions, counter-claims,
and cross-claims) alleging that a Contributor Version directly or
indirectly infringes any patent, then the rights granted to You by any and
all Contributors for the Covered Software under Section 2.1 of this License
shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an “as is” basis, without
warranty of any kind, either expressed, implied, or statutory, including,
without limitation, warranties that the Covered Software is free of defects,
merchantable, fit for a particular purpose or non-infringing. The entire
risk as to the quality and performance of the Covered Software is with You.
Should any Covered Software prove defective in any respect, You (not any
Contributor) assume the cost of any necessary servicing, repair, or
correction. This disclaimer of warranty constitutes an essential part of this
License. No use of any Covered Software is authorized under this License
except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from such
partys negligence to the extent applicable law prohibits such limitation.
Some jurisdictions do not allow the exclusion or limitation of incidental or
consequential damages, so this exclusion and limitation may not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts of
a jurisdiction where the defendant maintains its principal place of business
and such litigation shall be governed by laws of that jurisdiction, without
reference to its conflict-of-law provisions. Nothing in this Section shall
prevent a partys ability to bring cross-claims or counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject matter
hereof. If any provision of this License is held to be unenforceable, such
provision shall be reformed only to the extent necessary to make it
enforceable. Any law or regulation which provides that the language of a
contract shall be construed against the drafter shall not be used to construe
this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version of
the License under which You originally received the Covered Software, or
under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a modified
version of this License if you rename the license and remove any
references to the name of the license steward (except to note that such
modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a relevant
directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - “Incompatible With Secondary Licenses” Notice
This Source Code Form is “Incompatible
With Secondary Licenses”, as defined by
the Mozilla Public License, v. 2.0.

89
vendor/github.com/hashicorp/errwrap/README.md generated vendored Normal file
View File

@ -0,0 +1,89 @@
# errwrap
`errwrap` is a package for Go that formalizes the pattern of wrapping errors
and checking if an error contains another error.
There is a common pattern in Go of taking a returned `error` value and
then wrapping it (such as with `fmt.Errorf`) before returning it. The problem
with this pattern is that you completely lose the original `error` structure.
Arguably the _correct_ approach is that you should make a custom structure
implementing the `error` interface, and have the original error as a field
on that structure, such [as this example](http://golang.org/pkg/os/#PathError).
This is a good approach, but you have to know the entire chain of possible
rewrapping that happens, when you might just care about one.
`errwrap` formalizes this pattern (it doesn't matter what approach you use
above) by giving a single interface for wrapping errors, checking if a specific
error is wrapped, and extracting that error.
## Installation and Docs
Install using `go get github.com/hashicorp/errwrap`.
Full documentation is available at
http://godoc.org/github.com/hashicorp/errwrap
## Usage
#### Basic Usage
Below is a very basic example of its usage:
```go
// A function that always returns an error, but wraps it, like a real
// function might.
func tryOpen() error {
_, err := os.Open("/i/dont/exist")
if err != nil {
return errwrap.Wrapf("Doesn't exist: {{err}}", err)
}
return nil
}
func main() {
err := tryOpen()
// We can use the Contains helpers to check if an error contains
// another error. It is safe to do this with a nil error, or with
// an error that doesn't even use the errwrap package.
if errwrap.Contains(err, ErrNotExist) {
// Do something
}
if errwrap.ContainsType(err, new(os.PathError)) {
// Do something
}
// Or we can use the associated `Get` functions to just extract
// a specific error. This would return nil if that specific error doesn't
// exist.
perr := errwrap.GetType(err, new(os.PathError))
}
```
#### Custom Types
If you're already making custom types that properly wrap errors, then
you can get all the functionality of `errwraps.Contains` and such by
implementing the `Wrapper` interface with just one function. Example:
```go
type AppError {
Code ErrorCode
Err error
}
func (e *AppError) WrappedErrors() []error {
return []error{e.Err}
}
```
Now this works:
```go
err := &AppError{Err: fmt.Errorf("an error")}
if errwrap.ContainsType(err, fmt.Errorf("")) {
// This will work!
}
```

169
vendor/github.com/hashicorp/errwrap/errwrap.go generated vendored Normal file
View File

@ -0,0 +1,169 @@
// Package errwrap implements methods to formalize error wrapping in Go.
//
// All of the top-level functions that take an `error` are built to be able
// to take any error, not just wrapped errors. This allows you to use errwrap
// without having to type-check and type-cast everywhere.
package errwrap
import (
"errors"
"reflect"
"strings"
)
// WalkFunc is the callback called for Walk.
type WalkFunc func(error)
// Wrapper is an interface that can be implemented by custom types to
// have all the Contains, Get, etc. functions in errwrap work.
//
// When Walk reaches a Wrapper, it will call the callback for every
// wrapped error in addition to the wrapper itself. Since all the top-level
// functions in errwrap use Walk, this means that all those functions work
// with your custom type.
type Wrapper interface {
WrappedErrors() []error
}
// Wrap defines that outer wraps inner, returning an error type that
// can be cleanly used with the other methods in this package, such as
// Contains, GetAll, etc.
//
// This function won't modify the error message at all (the outer message
// will be used).
func Wrap(outer, inner error) error {
return &wrappedError{
Outer: outer,
Inner: inner,
}
}
// Wrapf wraps an error with a formatting message. This is similar to using
// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap
// errors, you should replace it with this.
//
// format is the format of the error message. The string '{{err}}' will
// be replaced with the original error message.
func Wrapf(format string, err error) error {
outerMsg := "<nil>"
if err != nil {
outerMsg = err.Error()
}
outer := errors.New(strings.Replace(
format, "{{err}}", outerMsg, -1))
return Wrap(outer, err)
}
// Contains checks if the given error contains an error with the
// message msg. If err is not a wrapped error, this will always return
// false unless the error itself happens to match this msg.
func Contains(err error, msg string) bool {
return len(GetAll(err, msg)) > 0
}
// ContainsType checks if the given error contains an error with
// the same concrete type as v. If err is not a wrapped error, this will
// check the err itself.
func ContainsType(err error, v interface{}) bool {
return len(GetAllType(err, v)) > 0
}
// Get is the same as GetAll but returns the deepest matching error.
func Get(err error, msg string) error {
es := GetAll(err, msg)
if len(es) > 0 {
return es[len(es)-1]
}
return nil
}
// GetType is the same as GetAllType but returns the deepest matching error.
func GetType(err error, v interface{}) error {
es := GetAllType(err, v)
if len(es) > 0 {
return es[len(es)-1]
}
return nil
}
// GetAll gets all the errors that might be wrapped in err with the
// given message. The order of the errors is such that the outermost
// matching error (the most recent wrap) is index zero, and so on.
func GetAll(err error, msg string) []error {
var result []error
Walk(err, func(err error) {
if err.Error() == msg {
result = append(result, err)
}
})
return result
}
// GetAllType gets all the errors that are the same type as v.
//
// The order of the return value is the same as described in GetAll.
func GetAllType(err error, v interface{}) []error {
var result []error
var search string
if v != nil {
search = reflect.TypeOf(v).String()
}
Walk(err, func(err error) {
var needle string
if err != nil {
needle = reflect.TypeOf(err).String()
}
if needle == search {
result = append(result, err)
}
})
return result
}
// Walk walks all the wrapped errors in err and calls the callback. If
// err isn't a wrapped error, this will be called once for err. If err
// is a wrapped error, the callback will be called for both the wrapper
// that implements error as well as the wrapped error itself.
func Walk(err error, cb WalkFunc) {
if err == nil {
return
}
switch e := err.(type) {
case *wrappedError:
cb(e.Outer)
Walk(e.Inner, cb)
case Wrapper:
cb(err)
for _, err := range e.WrappedErrors() {
Walk(err, cb)
}
default:
cb(err)
}
}
// wrappedError is an implementation of error that has both the
// outer and inner errors.
type wrappedError struct {
Outer error
Inner error
}
func (w *wrappedError) Error() string {
return w.Outer.Error()
}
func (w *wrappedError) WrappedErrors() []error {
return []error{w.Outer, w.Inner}
}

353
vendor/github.com/hashicorp/go-multierror/LICENSE generated vendored Normal file
View File

@ -0,0 +1,353 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. “Contributor”
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. “Contributor Version”
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributors Contribution.
1.3. “Contribution”
means Covered Software of a particular Contributor.
1.4. “Covered Software”
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. “Incompatible With Secondary Licenses”
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of version
1.1 or earlier of the License, but not also under the terms of a
Secondary License.
1.6. “Executable Form”
means any form of the work other than Source Code Form.
1.7. “Larger Work”
means a work that combines Covered Software with other material, in a separate
file or files, that is not Covered Software.
1.8. “License”
means this document.
1.9. “Licensable”
means having the right to grant, to the maximum extent possible, whether at the
time of the initial grant or subsequently, any and all of the rights conveyed by
this License.
1.10. “Modifications”
means any of the following:
a. any file in Source Code Form that results from an addition to, deletion
from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. “Patent Claims” of a Contributor
means any patent claim(s), including without limitation, method, process,
and apparatus claims, in any patent Licensable by such Contributor that
would be infringed, but for the grant of the License, by the making,
using, selling, offering for sale, having made, import, or transfer of
either its Contributions or its Contributor Version.
1.12. “Secondary License”
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. “Source Code Form”
means the form of the work preferred for making modifications.
1.14. “You” (or “Your”)
means an individual or a legal entity exercising rights under this
License. For legal entities, “You” includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, “control” means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or as
part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its Contributions
or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution become
effective for each Contribution on the date the Contributor first distributes
such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under this
License. No additional rights or licenses will be implied from the distribution
or licensing of Covered Software under this License. Notwithstanding Section
2.1(b) above, no patent license is granted by a Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third partys
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of its
Contributions.
This License does not grant any rights in the trademarks, service marks, or
logos of any Contributor (except as may be necessary to comply with the
notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this License
(see Section 10.2) or under the terms of a Secondary License (if permitted
under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its Contributions
are its original creation(s) or it has sufficient rights to grant the
rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under applicable
copyright doctrines of fair use, fair dealing, or other equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under the
terms of this License. You must inform recipients that the Source Code Form
of the Covered Software is governed by the terms of this License, and how
they can obtain a copy of this License. You may not attempt to alter or
restrict the recipients rights in the Source Code Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this License,
or sublicense it under different terms, provided that the license for
the Executable Form does not attempt to limit or alter the recipients
rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for the
Covered Software. If the Larger Work is a combination of Covered Software
with a work governed by one or more Secondary Licenses, and the Covered
Software is not Incompatible With Secondary Licenses, this License permits
You to additionally distribute such Covered Software under the terms of
such Secondary License(s), so that the recipient of the Larger Work may, at
their option, further distribute the Covered Software under the terms of
either this License or such Secondary License(s).
3.4. Notices
You may not remove or alter the substance of any license notices (including
copyright notices, patent notices, disclaimers of warranty, or limitations
of liability) contained within the Source Code Form of the Covered
Software, except that You may alter any license notices to the extent
required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on behalf
of any Contributor. You must make it absolutely clear that any such
warranty, support, indemnity, or liability obligation is offered by You
alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute, judicial
order, or regulation then You must: (a) comply with the terms of this License
to the maximum extent possible; and (b) describe the limitations and the code
they affect. Such description must be placed in a text file included with all
distributions of the Covered Software under this License. Except to the
extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
if such Contributor fails to notify You of the non-compliance by some
reasonable means prior to 60 days after You have come back into compliance.
Moreover, Your grants from a particular Contributor are reinstated on an
ongoing basis if such Contributor notifies You of the non-compliance by
some reasonable means, this is the first time You have received notice of
non-compliance with this License from such Contributor, and You become
compliant prior to 30 days after Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions, counter-claims,
and cross-claims) alleging that a Contributor Version directly or
indirectly infringes any patent, then the rights granted to You by any and
all Contributors for the Covered Software under Section 2.1 of this License
shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an “as is” basis, without
warranty of any kind, either expressed, implied, or statutory, including,
without limitation, warranties that the Covered Software is free of defects,
merchantable, fit for a particular purpose or non-infringing. The entire
risk as to the quality and performance of the Covered Software is with You.
Should any Covered Software prove defective in any respect, You (not any
Contributor) assume the cost of any necessary servicing, repair, or
correction. This disclaimer of warranty constitutes an essential part of this
License. No use of any Covered Software is authorized under this License
except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from such
partys negligence to the extent applicable law prohibits such limitation.
Some jurisdictions do not allow the exclusion or limitation of incidental or
consequential damages, so this exclusion and limitation may not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts of
a jurisdiction where the defendant maintains its principal place of business
and such litigation shall be governed by laws of that jurisdiction, without
reference to its conflict-of-law provisions. Nothing in this Section shall
prevent a partys ability to bring cross-claims or counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject matter
hereof. If any provision of this License is held to be unenforceable, such
provision shall be reformed only to the extent necessary to make it
enforceable. Any law or regulation which provides that the language of a
contract shall be construed against the drafter shall not be used to construe
this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version of
the License under which You originally received the Covered Software, or
under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a modified
version of this License if you rename the license and remove any
references to the name of the license steward (except to note that such
modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a relevant
directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - “Incompatible With Secondary Licenses” Notice
This Source Code Form is “Incompatible
With Secondary Licenses”, as defined by
the Mozilla Public License, v. 2.0.

97
vendor/github.com/hashicorp/go-multierror/README.md generated vendored Normal file
View File

@ -0,0 +1,97 @@
# go-multierror
[![Build Status](http://img.shields.io/travis/hashicorp/go-multierror.svg?style=flat-square)][travis]
[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]
[travis]: https://travis-ci.org/hashicorp/go-multierror
[godocs]: https://godoc.org/github.com/hashicorp/go-multierror
`go-multierror` is a package for Go that provides a mechanism for
representing a list of `error` values as a single `error`.
This allows a function in Go to return an `error` that might actually
be a list of errors. If the caller knows this, they can unwrap the
list and access the errors. If the caller doesn't know, the error
formats to a nice human-readable format.
`go-multierror` implements the
[errwrap](https://github.com/hashicorp/errwrap) interface so that it can
be used with that library, as well.
## Installation and Docs
Install using `go get github.com/hashicorp/go-multierror`.
Full documentation is available at
http://godoc.org/github.com/hashicorp/go-multierror
## Usage
go-multierror is easy to use and purposely built to be unobtrusive in
existing Go applications/libraries that may not be aware of it.
**Building a list of errors**
The `Append` function is used to create a list of errors. This function
behaves a lot like the Go built-in `append` function: it doesn't matter
if the first argument is nil, a `multierror.Error`, or any other `error`,
the function behaves as you would expect.
```go
var result error
if err := step1(); err != nil {
result = multierror.Append(result, err)
}
if err := step2(); err != nil {
result = multierror.Append(result, err)
}
return result
```
**Customizing the formatting of the errors**
By specifying a custom `ErrorFormat`, you can customize the format
of the `Error() string` function:
```go
var result *multierror.Error
// ... accumulate errors here, maybe using Append
if result != nil {
result.ErrorFormat = func([]error) string {
return "errors!"
}
}
```
**Accessing the list of errors**
`multierror.Error` implements `error` so if the caller doesn't know about
multierror, it will work just fine. But if you're aware a multierror might
be returned, you can use type switches to access the list of errors:
```go
if err := something(); err != nil {
if merr, ok := err.(*multierror.Error); ok {
// Use merr.Errors
}
}
```
**Returning a multierror only if there are errors**
If you build a `multierror.Error`, you can use the `ErrorOrNil` function
to return an `error` implementation only if there are errors to return:
```go
var result *multierror.Error
// ... accumulate errors here
// Return the `error` only if errors were added to the multierror, otherwise
// return nil since there are no errors.
return result.ErrorOrNil()
```

41
vendor/github.com/hashicorp/go-multierror/append.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
package multierror
// Append is a helper function that will append more errors
// onto an Error in order to create a larger multi-error.
//
// If err is not a multierror.Error, then it will be turned into
// one. If any of the errs are multierr.Error, they will be flattened
// one level into err.
func Append(err error, errs ...error) *Error {
switch err := err.(type) {
case *Error:
// Typed nils can reach here, so initialize if we are nil
if err == nil {
err = new(Error)
}
// Go through each error and flatten
for _, e := range errs {
switch e := e.(type) {
case *Error:
if e != nil {
err.Errors = append(err.Errors, e.Errors...)
}
default:
if e != nil {
err.Errors = append(err.Errors, e)
}
}
}
return err
default:
newErrs := make([]error, 0, len(errs)+1)
if err != nil {
newErrs = append(newErrs, err)
}
newErrs = append(newErrs, errs...)
return Append(&Error{}, newErrs...)
}
}

26
vendor/github.com/hashicorp/go-multierror/flatten.go generated vendored Normal file
View File

@ -0,0 +1,26 @@
package multierror
// Flatten flattens the given error, merging any *Errors together into
// a single *Error.
func Flatten(err error) error {
// If it isn't an *Error, just return the error as-is
if _, ok := err.(*Error); !ok {
return err
}
// Otherwise, make the result and flatten away!
flatErr := new(Error)
flatten(err, flatErr)
return flatErr
}
func flatten(err error, flatErr *Error) {
switch err := err.(type) {
case *Error:
for _, e := range err.Errors {
flatten(e, flatErr)
}
default:
flatErr.Errors = append(flatErr.Errors, err)
}
}

27
vendor/github.com/hashicorp/go-multierror/format.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package multierror
import (
"fmt"
"strings"
)
// ErrorFormatFunc is a function callback that is called by Error to
// turn the list of errors into a string.
type ErrorFormatFunc func([]error) string
// ListFormatFunc is a basic formatter that outputs the number of errors
// that occurred along with a bullet point list of the errors.
func ListFormatFunc(es []error) string {
if len(es) == 1 {
return fmt.Sprintf("1 error occurred:\n\n* %s", es[0])
}
points := make([]string, len(es))
for i, err := range es {
points[i] = fmt.Sprintf("* %s", err)
}
return fmt.Sprintf(
"%d errors occurred:\n\n%s",
len(es), strings.Join(points, "\n"))
}

View File

@ -0,0 +1,51 @@
package multierror
import (
"fmt"
)
// Error is an error type to track multiple errors. This is used to
// accumulate errors in cases and return them as a single "error".
type Error struct {
Errors []error
ErrorFormat ErrorFormatFunc
}
func (e *Error) Error() string {
fn := e.ErrorFormat
if fn == nil {
fn = ListFormatFunc
}
return fn(e.Errors)
}
// ErrorOrNil returns an error interface if this Error represents
// a list of errors, or returns nil if the list of errors is empty. This
// function is useful at the end of accumulation to make sure that the value
// returned represents the existence of errors.
func (e *Error) ErrorOrNil() error {
if e == nil {
return nil
}
if len(e.Errors) == 0 {
return nil
}
return e
}
func (e *Error) GoString() string {
return fmt.Sprintf("*%#v", *e)
}
// WrappedErrors returns the list of errors that this Error is wrapping.
// It is an implementatin of the errwrap.Wrapper interface so that
// multierror.Error can be used with that library.
//
// This method is not safe to be called concurrently and is no different
// than accessing the Errors field directly. It is implementd only to
// satisfy the errwrap.Wrapper interface.
func (e *Error) WrappedErrors() []error {
return e.Errors
}

37
vendor/github.com/hashicorp/go-multierror/prefix.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
package multierror
import (
"fmt"
"github.com/hashicorp/errwrap"
)
// Prefix is a helper function that will prefix some text
// to the given error. If the error is a multierror.Error, then
// it will be prefixed to each wrapped error.
//
// This is useful to use when appending multiple multierrors
// together in order to give better scoping.
func Prefix(err error, prefix string) error {
if err == nil {
return nil
}
format := fmt.Sprintf("%s {{err}}", prefix)
switch err := err.(type) {
case *Error:
// Typed nils can reach here, so initialize if we are nil
if err == nil {
err = new(Error)
}
// Wrap each of the errors
for i, e := range err.Errors {
err.Errors[i] = errwrap.Wrapf(format, e)
}
return err
default:
return errwrap.Wrapf(format, err)
}
}

View File

@ -0,0 +1,92 @@
// Package error implements generic tooling for tracking RFC 2119
// violations and linking back to the appropriate specification section.
package error
import (
"fmt"
"strings"
)
// Level represents the RFC 2119 compliance levels
type Level int
const (
// MAY-level
// May represents 'MAY' in RFC 2119.
May Level = iota
// Optional represents 'OPTIONAL' in RFC 2119.
Optional
// SHOULD-level
// Should represents 'SHOULD' in RFC 2119.
Should
// ShouldNot represents 'SHOULD NOT' in RFC 2119.
ShouldNot
// Recommended represents 'RECOMMENDED' in RFC 2119.
Recommended
// NotRecommended represents 'NOT RECOMMENDED' in RFC 2119.
NotRecommended
// MUST-level
// Must represents 'MUST' in RFC 2119
Must
// MustNot represents 'MUST NOT' in RFC 2119.
MustNot
// Shall represents 'SHALL' in RFC 2119.
Shall
// ShallNot represents 'SHALL NOT' in RFC 2119.
ShallNot
// Required represents 'REQUIRED' in RFC 2119.
Required
)
// Error represents an error with compliance level and specification reference.
type Error struct {
// Level represents the RFC 2119 compliance level.
Level Level
// Reference is a URL for the violated specification requirement.
Reference string
// Err holds additional details about the violation.
Err error
}
// ParseLevel takes a string level and returns the RFC 2119 compliance level constant.
func ParseLevel(level string) (Level, error) {
switch strings.ToUpper(level) {
case "MAY":
fallthrough
case "OPTIONAL":
return May, nil
case "SHOULD":
fallthrough
case "SHOULDNOT":
fallthrough
case "RECOMMENDED":
fallthrough
case "NOTRECOMMENDED":
return Should, nil
case "MUST":
fallthrough
case "MUSTNOT":
fallthrough
case "SHALL":
fallthrough
case "SHALLNOT":
fallthrough
case "REQUIRED":
return Must, nil
}
var l Level
return l, fmt.Errorf("%q is not a valid compliance level", level)
}
// Error returns the error message with specification reference.
func (err *Error) Error() string {
return fmt.Sprintf("%s\nRefer to: %s", err.Err.Error(), err.Reference)
}

View File

@ -341,6 +341,13 @@ func (g *Generator) RemoveAnnotation(key string) {
delete(g.spec.Annotations, key) delete(g.spec.Annotations, key)
} }
// SetProcessConsoleSize sets g.spec.Process.ConsoleSize.
func (g *Generator) SetProcessConsoleSize(width, height uint) {
g.initSpecProcessConsoleSize()
g.spec.Process.ConsoleSize.Width = width
g.spec.Process.ConsoleSize.Height = height
}
// SetProcessUID sets g.spec.Process.User.UID. // SetProcessUID sets g.spec.Process.User.UID.
func (g *Generator) SetProcessUID(uid uint32) { func (g *Generator) SetProcessUID(uid uint32) {
g.initSpecProcess() g.initSpecProcess()
@ -737,6 +744,38 @@ func (g *Generator) ClearPreStartHooks() {
func (g *Generator) AddPreStartHook(path string, args []string) { func (g *Generator) AddPreStartHook(path string, args []string) {
g.initSpecHooks() g.initSpecHooks()
hook := rspec.Hook{Path: path, Args: args} hook := rspec.Hook{Path: path, Args: args}
for i, hook := range g.spec.Hooks.Prestart {
if hook.Path == path {
g.spec.Hooks.Prestart[i] = hook
return
}
}
g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook)
}
// AddPreStartHookEnv adds envs of a prestart hook into g.spec.Hooks.Prestart.
func (g *Generator) AddPreStartHookEnv(path string, envs []string) {
g.initSpecHooks()
for i, hook := range g.spec.Hooks.Prestart {
if hook.Path == path {
g.spec.Hooks.Prestart[i].Env = envs
return
}
}
hook := rspec.Hook{Path: path, Env: envs}
g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook)
}
// AddPreStartHookTimeout adds timeout of a prestart hook into g.spec.Hooks.Prestart.
func (g *Generator) AddPreStartHookTimeout(path string, timeout int) {
g.initSpecHooks()
for i, hook := range g.spec.Hooks.Prestart {
if hook.Path == path {
g.spec.Hooks.Prestart[i].Timeout = &timeout
return
}
}
hook := rspec.Hook{Path: path, Timeout: &timeout}
g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook) g.spec.Hooks.Prestart = append(g.spec.Hooks.Prestart, hook)
} }
@ -755,6 +794,38 @@ func (g *Generator) ClearPostStopHooks() {
func (g *Generator) AddPostStopHook(path string, args []string) { func (g *Generator) AddPostStopHook(path string, args []string) {
g.initSpecHooks() g.initSpecHooks()
hook := rspec.Hook{Path: path, Args: args} hook := rspec.Hook{Path: path, Args: args}
for i, hook := range g.spec.Hooks.Poststop {
if hook.Path == path {
g.spec.Hooks.Poststop[i] = hook
return
}
}
g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook)
}
// AddPostStopHookEnv adds envs of a poststop hook into g.spec.Hooks.Poststop.
func (g *Generator) AddPostStopHookEnv(path string, envs []string) {
g.initSpecHooks()
for i, hook := range g.spec.Hooks.Poststop {
if hook.Path == path {
g.spec.Hooks.Poststop[i].Env = envs
return
}
}
hook := rspec.Hook{Path: path, Env: envs}
g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook)
}
// AddPostStopHookTimeout adds timeout of a poststop hook into g.spec.Hooks.Poststop.
func (g *Generator) AddPostStopHookTimeout(path string, timeout int) {
g.initSpecHooks()
for i, hook := range g.spec.Hooks.Poststop {
if hook.Path == path {
g.spec.Hooks.Poststop[i].Timeout = &timeout
return
}
}
hook := rspec.Hook{Path: path, Timeout: &timeout}
g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook) g.spec.Hooks.Poststop = append(g.spec.Hooks.Poststop, hook)
} }
@ -773,6 +844,38 @@ func (g *Generator) ClearPostStartHooks() {
func (g *Generator) AddPostStartHook(path string, args []string) { func (g *Generator) AddPostStartHook(path string, args []string) {
g.initSpecHooks() g.initSpecHooks()
hook := rspec.Hook{Path: path, Args: args} hook := rspec.Hook{Path: path, Args: args}
for i, hook := range g.spec.Hooks.Poststart {
if hook.Path == path {
g.spec.Hooks.Poststart[i] = hook
return
}
}
g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook)
}
// AddPostStartHookEnv adds envs of a poststart hook into g.spec.Hooks.Poststart.
func (g *Generator) AddPostStartHookEnv(path string, envs []string) {
g.initSpecHooks()
for i, hook := range g.spec.Hooks.Poststart {
if hook.Path == path {
g.spec.Hooks.Poststart[i].Env = envs
return
}
}
hook := rspec.Hook{Path: path, Env: envs}
g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook)
}
// AddPostStartHookTimeout adds timeout of a poststart hook into g.spec.Hooks.Poststart.
func (g *Generator) AddPostStartHookTimeout(path string, timeout int) {
g.initSpecHooks()
for i, hook := range g.spec.Hooks.Poststart {
if hook.Path == path {
g.spec.Hooks.Poststart[i].Timeout = &timeout
return
}
}
hook := rspec.Hook{Path: path, Timeout: &timeout}
g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook) g.spec.Hooks.Poststart = append(g.spec.Hooks.Poststart, hook)
} }
@ -853,11 +956,12 @@ func (g *Generator) SetupPrivileged(privileged bool) {
} }
g.initSpecLinux() g.initSpecLinux()
g.initSpecProcessCapabilities() g.initSpecProcessCapabilities()
g.spec.Process.Capabilities.Bounding = finalCapList g.ClearProcessCapabilities()
g.spec.Process.Capabilities.Effective = finalCapList g.spec.Process.Capabilities.Bounding = append(g.spec.Process.Capabilities.Bounding, finalCapList...)
g.spec.Process.Capabilities.Inheritable = finalCapList g.spec.Process.Capabilities.Effective = append(g.spec.Process.Capabilities.Effective, finalCapList...)
g.spec.Process.Capabilities.Permitted = finalCapList g.spec.Process.Capabilities.Inheritable = append(g.spec.Process.Capabilities.Inheritable, finalCapList...)
g.spec.Process.Capabilities.Ambient = finalCapList g.spec.Process.Capabilities.Permitted = append(g.spec.Process.Capabilities.Permitted, finalCapList...)
g.spec.Process.Capabilities.Ambient = append(g.spec.Process.Capabilities.Ambient, finalCapList...)
g.spec.Process.SelinuxLabel = "" g.spec.Process.SelinuxLabel = ""
g.spec.Process.ApparmorProfile = "" g.spec.Process.ApparmorProfile = ""
g.spec.Linux.Seccomp = nil g.spec.Linux.Seccomp = nil
@ -885,40 +989,60 @@ func (g *Generator) AddProcessCapability(c string) error {
g.initSpecProcessCapabilities() g.initSpecProcessCapabilities()
var foundBounding bool
for _, cap := range g.spec.Process.Capabilities.Bounding { for _, cap := range g.spec.Process.Capabilities.Bounding {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
return nil foundBounding = true
break
} }
} }
if !foundBounding {
g.spec.Process.Capabilities.Bounding = append(g.spec.Process.Capabilities.Bounding, cp) g.spec.Process.Capabilities.Bounding = append(g.spec.Process.Capabilities.Bounding, cp)
}
var foundEffective bool
for _, cap := range g.spec.Process.Capabilities.Effective { for _, cap := range g.spec.Process.Capabilities.Effective {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
return nil foundEffective = true
break
} }
} }
if !foundEffective {
g.spec.Process.Capabilities.Effective = append(g.spec.Process.Capabilities.Effective, cp) g.spec.Process.Capabilities.Effective = append(g.spec.Process.Capabilities.Effective, cp)
}
var foundInheritable bool
for _, cap := range g.spec.Process.Capabilities.Inheritable { for _, cap := range g.spec.Process.Capabilities.Inheritable {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
return nil foundInheritable = true
break
} }
} }
if !foundInheritable {
g.spec.Process.Capabilities.Inheritable = append(g.spec.Process.Capabilities.Inheritable, cp) g.spec.Process.Capabilities.Inheritable = append(g.spec.Process.Capabilities.Inheritable, cp)
}
var foundPermitted bool
for _, cap := range g.spec.Process.Capabilities.Permitted { for _, cap := range g.spec.Process.Capabilities.Permitted {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
return nil foundPermitted = true
break
} }
} }
if !foundPermitted {
g.spec.Process.Capabilities.Permitted = append(g.spec.Process.Capabilities.Permitted, cp) g.spec.Process.Capabilities.Permitted = append(g.spec.Process.Capabilities.Permitted, cp)
}
var foundAmbient bool
for _, cap := range g.spec.Process.Capabilities.Ambient { for _, cap := range g.spec.Process.Capabilities.Ambient {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
return nil foundAmbient = true
break
} }
} }
if !foundAmbient {
g.spec.Process.Capabilities.Ambient = append(g.spec.Process.Capabilities.Ambient, cp) g.spec.Process.Capabilities.Ambient = append(g.spec.Process.Capabilities.Ambient, cp)
}
return nil return nil
} }
@ -926,42 +1050,49 @@ func (g *Generator) AddProcessCapability(c string) error {
// DropProcessCapability drops a process capability from g.spec.Process.Capabilities. // DropProcessCapability drops a process capability from g.spec.Process.Capabilities.
func (g *Generator) DropProcessCapability(c string) error { func (g *Generator) DropProcessCapability(c string) error {
cp := strings.ToUpper(c) cp := strings.ToUpper(c)
if err := validate.CapValid(cp, g.HostSpecific); err != nil {
return err
}
g.initSpecProcessCapabilities() g.initSpecProcessCapabilities()
// we don't care about order...and this is way faster...
removeFunc := func(s []string, i int) []string {
s[i] = s[len(s)-1]
return s[:len(s)-1]
}
for i, cap := range g.spec.Process.Capabilities.Bounding { for i, cap := range g.spec.Process.Capabilities.Bounding {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
g.spec.Process.Capabilities.Bounding = append(g.spec.Process.Capabilities.Bounding[:i], g.spec.Process.Capabilities.Bounding[i+1:]...) g.spec.Process.Capabilities.Bounding = removeFunc(g.spec.Process.Capabilities.Bounding, i)
} }
} }
for i, cap := range g.spec.Process.Capabilities.Effective { for i, cap := range g.spec.Process.Capabilities.Effective {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
g.spec.Process.Capabilities.Effective = append(g.spec.Process.Capabilities.Effective[:i], g.spec.Process.Capabilities.Effective[i+1:]...) g.spec.Process.Capabilities.Effective = removeFunc(g.spec.Process.Capabilities.Effective, i)
} }
} }
for i, cap := range g.spec.Process.Capabilities.Inheritable { for i, cap := range g.spec.Process.Capabilities.Inheritable {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
g.spec.Process.Capabilities.Inheritable = append(g.spec.Process.Capabilities.Inheritable[:i], g.spec.Process.Capabilities.Inheritable[i+1:]...) g.spec.Process.Capabilities.Inheritable = removeFunc(g.spec.Process.Capabilities.Inheritable, i)
} }
} }
for i, cap := range g.spec.Process.Capabilities.Permitted { for i, cap := range g.spec.Process.Capabilities.Permitted {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
g.spec.Process.Capabilities.Permitted = append(g.spec.Process.Capabilities.Permitted[:i], g.spec.Process.Capabilities.Permitted[i+1:]...) g.spec.Process.Capabilities.Permitted = removeFunc(g.spec.Process.Capabilities.Permitted, i)
} }
} }
for i, cap := range g.spec.Process.Capabilities.Ambient { for i, cap := range g.spec.Process.Capabilities.Ambient {
if strings.ToUpper(cap) == cp { if strings.ToUpper(cap) == cp {
g.spec.Process.Capabilities.Ambient = append(g.spec.Process.Capabilities.Ambient[:i], g.spec.Process.Capabilities.Ambient[i+1:]...) g.spec.Process.Capabilities.Ambient = removeFunc(g.spec.Process.Capabilities.Ambient, i)
} }
} }
if err := validate.CapValid(cp, false); err != nil {
return err
}
return nil return nil
} }

View File

@ -17,6 +17,13 @@ func (g *Generator) initSpecProcess() {
} }
} }
func (g *Generator) initSpecProcessConsoleSize() {
g.initSpecProcess()
if g.spec.Process.ConsoleSize == nil {
g.spec.Process.ConsoleSize = &rspec.Box{}
}
}
func (g *Generator) initSpecProcessCapabilities() { func (g *Generator) initSpecProcessCapabilities() {
g.initSpecProcess() g.initSpecProcess()
if g.spec.Process.Capabilities == nil { if g.spec.Process.Capabilities == nil {

View File

@ -0,0 +1,170 @@
// Package specerror implements runtime-spec-specific tooling for
// tracking RFC 2119 violations.
package specerror
import (
"fmt"
"github.com/hashicorp/go-multierror"
rfc2119 "github.com/opencontainers/runtime-tools/error"
)
const referenceTemplate = "https://github.com/opencontainers/runtime-spec/blob/v%s/%s"
// Code represents the spec violation, enumerating both
// configuration violations and runtime violations.
type Code int
const (
// NonError represents that an input is not an error
NonError Code = iota
// NonRFCError represents that an error is not a rfc2119 error
NonRFCError
// ConfigFileExistence represents the error code of 'config.json' existence test
ConfigFileExistence
// ArtifactsInSingleDir represents the error code of artifacts place test
ArtifactsInSingleDir
// SpecVersion represents the error code of specfication version test
SpecVersion
// RootOnNonHyperV represents the error code of root setting test on non hyper-v containers
RootOnNonHyperV
// RootOnHyperV represents the error code of root setting test on hyper-v containers
RootOnHyperV
// PathFormatOnWindows represents the error code of the path format test on Window
PathFormatOnWindows
// PathName represents the error code of the path name test
PathName
// PathExistence represents the error code of the path existence test
PathExistence
// ReadonlyFilesystem represents the error code of readonly test
ReadonlyFilesystem
// ReadonlyOnWindows represents the error code of readonly setting test on Windows
ReadonlyOnWindows
// DefaultFilesystems represents the error code of default filesystems test
DefaultFilesystems
// CreateWithID represents the error code of 'create' lifecyle test with 'id' provided
CreateWithID
// CreateWithUniqueID represents the error code of 'create' lifecyle test with unique 'id' provided
CreateWithUniqueID
// CreateNewContainer represents the error code 'create' lifecyle test that creates new container
CreateNewContainer
)
type errorTemplate struct {
Level rfc2119.Level
Reference func(version string) (reference string, err error)
}
// Error represents a runtime-spec violation.
type Error struct {
// Err holds the RFC 2119 violation.
Err rfc2119.Error
// Code is a matchable holds a Code
Code Code
}
var (
containerFormatRef = func(version string) (reference string, err error) {
return fmt.Sprintf(referenceTemplate, version, "bundle.md#container-format"), nil
}
specVersionRef = func(version string) (reference string, err error) {
return fmt.Sprintf(referenceTemplate, version, "config.md#specification-version"), nil
}
rootRef = func(version string) (reference string, err error) {
return fmt.Sprintf(referenceTemplate, version, "config.md#root"), nil
}
defaultFSRef = func(version string) (reference string, err error) {
return fmt.Sprintf(referenceTemplate, version, "config-linux.md#default-filesystems"), nil
}
runtimeCreateRef = func(version string) (reference string, err error) {
return fmt.Sprintf(referenceTemplate, version, "runtime.md#create"), nil
}
)
var ociErrors = map[Code]errorTemplate{
// Bundle.md
// Container Format
ConfigFileExistence: {Level: rfc2119.Must, Reference: containerFormatRef},
ArtifactsInSingleDir: {Level: rfc2119.Must, Reference: containerFormatRef},
// Config.md
// Specification Version
SpecVersion: {Level: rfc2119.Must, Reference: specVersionRef},
// Root
RootOnNonHyperV: {Level: rfc2119.Required, Reference: rootRef},
RootOnHyperV: {Level: rfc2119.Must, Reference: rootRef},
// TODO: add tests for 'PathFormatOnWindows'
PathFormatOnWindows: {Level: rfc2119.Must, Reference: rootRef},
PathName: {Level: rfc2119.Should, Reference: rootRef},
PathExistence: {Level: rfc2119.Must, Reference: rootRef},
ReadonlyFilesystem: {Level: rfc2119.Must, Reference: rootRef},
ReadonlyOnWindows: {Level: rfc2119.Must, Reference: rootRef},
// Config-Linux.md
// Default Filesystems
DefaultFilesystems: {Level: rfc2119.Should, Reference: defaultFSRef},
// Runtime.md
// Create
CreateWithID: {Level: rfc2119.Must, Reference: runtimeCreateRef},
CreateWithUniqueID: {Level: rfc2119.Must, Reference: runtimeCreateRef},
CreateNewContainer: {Level: rfc2119.Must, Reference: runtimeCreateRef},
}
// Error returns the error message with specification reference.
func (err *Error) Error() string {
return err.Err.Error()
}
// NewError creates an Error referencing a spec violation. The error
// can be cast to an *Error for extracting structured information
// about the level of the violation and a reference to the violated
// spec condition.
//
// A version string (for the version of the spec that was violated)
// must be set to get a working URL.
func NewError(code Code, err error, version string) error {
template := ociErrors[code]
reference, err2 := template.Reference(version)
if err2 != nil {
return err2
}
return &Error{
Err: rfc2119.Error{
Level: template.Level,
Reference: reference,
Err: err,
},
Code: code,
}
}
// FindError finds an error from a source error (multiple error) and
// returns the error code if found.
// If the source error is nil or empty, return NonError.
// If the source error is not a multiple error, return NonRFCError.
func FindError(err error, code Code) Code {
if err == nil {
return NonError
}
if merr, ok := err.(*multierror.Error); ok {
if merr.ErrorOrNil() == nil {
return NonError
}
for _, e := range merr.Errors {
if rfcErr, ok := e.(*Error); ok {
if rfcErr.Code == code {
return code
}
}
}
}
return NonRFCError
}

View File

@ -1,110 +0,0 @@
package validate
import (
"errors"
"fmt"
"strings"
rspec "github.com/opencontainers/runtime-spec/specs-go"
)
// ComplianceLevel represents the OCI compliance levels
type ComplianceLevel int
const (
// MAY-level
// ComplianceMay represents 'MAY' in RFC2119
ComplianceMay ComplianceLevel = iota
// ComplianceOptional represents 'OPTIONAL' in RFC2119
ComplianceOptional
// SHOULD-level
// ComplianceShould represents 'SHOULD' in RFC2119
ComplianceShould
// ComplianceShouldNot represents 'SHOULD NOT' in RFC2119
ComplianceShouldNot
// ComplianceRecommended represents 'RECOMMENDED' in RFC2119
ComplianceRecommended
// ComplianceNotRecommended represents 'NOT RECOMMENDED' in RFC2119
ComplianceNotRecommended
// MUST-level
// ComplianceMust represents 'MUST' in RFC2119
ComplianceMust
// ComplianceMustNot represents 'MUST NOT' in RFC2119
ComplianceMustNot
// ComplianceShall represents 'SHALL' in RFC2119
ComplianceShall
// ComplianceShallNot represents 'SHALL NOT' in RFC2119
ComplianceShallNot
// ComplianceRequired represents 'REQUIRED' in RFC2119
ComplianceRequired
)
// ErrorCode represents the compliance content
type ErrorCode int
const (
// DefaultFilesystems represents the error code of default filesystems test
DefaultFilesystems ErrorCode = iota
)
// Error represents an error with compliance level and OCI reference
type Error struct {
Level ComplianceLevel
Reference string
Err error
}
const referencePrefix = "https://github.com/opencontainers/runtime-spec/blob"
var ociErrors = map[ErrorCode]Error{
DefaultFilesystems: Error{Level: ComplianceShould, Reference: "config-linux.md#default-filesystems"},
}
// ParseLevel takes a string level and returns the OCI compliance level constant
func ParseLevel(level string) (ComplianceLevel, error) {
switch strings.ToUpper(level) {
case "MAY":
fallthrough
case "OPTIONAL":
return ComplianceMay, nil
case "SHOULD":
fallthrough
case "SHOULDNOT":
fallthrough
case "RECOMMENDED":
fallthrough
case "NOTRECOMMENDED":
return ComplianceShould, nil
case "MUST":
fallthrough
case "MUSTNOT":
fallthrough
case "SHALL":
fallthrough
case "SHALLNOT":
fallthrough
case "REQUIRED":
return ComplianceMust, nil
}
var l ComplianceLevel
return l, fmt.Errorf("%q is not a valid compliance level", level)
}
// NewError creates an Error by ErrorCode and message
func NewError(code ErrorCode, msg string) error {
err := ociErrors[code]
err.Err = errors.New(msg)
return &err
}
// Error returns the error message with OCI reference
func (oci *Error) Error() string {
return fmt.Sprintf("%s\nRefer to: %s/v%s/%s", oci.Err.Error(), referencePrefix, rspec.Version, oci.Reference)
}

View File

@ -3,21 +3,27 @@ package validate
import ( import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp"
"runtime" "runtime"
"strings" "strings"
"syscall"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
"github.com/blang/semver" "github.com/blang/semver"
"github.com/hashicorp/go-multierror"
rspec "github.com/opencontainers/runtime-spec/specs-go" rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/syndtr/gocapability/capability" "github.com/syndtr/gocapability/capability"
"github.com/opencontainers/runtime-tools/specerror"
) )
const specConfig = "config.json" const specConfig = "config.json"
@ -70,7 +76,7 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string)
platform = runtime.GOOS platform = runtime.GOOS
} }
if bundlePath == "" { if bundlePath == "" {
return Validator{}, fmt.Errorf("Bundle path shouldn't be empty") return Validator{}, fmt.Errorf("bundle path shouldn't be empty")
} }
if _, err := os.Stat(bundlePath); err != nil { if _, err := os.Stat(bundlePath); err != nil {
@ -80,7 +86,7 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string)
configPath := filepath.Join(bundlePath, specConfig) configPath := filepath.Join(bundlePath, specConfig)
content, err := ioutil.ReadFile(configPath) content, err := ioutil.ReadFile(configPath)
if err != nil { if err != nil {
return Validator{}, err return Validator{}, specerror.NewError(specerror.ConfigFileExistence, err, rspec.Version)
} }
if !utf8.Valid(content) { if !utf8.Valid(content) {
return Validator{}, fmt.Errorf("%q is not encoded in UTF-8", configPath) return Validator{}, fmt.Errorf("%q is not encoded in UTF-8", configPath)
@ -94,28 +100,46 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string)
} }
// CheckAll checks all parts of runtime bundle // CheckAll checks all parts of runtime bundle
func (v *Validator) CheckAll() (msgs []string) { func (v *Validator) CheckAll() error {
msgs = append(msgs, v.CheckPlatform()...) var errs *multierror.Error
msgs = append(msgs, v.CheckRootfsPath()...) errs = multierror.Append(errs, v.CheckPlatform())
msgs = append(msgs, v.CheckMandatoryFields()...) errs = multierror.Append(errs, v.CheckRoot())
msgs = append(msgs, v.CheckSemVer()...) errs = multierror.Append(errs, v.CheckMandatoryFields())
msgs = append(msgs, v.CheckMounts()...) errs = multierror.Append(errs, v.CheckSemVer())
msgs = append(msgs, v.CheckProcess()...) errs = multierror.Append(errs, v.CheckMounts())
msgs = append(msgs, v.CheckHooks()...) errs = multierror.Append(errs, v.CheckProcess())
if v.spec.Linux != nil { errs = multierror.Append(errs, v.CheckHooks())
msgs = append(msgs, v.CheckLinux()...) errs = multierror.Append(errs, v.CheckLinux())
}
return return errs.ErrorOrNil()
} }
// CheckRootfsPath checks status of v.spec.Root.Path // CheckRoot checks status of v.spec.Root
func (v *Validator) CheckRootfsPath() (msgs []string) { func (v *Validator) CheckRoot() (errs error) {
logrus.Debugf("check rootfs path") logrus.Debugf("check root")
if v.platform == "windows" && v.spec.Windows != nil && v.spec.Windows.HyperV != nil {
if v.spec.Root != nil {
errs = multierror.Append(errs,
specerror.NewError(specerror.RootOnHyperV, fmt.Errorf("for Hyper-V containers, Root must not be set"), rspec.Version))
return
}
return
} else if v.spec.Root == nil {
errs = multierror.Append(errs,
specerror.NewError(specerror.RootOnNonHyperV, fmt.Errorf("for non-Hyper-V containers, Root must be set"), rspec.Version))
return
}
absBundlePath, err := filepath.Abs(v.bundlePath) absBundlePath, err := filepath.Abs(v.bundlePath)
if err != nil { if err != nil {
msgs = append(msgs, fmt.Sprintf("unable to convert %q to an absolute path", v.bundlePath)) errs = multierror.Append(errs, fmt.Errorf("unable to convert %q to an absolute path", v.bundlePath))
return
}
if filepath.Base(v.spec.Root.Path) != "rootfs" {
errs = multierror.Append(errs,
specerror.NewError(specerror.PathName, fmt.Errorf("path name should be the conventional 'rootfs'"), rspec.Version))
} }
var rootfsPath string var rootfsPath string
@ -128,24 +152,29 @@ func (v *Validator) CheckRootfsPath() (msgs []string) {
rootfsPath = filepath.Join(v.bundlePath, v.spec.Root.Path) rootfsPath = filepath.Join(v.bundlePath, v.spec.Root.Path)
absRootPath, err = filepath.Abs(rootfsPath) absRootPath, err = filepath.Abs(rootfsPath)
if err != nil { if err != nil {
msgs = append(msgs, fmt.Sprintf("unable to convert %q to an absolute path", rootfsPath)) errs = multierror.Append(errs, fmt.Errorf("unable to convert %q to an absolute path", rootfsPath))
return
} }
} }
if fi, err := os.Stat(rootfsPath); err != nil { if fi, err := os.Stat(rootfsPath); err != nil {
msgs = append(msgs, fmt.Sprintf("Cannot find the root path %q", rootfsPath)) errs = multierror.Append(errs,
specerror.NewError(specerror.PathExistence, fmt.Errorf("cannot find the root path %q", rootfsPath), rspec.Version))
} else if !fi.IsDir() { } else if !fi.IsDir() {
msgs = append(msgs, fmt.Sprintf("The root path %q is not a directory.", rootfsPath)) errs = multierror.Append(errs,
specerror.NewError(specerror.PathExistence, fmt.Errorf("root.path %q is not a directory", rootfsPath), rspec.Version))
} }
rootParent := filepath.Dir(absRootPath) rootParent := filepath.Dir(absRootPath)
if absRootPath == string(filepath.Separator) || rootParent != absBundlePath { if absRootPath == string(filepath.Separator) || rootParent != absBundlePath {
msgs = append(msgs, fmt.Sprintf("root.path is %q, but it MUST be a child of %q", v.spec.Root.Path, absBundlePath)) errs = multierror.Append(errs,
specerror.NewError(specerror.ArtifactsInSingleDir, fmt.Errorf("root.path is %q, but it MUST be a child of %q", v.spec.Root.Path, absBundlePath), rspec.Version))
} }
if v.platform == "windows" { if v.platform == "windows" {
if v.spec.Root.Readonly { if v.spec.Root.Readonly {
msgs = append(msgs, "root.readonly field MUST be omitted or false when target platform is windows") errs = multierror.Append(errs,
specerror.NewError(specerror.ReadonlyOnWindows, fmt.Errorf("root.readonly field MUST be omitted or false when target platform is windows"), rspec.Version))
} }
} }
@ -153,53 +182,54 @@ func (v *Validator) CheckRootfsPath() (msgs []string) {
} }
// CheckSemVer checks v.spec.Version // CheckSemVer checks v.spec.Version
func (v *Validator) CheckSemVer() (msgs []string) { func (v *Validator) CheckSemVer() (errs error) {
logrus.Debugf("check semver") logrus.Debugf("check semver")
version := v.spec.Version version := v.spec.Version
_, err := semver.Parse(version) _, err := semver.Parse(version)
if err != nil { if err != nil {
msgs = append(msgs, fmt.Sprintf("%q is not valid SemVer: %s", version, err.Error())) errs = multierror.Append(errs,
specerror.NewError(specerror.SpecVersion, fmt.Errorf("%q is not valid SemVer: %s", version, err.Error()), rspec.Version))
} }
if version != rspec.Version { if version != rspec.Version {
msgs = append(msgs, fmt.Sprintf("internal error: validate currently only handles version %s, but the supplied configuration targets %s", rspec.Version, version)) errs = multierror.Append(errs, fmt.Errorf("validate currently only handles version %s, but the supplied configuration targets %s", rspec.Version, version))
} }
return return
} }
// CheckHooks check v.spec.Hooks // CheckHooks check v.spec.Hooks
func (v *Validator) CheckHooks() (msgs []string) { func (v *Validator) CheckHooks() (errs error) {
logrus.Debugf("check hooks") logrus.Debugf("check hooks")
if v.spec.Hooks != nil { if v.spec.Hooks != nil {
msgs = append(msgs, checkEventHooks("pre-start", v.spec.Hooks.Prestart, v.HostSpecific)...) errs = multierror.Append(errs, checkEventHooks("pre-start", v.spec.Hooks.Prestart, v.HostSpecific))
msgs = append(msgs, checkEventHooks("post-start", v.spec.Hooks.Poststart, v.HostSpecific)...) errs = multierror.Append(errs, checkEventHooks("post-start", v.spec.Hooks.Poststart, v.HostSpecific))
msgs = append(msgs, checkEventHooks("post-stop", v.spec.Hooks.Poststop, v.HostSpecific)...) errs = multierror.Append(errs, checkEventHooks("post-stop", v.spec.Hooks.Poststop, v.HostSpecific))
} }
return return
} }
func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (msgs []string) { func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (errs error) {
for _, hook := range hooks { for _, hook := range hooks {
if !filepath.IsAbs(hook.Path) { if !filepath.IsAbs(hook.Path) {
msgs = append(msgs, fmt.Sprintf("The %s hook %v: is not absolute path", hookType, hook.Path)) errs = multierror.Append(errs, fmt.Errorf("the %s hook %v: is not absolute path", hookType, hook.Path))
} }
if hostSpecific { if hostSpecific {
fi, err := os.Stat(hook.Path) fi, err := os.Stat(hook.Path)
if err != nil { if err != nil {
msgs = append(msgs, fmt.Sprintf("Cannot find %s hook: %v", hookType, hook.Path)) errs = multierror.Append(errs, fmt.Errorf("cannot find %s hook: %v", hookType, hook.Path))
} }
if fi.Mode()&0111 == 0 { if fi.Mode()&0111 == 0 {
msgs = append(msgs, fmt.Sprintf("The %s hook %v: is not executable", hookType, hook.Path)) errs = multierror.Append(errs, fmt.Errorf("the %s hook %v: is not executable", hookType, hook.Path))
} }
} }
for _, env := range hook.Env { for _, env := range hook.Env {
if !envValid(env) { if !envValid(env) {
msgs = append(msgs, fmt.Sprintf("Env %q for hook %v is in the invalid form.", env, hook.Path)) errs = multierror.Append(errs, fmt.Errorf("env %q for hook %v is in the invalid form", env, hook.Path))
} }
} }
} }
@ -208,22 +238,26 @@ func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (ms
} }
// CheckProcess checks v.spec.Process // CheckProcess checks v.spec.Process
func (v *Validator) CheckProcess() (msgs []string) { func (v *Validator) CheckProcess() (errs error) {
logrus.Debugf("check process") logrus.Debugf("check process")
if v.spec.Process == nil {
return
}
process := v.spec.Process process := v.spec.Process
if !filepath.IsAbs(process.Cwd) { if !filepath.IsAbs(process.Cwd) {
msgs = append(msgs, fmt.Sprintf("cwd %q is not an absolute path", process.Cwd)) errs = multierror.Append(errs, fmt.Errorf("cwd %q is not an absolute path", process.Cwd))
} }
for _, env := range process.Env { for _, env := range process.Env {
if !envValid(env) { if !envValid(env) {
msgs = append(msgs, fmt.Sprintf("env %q should be in the form of 'key=value'. The left hand side must consist solely of letters, digits, and underscores '_'.", env)) errs = multierror.Append(errs, fmt.Errorf("env %q should be in the form of 'key=value'. The left hand side must consist solely of letters, digits, and underscores '_'", env))
} }
} }
if len(process.Args) == 0 { if len(process.Args) == 0 {
msgs = append(msgs, fmt.Sprintf("args must not be empty")) errs = multierror.Append(errs, fmt.Errorf("args must not be empty"))
} else { } else {
if filepath.IsAbs(process.Args[0]) { if filepath.IsAbs(process.Args[0]) {
var rootfsPath string var rootfsPath string
@ -237,27 +271,27 @@ func (v *Validator) CheckProcess() (msgs []string) {
if os.IsNotExist(err) { if os.IsNotExist(err) {
logrus.Warnf("executable %q is not available in rootfs currently", process.Args[0]) logrus.Warnf("executable %q is not available in rootfs currently", process.Args[0])
} else if err != nil { } else if err != nil {
msgs = append(msgs, err.Error()) errs = multierror.Append(errs, err)
} else { } else {
m := fileinfo.Mode() m := fileinfo.Mode()
if m.IsDir() || m&0111 == 0 { if m.IsDir() || m&0111 == 0 {
msgs = append(msgs, fmt.Sprintf("arg %q is not executable", process.Args[0])) errs = multierror.Append(errs, fmt.Errorf("arg %q is not executable", process.Args[0]))
} }
} }
} }
} }
if v.spec.Process.Capabilities != nil { if v.spec.Process.Capabilities != nil {
msgs = append(msgs, v.CheckCapabilities()...) errs = multierror.Append(errs, v.CheckCapabilities())
} }
msgs = append(msgs, v.CheckRlimits()...) errs = multierror.Append(errs, v.CheckRlimits())
if v.platform == "linux" { if v.platform == "linux" {
if len(process.ApparmorProfile) > 0 { if len(process.ApparmorProfile) > 0 {
profilePath := filepath.Join(v.bundlePath, v.spec.Root.Path, "/etc/apparmor.d", process.ApparmorProfile) profilePath := filepath.Join(v.bundlePath, v.spec.Root.Path, "/etc/apparmor.d", process.ApparmorProfile)
_, err := os.Stat(profilePath) _, err := os.Stat(profilePath)
if err != nil { if err != nil {
msgs = append(msgs, err.Error()) errs = multierror.Append(errs, err)
} }
} }
} }
@ -266,7 +300,7 @@ func (v *Validator) CheckProcess() (msgs []string) {
} }
// CheckCapabilities checks v.spec.Process.Capabilities // CheckCapabilities checks v.spec.Process.Capabilities
func (v *Validator) CheckCapabilities() (msgs []string) { func (v *Validator) CheckCapabilities() (errs error) {
process := v.spec.Process process := v.spec.Process
if v.platform == "linux" { if v.platform == "linux" {
var effective, permitted, inheritable, ambient bool var effective, permitted, inheritable, ambient bool
@ -290,29 +324,33 @@ func (v *Validator) CheckCapabilities() (msgs []string) {
for capability, owns := range caps { for capability, owns := range caps {
if err := CapValid(capability, v.HostSpecific); err != nil { if err := CapValid(capability, v.HostSpecific); err != nil {
msgs = append(msgs, fmt.Sprintf("capability %q is not valid, man capabilities(7)", capability)) errs = multierror.Append(errs, fmt.Errorf("capability %q is not valid, man capabilities(7)", capability))
} }
effective, permitted, ambient, inheritable = false, false, false, false effective, permitted, ambient, inheritable = false, false, false, false
for _, set := range owns { for _, set := range owns {
if set == "effective" { if set == "effective" {
effective = true effective = true
continue
} }
if set == "inheritable" { if set == "inheritable" {
inheritable = true inheritable = true
continue
} }
if set == "permitted" { if set == "permitted" {
permitted = true permitted = true
continue
} }
if set == "ambient" { if set == "ambient" {
ambient = true ambient = true
continue
} }
} }
if effective && !permitted { if effective && !permitted {
msgs = append(msgs, fmt.Sprintf("effective capability %q is not allowed, as it's not permitted", capability)) errs = multierror.Append(errs, fmt.Errorf("effective capability %q is not allowed, as it's not permitted", capability))
} }
if ambient && !(effective && inheritable) { if ambient && !(effective && inheritable) {
msgs = append(msgs, fmt.Sprintf("ambient capability %q is not allowed, as it's not permitted and inheribate", capability)) errs = multierror.Append(errs, fmt.Errorf("ambient capability %q is not allowed, as it's not permitted and inheribate", capability))
} }
} }
} else { } else {
@ -323,15 +361,15 @@ func (v *Validator) CheckCapabilities() (msgs []string) {
} }
// CheckRlimits checks v.spec.Process.Rlimits // CheckRlimits checks v.spec.Process.Rlimits
func (v *Validator) CheckRlimits() (msgs []string) { func (v *Validator) CheckRlimits() (errs error) {
process := v.spec.Process process := v.spec.Process
for index, rlimit := range process.Rlimits { for index, rlimit := range process.Rlimits {
for i := index + 1; i < len(process.Rlimits); i++ { for i := index + 1; i < len(process.Rlimits); i++ {
if process.Rlimits[index].Type == process.Rlimits[i].Type { if process.Rlimits[index].Type == process.Rlimits[i].Type {
msgs = append(msgs, fmt.Sprintf("rlimit can not contain the same type %q.", process.Rlimits[index].Type)) errs = multierror.Append(errs, fmt.Errorf("rlimit can not contain the same type %q", process.Rlimits[index].Type))
} }
} }
msgs = append(msgs, v.rlimitValid(rlimit)...) errs = multierror.Append(errs, v.rlimitValid(rlimit))
} }
return return
@ -379,24 +417,49 @@ func supportedMountTypes(OS string, hostSpecific bool) (map[string]bool, error)
} }
// CheckMounts checks v.spec.Mounts // CheckMounts checks v.spec.Mounts
func (v *Validator) CheckMounts() (msgs []string) { func (v *Validator) CheckMounts() (errs error) {
logrus.Debugf("check mounts") logrus.Debugf("check mounts")
supportedTypes, err := supportedMountTypes(v.platform, v.HostSpecific) supportedTypes, err := supportedMountTypes(v.platform, v.HostSpecific)
if err != nil { if err != nil {
msgs = append(msgs, err.Error()) errs = multierror.Append(errs, err)
return return
} }
for _, mount := range v.spec.Mounts { for i, mountA := range v.spec.Mounts {
if supportedTypes != nil { if supportedTypes != nil && !supportedTypes[mountA.Type] {
if !supportedTypes[mount.Type] { errs = multierror.Append(errs, fmt.Errorf("unsupported mount type %q", mountA.Type))
msgs = append(msgs, fmt.Sprintf("Unsupported mount type %q", mount.Type)) }
if v.platform == "windows" {
if err := pathValid(v.platform, mountA.Destination); err != nil {
errs = multierror.Append(errs, err)
}
if err := pathValid(v.platform, mountA.Source); err != nil {
errs = multierror.Append(errs, err)
}
} else {
if err := pathValid(v.platform, mountA.Destination); err != nil {
errs = multierror.Append(errs, err)
}
}
for j, mountB := range v.spec.Mounts {
if i == j {
continue
}
// whether B.Desination is nested within A.Destination
nested, err := nestedValid(v.platform, mountA.Destination, mountB.Destination)
if err != nil {
errs = multierror.Append(errs, err)
continue
}
if nested {
if v.platform == "windows" && i < j {
errs = multierror.Append(errs, fmt.Errorf("on Windows, %v nested within %v is forbidden", mountB.Destination, mountA.Destination))
}
if i > j {
logrus.Warnf("%v will be covered by %v", mountB.Destination, mountA.Destination)
} }
} }
if !filepath.IsAbs(mount.Destination) {
msgs = append(msgs, fmt.Sprintf("destination %v is not an absolute path", mount.Destination))
} }
} }
@ -404,17 +467,17 @@ func (v *Validator) CheckMounts() (msgs []string) {
} }
// CheckPlatform checks v.platform // CheckPlatform checks v.platform
func (v *Validator) CheckPlatform() (msgs []string) { func (v *Validator) CheckPlatform() (errs error) {
logrus.Debugf("check platform") logrus.Debugf("check platform")
if v.platform != "linux" && v.platform != "solaris" && v.platform != "windows" { if v.platform != "linux" && v.platform != "solaris" && v.platform != "windows" {
msgs = append(msgs, fmt.Sprintf("platform %q is not supported", v.platform)) errs = multierror.Append(errs, fmt.Errorf("platform %q is not supported", v.platform))
return return
} }
if v.platform == "windows" { if v.platform == "windows" {
if v.spec.Windows == nil { if v.spec.Windows == nil {
msgs = append(msgs, "'windows' MUST be set when platform is `windows`") errs = multierror.Append(errs, errors.New("'windows' MUST be set when platform is `windows`"))
} }
} }
@ -422,10 +485,14 @@ func (v *Validator) CheckPlatform() (msgs []string) {
} }
// CheckLinux checks v.spec.Linux // CheckLinux checks v.spec.Linux
func (v *Validator) CheckLinux() (msgs []string) { func (v *Validator) CheckLinux() (errs error) {
logrus.Debugf("check linux") logrus.Debugf("check linux")
var typeList = map[rspec.LinuxNamespaceType]struct { if v.spec.Linux == nil {
return
}
var nsTypeList = map[rspec.LinuxNamespaceType]struct {
num int num int
newExist bool newExist bool
}{ }{
@ -441,58 +508,142 @@ func (v *Validator) CheckLinux() (msgs []string) {
for index := 0; index < len(v.spec.Linux.Namespaces); index++ { for index := 0; index < len(v.spec.Linux.Namespaces); index++ {
ns := v.spec.Linux.Namespaces[index] ns := v.spec.Linux.Namespaces[index]
if !namespaceValid(ns) { if !namespaceValid(ns) {
msgs = append(msgs, fmt.Sprintf("namespace %v is invalid.", ns)) errs = multierror.Append(errs, fmt.Errorf("namespace %v is invalid", ns))
} }
tmpItem := typeList[ns.Type] tmpItem := nsTypeList[ns.Type]
tmpItem.num = tmpItem.num + 1 tmpItem.num = tmpItem.num + 1
if tmpItem.num > 1 { if tmpItem.num > 1 {
msgs = append(msgs, fmt.Sprintf("duplicated namespace %q", ns.Type)) errs = multierror.Append(errs, fmt.Errorf("duplicated namespace %q", ns.Type))
} }
if len(ns.Path) == 0 { if len(ns.Path) == 0 {
tmpItem.newExist = true tmpItem.newExist = true
} }
typeList[ns.Type] = tmpItem nsTypeList[ns.Type] = tmpItem
} }
if (len(v.spec.Linux.UIDMappings) > 0 || len(v.spec.Linux.GIDMappings) > 0) && !typeList[rspec.UserNamespace].newExist { if (len(v.spec.Linux.UIDMappings) > 0 || len(v.spec.Linux.GIDMappings) > 0) && !nsTypeList[rspec.UserNamespace].newExist {
msgs = append(msgs, "UID/GID mappings requires a new User namespace to be specified as well") errs = multierror.Append(errs, errors.New("the UID/GID mappings requires a new User namespace to be specified as well"))
} else if len(v.spec.Linux.UIDMappings) > 5 { } else if len(v.spec.Linux.UIDMappings) > 5 {
msgs = append(msgs, "Only 5 UID mappings are allowed (linux kernel restriction).") errs = multierror.Append(errs, errors.New("only 5 UID mappings are allowed (linux kernel restriction)"))
} else if len(v.spec.Linux.GIDMappings) > 5 { } else if len(v.spec.Linux.GIDMappings) > 5 {
msgs = append(msgs, "Only 5 GID mappings are allowed (linux kernel restriction).") errs = multierror.Append(errs, errors.New("only 5 GID mappings are allowed (linux kernel restriction)"))
} }
for k := range v.spec.Linux.Sysctl { for k := range v.spec.Linux.Sysctl {
if strings.HasPrefix(k, "net.") && !typeList[rspec.NetworkNamespace].newExist { if strings.HasPrefix(k, "net.") && !nsTypeList[rspec.NetworkNamespace].newExist {
msgs = append(msgs, fmt.Sprintf("Sysctl %v requires a new Network namespace to be specified as well", k)) errs = multierror.Append(errs, fmt.Errorf("sysctl %v requires a new Network namespace to be specified as well", k))
} }
if strings.HasPrefix(k, "fs.mqueue.") { if strings.HasPrefix(k, "fs.mqueue.") {
if !typeList[rspec.MountNamespace].newExist || !typeList[rspec.IPCNamespace].newExist { if !nsTypeList[rspec.MountNamespace].newExist || !nsTypeList[rspec.IPCNamespace].newExist {
msgs = append(msgs, fmt.Sprintf("Sysctl %v requires a new IPC namespace and Mount namespace to be specified as well", k)) errs = multierror.Append(errs, fmt.Errorf("sysctl %v requires a new IPC namespace and Mount namespace to be specified as well", k))
} }
} }
} }
if v.platform == "linux" && !typeList[rspec.UTSNamespace].newExist && v.spec.Hostname != "" { if v.platform == "linux" && !nsTypeList[rspec.UTSNamespace].newExist && v.spec.Hostname != "" {
msgs = append(msgs, fmt.Sprintf("On Linux, hostname requires a new UTS namespace to be specified as well")) errs = multierror.Append(errs, fmt.Errorf("on Linux, hostname requires a new UTS namespace to be specified as well"))
} }
// Linux devices validation
devList := make(map[string]bool)
devTypeList := make(map[string]bool)
for index := 0; index < len(v.spec.Linux.Devices); index++ { for index := 0; index < len(v.spec.Linux.Devices); index++ {
if !deviceValid(v.spec.Linux.Devices[index]) { device := v.spec.Linux.Devices[index]
msgs = append(msgs, fmt.Sprintf("device %v is invalid.", v.spec.Linux.Devices[index])) if !deviceValid(device) {
errs = multierror.Append(errs, fmt.Errorf("device %v is invalid", device))
}
if _, exists := devList[device.Path]; exists {
errs = multierror.Append(errs, fmt.Errorf("device %s is duplicated", device.Path))
} else {
var rootfsPath string
if filepath.IsAbs(v.spec.Root.Path) {
rootfsPath = v.spec.Root.Path
} else {
rootfsPath = filepath.Join(v.bundlePath, v.spec.Root.Path)
}
absPath := filepath.Join(rootfsPath, device.Path)
fi, err := os.Stat(absPath)
if os.IsNotExist(err) {
devList[device.Path] = true
} else if err != nil {
errs = multierror.Append(errs, err)
} else {
fStat, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
errs = multierror.Append(errs, fmt.Errorf("cannot determine state for device %s", device.Path))
continue
}
var devType string
switch fStat.Mode & syscall.S_IFMT {
case syscall.S_IFCHR:
devType = "c"
case syscall.S_IFBLK:
devType = "b"
case syscall.S_IFIFO:
devType = "p"
default:
devType = "unmatched"
}
if devType != device.Type || (devType == "c" && device.Type == "u") {
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
continue
}
if devType != "p" {
dev := fStat.Rdev
major := (dev >> 8) & 0xfff
minor := (dev & 0xff) | ((dev >> 12) & 0xfff00)
if int64(major) != device.Major || int64(minor) != device.Minor {
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
continue
}
}
if device.FileMode != nil {
expectedPerm := *device.FileMode & os.ModePerm
actualPerm := fi.Mode() & os.ModePerm
if expectedPerm != actualPerm {
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
continue
}
}
if device.UID != nil {
if *device.UID != fStat.Uid {
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
continue
}
}
if device.GID != nil {
if *device.GID != fStat.Gid {
errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
continue
}
}
}
}
// unify u->c when comparing, they are synonyms
var devID string
if device.Type == "u" {
devID = fmt.Sprintf("%s:%d:%d", "c", device.Major, device.Minor)
} else {
devID = fmt.Sprintf("%s:%d:%d", device.Type, device.Major, device.Minor)
}
if _, exists := devTypeList[devID]; exists {
logrus.Warnf("type:%s, major:%d and minor:%d for linux devices is duplicated", device.Type, device.Major, device.Minor)
} else {
devTypeList[devID] = true
} }
} }
if v.spec.Linux.Resources != nil { if v.spec.Linux.Resources != nil {
ms := v.CheckLinuxResources() errs = multierror.Append(errs, v.CheckLinuxResources())
msgs = append(msgs, ms...)
} }
if v.spec.Linux.Seccomp != nil { if v.spec.Linux.Seccomp != nil {
ms := v.CheckSeccomp() errs = multierror.Append(errs, v.CheckSeccomp())
msgs = append(msgs, ms...)
} }
switch v.spec.Linux.RootfsPropagation { switch v.spec.Linux.RootfsPropagation {
@ -506,18 +657,18 @@ func (v *Validator) CheckLinux() (msgs []string) {
case "unbindable": case "unbindable":
case "runbindable": case "runbindable":
default: default:
msgs = append(msgs, "rootfsPropagation must be empty or one of \"private|rprivate|slave|rslave|shared|rshared|unbindable|runbindable\"") errs = multierror.Append(errs, errors.New("rootfsPropagation must be empty or one of \"private|rprivate|slave|rslave|shared|rshared|unbindable|runbindable\""))
} }
for _, maskedPath := range v.spec.Linux.MaskedPaths { for _, maskedPath := range v.spec.Linux.MaskedPaths {
if !strings.HasPrefix(maskedPath, "/") { if !strings.HasPrefix(maskedPath, "/") {
msgs = append(msgs, fmt.Sprintf("maskedPath %v is not an absolute path", maskedPath)) errs = multierror.Append(errs, fmt.Errorf("maskedPath %v is not an absolute path", maskedPath))
} }
} }
for _, readonlyPath := range v.spec.Linux.ReadonlyPaths { for _, readonlyPath := range v.spec.Linux.ReadonlyPaths {
if !strings.HasPrefix(readonlyPath, "/") { if !strings.HasPrefix(readonlyPath, "/") {
msgs = append(msgs, fmt.Sprintf("readonlyPath %v is not an absolute path", readonlyPath)) errs = multierror.Append(errs, fmt.Errorf("readonlyPath %v is not an absolute path", readonlyPath))
} }
} }
@ -525,23 +676,23 @@ func (v *Validator) CheckLinux() (msgs []string) {
} }
// CheckLinuxResources checks v.spec.Linux.Resources // CheckLinuxResources checks v.spec.Linux.Resources
func (v *Validator) CheckLinuxResources() (msgs []string) { func (v *Validator) CheckLinuxResources() (errs error) {
logrus.Debugf("check linux resources") logrus.Debugf("check linux resources")
r := v.spec.Linux.Resources r := v.spec.Linux.Resources
if r.Memory != nil { if r.Memory != nil {
if r.Memory.Limit != nil && r.Memory.Swap != nil && uint64(*r.Memory.Limit) > uint64(*r.Memory.Swap) { if r.Memory.Limit != nil && r.Memory.Swap != nil && uint64(*r.Memory.Limit) > uint64(*r.Memory.Swap) {
msgs = append(msgs, fmt.Sprintf("Minimum memoryswap should be larger than memory limit")) errs = multierror.Append(errs, fmt.Errorf("minimum memoryswap should be larger than memory limit"))
} }
if r.Memory.Limit != nil && r.Memory.Reservation != nil && uint64(*r.Memory.Reservation) > uint64(*r.Memory.Limit) { if r.Memory.Limit != nil && r.Memory.Reservation != nil && uint64(*r.Memory.Reservation) > uint64(*r.Memory.Limit) {
msgs = append(msgs, fmt.Sprintf("Minimum memory limit should be larger than memory reservation")) errs = multierror.Append(errs, fmt.Errorf("minimum memory limit should be larger than memory reservation"))
} }
} }
if r.Network != nil && v.HostSpecific { if r.Network != nil && v.HostSpecific {
var exist bool var exist bool
interfaces, err := net.Interfaces() interfaces, err := net.Interfaces()
if err != nil { if err != nil {
msgs = append(msgs, err.Error()) errs = multierror.Append(errs, err)
return return
} }
for _, prio := range r.Network.Priorities { for _, prio := range r.Network.Priorities {
@ -553,7 +704,24 @@ func (v *Validator) CheckLinuxResources() (msgs []string) {
} }
} }
if !exist { if !exist {
msgs = append(msgs, fmt.Sprintf("Interface %s does not exist currently", prio.Name)) errs = multierror.Append(errs, fmt.Errorf("interface %s does not exist currently", prio.Name))
}
}
}
for index := 0; index < len(r.Devices); index++ {
switch r.Devices[index].Type {
case "a", "b", "c":
default:
errs = multierror.Append(errs, fmt.Errorf("type of devices %s is invalid", r.Devices[index].Type))
}
access := []byte(r.Devices[index].Access)
for i := 0; i < len(access); i++ {
switch access[i] {
case 'r', 'w', 'm':
default:
errs = multierror.Append(errs, fmt.Errorf("access %s is invalid", r.Devices[index].Access))
return
} }
} }
} }
@ -562,16 +730,16 @@ func (v *Validator) CheckLinuxResources() (msgs []string) {
} }
// CheckSeccomp checkc v.spec.Linux.Seccomp // CheckSeccomp checkc v.spec.Linux.Seccomp
func (v *Validator) CheckSeccomp() (msgs []string) { func (v *Validator) CheckSeccomp() (errs error) {
logrus.Debugf("check linux seccomp") logrus.Debugf("check linux seccomp")
s := v.spec.Linux.Seccomp s := v.spec.Linux.Seccomp
if !seccompActionValid(s.DefaultAction) { if !seccompActionValid(s.DefaultAction) {
msgs = append(msgs, fmt.Sprintf("seccomp defaultAction %q is invalid.", s.DefaultAction)) errs = multierror.Append(errs, fmt.Errorf("seccomp defaultAction %q is invalid", s.DefaultAction))
} }
for index := 0; index < len(s.Syscalls); index++ { for index := 0; index < len(s.Syscalls); index++ {
if !syscallValid(s.Syscalls[index]) { if !syscallValid(s.Syscalls[index]) {
msgs = append(msgs, fmt.Sprintf("syscall %v is invalid.", s.Syscalls[index])) errs = multierror.Append(errs, fmt.Errorf("syscall %v is invalid", s.Syscalls[index]))
} }
} }
for index := 0; index < len(s.Architectures); index++ { for index := 0; index < len(s.Architectures); index++ {
@ -595,7 +763,7 @@ func (v *Validator) CheckSeccomp() (msgs []string) {
case rspec.ArchPARISC: case rspec.ArchPARISC:
case rspec.ArchPARISC64: case rspec.ArchPARISC64:
default: default:
msgs = append(msgs, fmt.Sprintf("seccomp architecture %q is invalid", s.Architectures[index])) errs = multierror.Append(errs, fmt.Errorf("seccomp architecture %q is invalid", s.Architectures[index]))
} }
} }
@ -612,7 +780,7 @@ func CapValid(c string, hostSpecific bool) error {
for _, cap := range capability.List() { for _, cap := range capability.List() {
if c == fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())) { if c == fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String())) {
if hostSpecific && cap > LastCap() { if hostSpecific && cap > LastCap() {
return fmt.Errorf("CAP_%s is not supported on the current host", c) return fmt.Errorf("%s is not supported on the current host", c)
} }
isValid = true isValid = true
break break
@ -620,7 +788,7 @@ func CapValid(c string, hostSpecific bool) error {
} }
if !isValid { if !isValid {
return fmt.Errorf("Invalid capability: %s", c) return fmt.Errorf("invalid capability: %s", c)
} }
return nil return nil
} }
@ -652,9 +820,9 @@ func envValid(env string) bool {
return true return true
} }
func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (msgs []string) { func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (errs error) {
if rlimit.Hard < rlimit.Soft { if rlimit.Hard < rlimit.Soft {
msgs = append(msgs, fmt.Sprintf("hard limit of rlimit %s should not be less than soft limit", rlimit.Type)) errs = multierror.Append(errs, fmt.Errorf("hard limit of rlimit %s should not be less than soft limit", rlimit.Type))
} }
if v.platform == "linux" { if v.platform == "linux" {
@ -663,7 +831,7 @@ func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (msgs []string) {
return return
} }
} }
msgs = append(msgs, fmt.Sprintf("rlimit type %q is invalid", rlimit.Type)) errs = multierror.Append(errs, fmt.Errorf("rlimit type %q is invalid", rlimit.Type))
} else { } else {
logrus.Warnf("process.rlimits validation not yet implemented for platform %q", v.platform) logrus.Warnf("process.rlimits validation not yet implemented for platform %q", v.platform)
} }
@ -691,6 +859,65 @@ func namespaceValid(ns rspec.LinuxNamespace) bool {
return true return true
} }
func pathValid(os, path string) error {
if os == "windows" {
matched, err := regexp.MatchString("^[a-zA-Z]:(\\\\[^\\\\/<>|:*?\"]+)+$", path)
if err != nil {
return err
}
if !matched {
return fmt.Errorf("invalid windows path %v", path)
}
return nil
}
if !filepath.IsAbs(path) {
return fmt.Errorf("%v is not an absolute path", path)
}
return nil
}
// Check whether pathB is nested whithin pathA
func nestedValid(os, pathA, pathB string) (bool, error) {
if pathA == pathB {
return false, nil
}
if pathA == "/" && pathB != "" {
return true, nil
}
var sep string
if os == "windows" {
sep = "\\"
} else {
sep = "/"
}
splitedPathA := strings.Split(filepath.Clean(pathA), sep)
splitedPathB := strings.Split(filepath.Clean(pathB), sep)
lenA := len(splitedPathA)
lenB := len(splitedPathB)
if lenA > lenB {
if (lenA - lenB) == 1 {
// if pathA is longer but not end with separator
if splitedPathA[lenA-1] != "" {
return false, nil
}
splitedPathA = splitedPathA[:lenA-1]
} else {
return false, nil
}
}
for i, partA := range splitedPathA {
if partA != splitedPathB[i] {
return false, nil
}
}
return true, nil
}
func deviceValid(d rspec.LinuxDevice) bool { func deviceValid(d rspec.LinuxDevice) bool {
switch d.Type { switch d.Type {
case "b", "c", "u": case "b", "c", "u":
@ -709,7 +936,6 @@ func deviceValid(d rspec.LinuxDevice) bool {
func seccompActionValid(secc rspec.LinuxSeccompAction) bool { func seccompActionValid(secc rspec.LinuxSeccompAction) bool {
switch secc { switch secc {
case "":
case rspec.ActKill: case rspec.ActKill:
case rspec.ActTrap: case rspec.ActTrap:
case rspec.ActErrno: case rspec.ActErrno:
@ -750,38 +976,38 @@ func isStructPtr(t reflect.Type) bool {
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
} }
func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, parent string) (msgs []string) { func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, parent string) (errs error) {
mandatory := !strings.Contains(tagField.Tag.Get("json"), "omitempty") mandatory := !strings.Contains(tagField.Tag.Get("json"), "omitempty")
switch field.Kind() { switch field.Kind() {
case reflect.Ptr: case reflect.Ptr:
if mandatory && field.IsNil() { if mandatory && field.IsNil() {
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name)) errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", parent, tagField.Name))
} }
case reflect.String: case reflect.String:
if mandatory && (field.Len() == 0) { if mandatory && (field.Len() == 0) {
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name)) errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", parent, tagField.Name))
} }
case reflect.Slice: case reflect.Slice:
if mandatory && (field.IsNil() || field.Len() == 0) { if mandatory && (field.IsNil() || field.Len() == 0) {
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name)) errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", parent, tagField.Name))
return return
} }
for index := 0; index < field.Len(); index++ { for index := 0; index < field.Len(); index++ {
mValue := field.Index(index) mValue := field.Index(index)
if mValue.CanInterface() { if mValue.CanInterface() {
msgs = append(msgs, checkMandatory(mValue.Interface())...) errs = multierror.Append(errs, checkMandatory(mValue.Interface()))
} }
} }
case reflect.Map: case reflect.Map:
if mandatory && (field.IsNil() || field.Len() == 0) { if mandatory && (field.IsNil() || field.Len() == 0) {
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name)) errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", parent, tagField.Name))
return msgs return
} }
keys := field.MapKeys() keys := field.MapKeys()
for index := 0; index < len(keys); index++ { for index := 0; index < len(keys); index++ {
mValue := field.MapIndex(keys[index]) mValue := field.MapIndex(keys[index])
if mValue.CanInterface() { if mValue.CanInterface() {
msgs = append(msgs, checkMandatory(mValue.Interface())...) errs = multierror.Append(errs, checkMandatory(mValue.Interface()))
} }
} }
default: default:
@ -790,7 +1016,7 @@ func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, paren
return return
} }
func checkMandatory(obj interface{}) (msgs []string) { func checkMandatory(obj interface{}) (errs error) {
objT := reflect.TypeOf(obj) objT := reflect.TypeOf(obj)
objV := reflect.ValueOf(obj) objV := reflect.ValueOf(obj)
if isStructPtr(objT) { if isStructPtr(objT) {
@ -804,12 +1030,12 @@ func checkMandatory(obj interface{}) (msgs []string) {
t := objT.Field(i).Type t := objT.Field(i).Type
if isStructPtr(t) && objV.Field(i).IsNil() { if isStructPtr(t) && objV.Field(i).IsNil() {
if !strings.Contains(objT.Field(i).Tag.Get("json"), "omitempty") { if !strings.Contains(objT.Field(i).Tag.Get("json"), "omitempty") {
msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty", objT.Name(), objT.Field(i).Name)) errs = multierror.Append(errs, fmt.Errorf("'%s.%s' should not be empty", objT.Name(), objT.Field(i).Name))
} }
} else if (isStruct(t) || isStructPtr(t)) && objV.Field(i).CanInterface() { } else if (isStruct(t) || isStructPtr(t)) && objV.Field(i).CanInterface() {
msgs = append(msgs, checkMandatory(objV.Field(i).Interface())...) errs = multierror.Append(errs, checkMandatory(objV.Field(i).Interface()))
} else { } else {
msgs = append(msgs, checkMandatoryUnit(objV.Field(i), objT.Field(i), objT.Name())...) errs = multierror.Append(errs, checkMandatoryUnit(objV.Field(i), objT.Field(i), objT.Name()))
} }
} }
@ -817,7 +1043,7 @@ func checkMandatory(obj interface{}) (msgs []string) {
} }
// CheckMandatoryFields checks mandatory field of container's config file // CheckMandatoryFields checks mandatory field of container's config file
func (v *Validator) CheckMandatoryFields() []string { func (v *Validator) CheckMandatoryFields() error {
logrus.Debugf("check mandatory fields") logrus.Debugf("check mandatory fields")
return checkMandatory(v.spec) return checkMandatory(v.spec)