vendor containerd d1e11f17ec7b325f89608dd46c128300b8727d50

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
docker-18.09
Akihiro Suda 2017-09-14 18:30:08 +00:00
parent a26eba7cda
commit 279940b60d
26 changed files with 608 additions and 78 deletions

View File

@ -69,7 +69,7 @@ Different versions of the example scripts show different ways of describing the
#### Supported runc version
During development buildkit is tested with the version of runc that is being used by the containerd repository. Please refer to [runc.md](https://github.com/containerd/containerd/blob/v1.0.0-beta.0/RUNC.md) for more information.
During development buildkit is tested with the version of runc that is being used by the containerd repository. Please refer to [runc.md](https://github.com/containerd/containerd/blob/d1e11f17ec7b325f89608dd46c128300b8727d50/RUNC.md) for more information.
#### Contributing

View File

@ -8,6 +8,7 @@ import (
"path/filepath"
"github.com/containerd/containerd/content/local"
"github.com/containerd/containerd/differ"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/namespaces"
ctdsnapshot "github.com/containerd/containerd/snapshot"
@ -54,8 +55,7 @@ func newStandalonePullDeps(root string) (*pullDeps, error) {
return nil, err
}
// TODO: use github.com/containerd/containerd/differ.newWalkingDiff() when it is exposed
differ, err := newWalkingDiff(c)
df, err := differ.NewWalkingDiff(c)
if err != nil {
return nil, err
}
@ -63,8 +63,8 @@ func newStandalonePullDeps(root string) (*pullDeps, error) {
return &pullDeps{
Snapshotter: &nsSnapshotter{s},
ContentStore: c,
Applier: differ,
Differ: differ,
Applier: df,
Differ: df,
}, nil
}

View File

@ -1,5 +1,5 @@
ARG RUNC_VERSION=e775f0fba3ea329b8b766451c892c41a3d49594d
ARG CONTAINERD_VERSION=v1.0.0-beta.0
ARG CONTAINERD_VERSION=d1e11f17ec7b325f89608dd46c128300b8727d50
FROM golang:1.8-alpine AS gobuild-base
RUN apk add --no-cache g++ linux-headers

View File

@ -222,7 +222,7 @@ func getLayers(ctx context.Context, provider content.Provider, desc ocispec.Desc
return nil, errors.Wrap(err, "failed to unmarshal manifest")
}
image := images.Image{Target: desc}
diffIDs, err := image.RootFS(ctx, provider)
diffIDs, err := image.RootFS(ctx, provider, "")
if err != nil {
return nil, errors.Wrap(err, "failed to resolve rootfs")
}

View File

@ -35,7 +35,7 @@ func Config(ctx context.Context, ref string, resolver remotes.Resolver, ingester
if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
return nil, err
}
config, err := images.Config(ctx, ingester, desc)
config, err := images.Config(ctx, ingester, desc, "")
if err != nil {
return nil, err
}

View File

@ -6,7 +6,7 @@ github.com/davecgh/go-spew v1.1.0
github.com/pmezard/go-difflib v1.0.0
golang.org/x/sys 739734461d1c916b6c72a63d7efda2b27edb369f
github.com/containerd/containerd v1.0.0-beta.0
github.com/containerd/containerd d1e11f17ec7b325f89608dd46c128300b8727d50
golang.org/x/sync f52d1811a62927559de87708c8913c1650ce4f26
github.com/sirupsen/logrus v1.0.0
google.golang.org/grpc v1.3.0

View File

@ -5,6 +5,7 @@ import (
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/typeurl"
"github.com/gogo/protobuf/types"
"github.com/opencontainers/image-spec/identity"
@ -79,7 +80,7 @@ func WithSnapshot(id string) NewContainerOpts {
// root filesystem in read-write mode
func WithNewSnapshot(id string, i Image) NewContainerOpts {
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 {
return err
}
@ -108,7 +109,7 @@ func WithSnapshotCleanup(ctx context.Context, client *Client, c containers.Conta
// root filesystem in read-only mode
func WithNewSnapshotView(id string, i Image) NewContainerOpts {
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 {
return err
}

View File

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

View File

@ -1,7 +1,4 @@
// This file is just copied from https://github.com/containerd/containerd/blob/v1.0.0-beta.0/differ/differ.go
// This file should be removed when the containerd exposes newWalkingDiff().
package control
package differ
import (
"io"
@ -9,11 +6,13 @@ import (
"os"
"strings"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/archive"
"github.com/containerd/containerd/archive/compression"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/plugin"
digest "github.com/opencontainers/go-digest"
@ -22,18 +21,45 @@ import (
"golang.org/x/net/context"
)
func init() {
plugin.Register(&plugin.Registration{
Type: plugin.DiffPlugin,
ID: "walking",
Requires: []plugin.PluginType{
plugin.ContentPlugin,
plugin.MetadataPlugin,
},
Init: func(ic *plugin.InitContext) (interface{}, error) {
c, err := ic.Get(plugin.ContentPlugin)
if err != nil {
return nil, err
}
md, err := ic.Get(plugin.MetadataPlugin)
if err != nil {
return nil, err
}
return NewWalkingDiff(metadata.NewContentStore(md.(*bolt.DB), c.(content.Store)))
},
})
}
type walkingDiff struct {
store content.Store
}
var emptyDesc = ocispec.Descriptor{}
func newWalkingDiff(store content.Store) (plugin.Differ, error) {
// NewWalkingDiff is a generic implementation of plugin.Differ.
// NewWalkingDiff is expected to work with any filesystem.
func NewWalkingDiff(store content.Store) (plugin.Differ, error) {
return &walkingDiff{
store: store,
}, nil
}
// Apply applies the content associated with the provided digests onto the
// provided mounts. Archive content will be extracted and decompressed if
// necessary.
func (s *walkingDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount) (ocispec.Descriptor, error) {
var isCompressed bool
switch desc.MediaType {
@ -45,7 +71,7 @@ func (s *walkingDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts
if strings.HasSuffix(desc.MediaType, ".tar.gzip") || strings.HasSuffix(desc.MediaType, ".tar+gzip") {
isCompressed = true
} else if !strings.HasSuffix(desc.MediaType, ".tar") {
return emptyDesc, errors.Wrapf(errdefs.ErrNotSupported, "unsupported diff media type: %v", desc.MediaType)
return emptyDesc, errors.Wrapf(errdefs.ErrNotImplemented, "unsupported diff media type: %v", desc.MediaType)
}
}
@ -97,6 +123,8 @@ func (s *walkingDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts
}, nil
}
// DiffMounts creates a diff between the given mounts and uploads the result
// to the content store.
func (s *walkingDiff) DiffMounts(ctx context.Context, lower, upper []mount.Mount, media, ref string) (ocispec.Descriptor, error) {
var isCompressed bool
switch media {
@ -107,7 +135,7 @@ func (s *walkingDiff) DiffMounts(ctx context.Context, lower, upper []mount.Mount
media = ocispec.MediaTypeImageLayerGzip
isCompressed = true
default:
return emptyDesc, errors.Wrapf(errdefs.ErrNotSupported, "unsupported diff media type: %v", media)
return emptyDesc, errors.Wrapf(errdefs.ErrNotImplemented, "unsupported diff media type: %v", media)
}
aDir, err := ioutil.TempDir("", "left-")
if err != nil {

View File

@ -26,7 +26,7 @@ var (
ErrAlreadyExists = errors.New("already exists")
ErrFailedPrecondition = errors.New("failed precondition")
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 {
@ -54,6 +54,6 @@ func IsUnavailable(err error) bool {
return errors.Cause(err) == ErrUnavailable
}
func IsNotSupported(err error) bool {
return errors.Cause(err) == ErrNotSupported
func IsNotImplemented(err error) bool {
return errors.Cause(err) == ErrNotImplemented
}

View File

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

View File

@ -2,10 +2,9 @@ package containerd
import (
"context"
"encoding/json"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/rootfs"
digest "github.com/opencontainers/go-digest"
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) {
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) {
@ -56,11 +55,11 @@ func (i *image) Size(ctx context.Context) (int64, error) {
func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) {
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 {
layers, err := i.getLayers(ctx)
layers, err := i.getLayers(ctx, platforms.Format(platforms.Default()))
if err != nil {
return err
}
@ -98,19 +97,15 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error {
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()
// TODO: Support manifest list
p, err := content.ReadBlob(ctx, cs, i.i.Target.Digest)
manifest, err := images.Manifest(ctx, cs, i.i.Target, platform)
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 {
return nil, errors.Wrap(err, "failed to unmarshal manifest")
}
diffIDs, err := i.i.RootFS(ctx, cs)
diffIDs, err := i.i.RootFS(ctx, cs, platform)
if err != nil {
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)
if err != nil {
if errors.Cause(err) == SkipDesc {
return nil // don't traverse the children.
continue // don't traverse the children.
}
return err
}

View File

@ -6,6 +6,8 @@ import (
"time"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/platforms"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
@ -39,16 +41,16 @@ type Store interface {
//
// The caller can then use the descriptor to resolve and process the
// configuration of the image.
func (image *Image) Config(ctx context.Context, provider content.Provider) (ocispec.Descriptor, error) {
return Config(ctx, provider, image.Target)
func (image *Image) Config(ctx context.Context, provider content.Provider, platform string) (ocispec.Descriptor, error) {
return Config(ctx, provider, image.Target, platform)
}
// 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
// values.
func (image *Image) RootFS(ctx context.Context, provider content.Provider) ([]digest.Digest, error) {
desc, err := image.Config(ctx, provider)
func (image *Image) RootFS(ctx context.Context, provider content.Provider, platform string) ([]digest.Digest, error) {
desc, err := image.Config(ctx, provider, platform)
if err != nil {
return nil, err
}
@ -67,17 +69,23 @@ func (image *Image) Size(ctx context.Context, provider content.Provider) (int64,
}), ChildrenHandler(provider)), image.Target)
}
// 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) (ocispec.Descriptor, error) {
var configDesc ocispec.Descriptor
return configDesc, Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
switch image.MediaType {
func Manifest(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform string) (ocispec.Manifest, error) {
var (
matcher platforms.Matcher
m *ocispec.Manifest
err error
)
if platform != "" {
matcher, err = platforms.Parse(platform)
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:
p, err := content.ReadBlob(ctx, provider, image.Digest)
p, err := content.ReadBlob(ctx, provider, desc.Digest)
if err != nil {
return nil, err
}
@ -87,14 +95,108 @@ func Config(ctx context.Context, provider content.Provider, image ocispec.Descri
return nil, err
}
configDesc = manifest.Config
if platform != "" {
if desc.Platform != nil && !matcher.Match(*desc.Platform) {
return nil, nil
}
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
default:
return nil, errors.New("could not resolve config")
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
}
}), image)
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.

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

@ -31,6 +31,11 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R
return nil, err
}
ctx, err = contextWithRepositoryScope(ctx, r.refspec, false)
if err != nil {
return nil, err
}
for _, path := range paths {
u := r.url(path)

View File

@ -28,6 +28,10 @@ type dockerPusher struct {
}
func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) {
ctx, err := contextWithRepositoryScope(ctx, p.refspec, true)
if err != nil {
return nil, err
}
ref := remotes.MakeRefKey(ctx, desc)
status, err := p.tracker.GetStatus(ref)
if err == nil {

View File

@ -116,6 +116,10 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp
urls = append(urls, fetcher.url("manifests", refspec.Object))
}
ctx, err = contextWithRepositoryScope(ctx, refspec, false)
if err != nil {
return "", ocispec.Descriptor{}, err
}
for _, u := range urls {
req, err := http.NewRequest(http.MethodHead, u, nil)
if err != nil {
@ -228,8 +232,9 @@ func (r *dockerResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher
}
type dockerBase struct {
base url.URL
token string
refspec reference.Spec
base url.URL
token string
client *http.Client
useBasic bool
@ -268,6 +273,7 @@ func (r *dockerResolver) base(refspec reference.Spec) (*dockerBase, error) {
base.Path = path.Join("/v2", prefix)
return &dockerBase{
refspec: refspec,
base: base,
client: r.client,
username: username,
@ -430,14 +436,10 @@ func (r *dockerBase) setTokenAuth(ctx context.Context, params map[string]string)
service: params["service"],
}
scope, ok := params["scope"]
if !ok {
to.scopes = getTokenScopes(ctx, params)
if len(to.scopes) == 0 {
return errors.Errorf("no scope specified for token auth challenge")
}
// TODO: Get added scopes from context
to.scopes = []string{scope}
if r.secret != "" {
// Credential information is provided, use oauth POST endpoint
r.token, err = r.fetchTokenWithOAuth(ctx, to)
@ -491,8 +493,9 @@ func (r *dockerBase) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (
}
defer resp.Body.Close()
if resp.StatusCode == 405 && r.username != "" {
// It would be nice if registries would implement the specifications
// Registries without support for POST may return 404 for POST /v2/token.
// As of September 2017, GCR is known to return 404.
if (resp.StatusCode == 405 && r.username != "") || resp.StatusCode == 404 {
return r.getToken(ctx, to)
} else if resp.StatusCode < 200 || resp.StatusCode >= 400 {
b, _ := ioutil.ReadAll(resp.Body)

View File

@ -0,0 +1,60 @@
package docker
import (
"context"
"net/url"
"sort"
"strings"
"github.com/containerd/containerd/reference"
)
// repositoryScope returns a repository scope string such as "repository:foo/bar:pull"
// for "host/foo/bar:baz".
// When push is true, both pull and push are added to the scope.
func repositoryScope(refspec reference.Spec, push bool) (string, error) {
u, err := url.Parse("dummy://" + refspec.Locator)
if err != nil {
return "", err
}
s := "repository:" + strings.TrimPrefix(u.Path, "/") + ":pull"
if push {
s += ",push"
}
return s, nil
}
// tokenScopesKey is used for the key for context.WithValue().
// value: []string (e.g. {"registry:foo/bar:pull"})
type tokenScopesKey struct{}
// contextWithRepositoryScope returns a context with tokenScopesKey{} and the repository scope value.
func contextWithRepositoryScope(ctx context.Context, refspec reference.Spec, push bool) (context.Context, error) {
s, err := repositoryScope(refspec, push)
if err != nil {
return nil, err
}
return context.WithValue(ctx, tokenScopesKey{}, []string{s}), nil
}
// getTokenScopes returns deduplicated and sorted scopes from ctx.Value(tokenScopesKey{}) and params["scope"].
func getTokenScopes(ctx context.Context, params map[string]string) []string {
var scopes []string
if x := ctx.Value(tokenScopesKey{}); x != nil {
scopes = append(scopes, x.([]string)...)
}
if scope, ok := params["scope"]; ok {
for _, s := range scopes {
// Note: this comparison is unaware of the scope grammar (https://docs.docker.com/registry/spec/auth/scope/)
// So, "repository:foo/bar:pull,push" != "repository:foo/bar:push,pull", although semantically they are equal.
if s == scope {
// already appended
goto Sort
}
}
scopes = append(scopes, scope)
}
Sort:
sort.Strings(scopes)
return scopes
}

View File

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

View File

@ -19,6 +19,7 @@ import (
"github.com/containerd/containerd/fs"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/platforms"
"github.com/opencontainers/image-spec/identity"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runc/libcontainer/user"
@ -72,7 +73,7 @@ func WithImageConfig(i Image) SpecOpts {
image = i.(*image)
store = client.ContentStore()
)
ic, err := image.i.Config(ctx, store)
ic, err := image.i.Config(ctx, store, platforms.Format(platforms.Default()))
if err != nil {
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 {
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 {
return err
}

View File

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

View File

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

View File

@ -15,7 +15,7 @@ github.com/docker/go-units v0.3.1
github.com/gogo/protobuf d2e1ade2d719b78fe5b061b4c18a9f7111b5bdc8
github.com/golang/protobuf 5a0f697c9ed9d68fef0116532c6e05cfeae00e55
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/containerd/btrfs cc52c4dea2ce11a44e6639e561bb5c2af9ada9e3
github.com/stretchr/testify v1.1.4