Support for multi-arch image unpacking

Resolves the platform on multi-arch manifests during unpack and config resolving.

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
This commit is contained in:
Derek McGowan 2017-09-13 15:42:05 -07:00
parent 49437711c3
commit 46ded63f2d
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB
7 changed files with 112 additions and 39 deletions

View File

@ -30,6 +30,7 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"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/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker" "github.com/containerd/containerd/remotes/docker"
"github.com/containerd/containerd/rootfs" "github.com/containerd/containerd/rootfs"
@ -205,7 +206,7 @@ func getImageLayers(ctx gocontext.Context, image images.Image, cs content.Store)
return nil, errors.Wrap(err, "failed to unmarshal manifest") return nil, errors.Wrap(err, "failed to unmarshal manifest")
} }
diffIDs, err := image.RootFS(ctx, cs) diffIDs, err := image.RootFS(ctx, cs, platforms.Format(platforms.Default()))
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

@ -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

@ -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

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/platforms" "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"
@ -40,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
} }
@ -68,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)
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 { 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
} }
@ -88,14 +95,80 @@ 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
}
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 return nil, nil
default: case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
return nil, errors.New("could not resolve config") p, err := content.ReadBlob(ctx, provider, desc.Digest)
} if err != nil {
return nil, err
}
}), image) 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. // Platforms returns one or more platforms supported by the image.

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
} }