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:
parent
49437711c3
commit
46ded63f2d
@ -30,6 +30,7 @@ import (
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/remotes"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
"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")
|
||||
}
|
||||
|
||||
diffIDs, err := image.RootFS(ctx, cs)
|
||||
diffIDs, err := image.RootFS(ctx, cs, platforms.Format(platforms.Default()))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to resolve rootfs")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
23
image.go
23
image.go
@ -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")
|
||||
}
|
||||
|
109
images/image.go
109
images/image.go
@ -6,6 +6,7 @@ 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"
|
||||
@ -40,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
|
||||
}
|
||||
@ -68,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) {
|
||||
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
|
||||
}
|
||||
@ -88,14 +95,80 @@ 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
|
||||
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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user