Merge pull request #1393 from dmcgowan/multiple-differs
Add support for multiple differs
This commit is contained in:
commit
0daa593b3a
@ -4,11 +4,14 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/containerd/containerd/archive"
|
"github.com/containerd/containerd/archive"
|
||||||
"github.com/containerd/containerd/archive/compression"
|
"github.com/containerd/containerd/archive/compression"
|
||||||
"github.com/containerd/containerd/content"
|
"github.com/containerd/containerd/content"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/images"
|
||||||
"github.com/containerd/containerd/metadata"
|
"github.com/containerd/containerd/metadata"
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
@ -21,7 +24,7 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
plugin.Register(&plugin.Registration{
|
plugin.Register(&plugin.Registration{
|
||||||
Type: plugin.DiffPlugin,
|
Type: plugin.DiffPlugin,
|
||||||
ID: "base-diff",
|
ID: "walking",
|
||||||
Requires: []plugin.PluginType{
|
Requires: []plugin.PluginType{
|
||||||
plugin.ContentPlugin,
|
plugin.ContentPlugin,
|
||||||
plugin.MetadataPlugin,
|
plugin.MetadataPlugin,
|
||||||
@ -35,27 +38,38 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewBaseDiff(metadata.NewContentStore(md.(*bolt.DB), c.(content.Store)))
|
return newWalkingDiff(metadata.NewContentStore(md.(*bolt.DB), c.(content.Store)))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseDiff struct {
|
type walkingDiff struct {
|
||||||
store content.Store
|
store content.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ plugin.Differ = &BaseDiff{}
|
|
||||||
|
|
||||||
var emptyDesc = ocispec.Descriptor{}
|
var emptyDesc = ocispec.Descriptor{}
|
||||||
|
|
||||||
func NewBaseDiff(store content.Store) (*BaseDiff, error) {
|
func newWalkingDiff(store content.Store) (plugin.Differ, error) {
|
||||||
return &BaseDiff{
|
return &walkingDiff{
|
||||||
store: store,
|
store: store,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount) (ocispec.Descriptor, error) {
|
func (s *walkingDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount) (ocispec.Descriptor, error) {
|
||||||
// TODO: Check for supported media types
|
var isCompressed bool
|
||||||
|
switch desc.MediaType {
|
||||||
|
case ocispec.MediaTypeImageLayer, images.MediaTypeDockerSchema2Layer:
|
||||||
|
case ocispec.MediaTypeImageLayerGzip, images.MediaTypeDockerSchema2LayerGzip:
|
||||||
|
isCompressed = true
|
||||||
|
default:
|
||||||
|
// Still apply all generic media types *.tar[.+]gzip and *.tar
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "extract-")
|
dir, err := ioutil.TempDir("", "extract-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return emptyDesc, errors.Wrap(err, "failed to create temporary directory")
|
return emptyDesc, errors.Wrap(err, "failed to create temporary directory")
|
||||||
@ -67,22 +81,25 @@ func (s *BaseDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
|
|||||||
}
|
}
|
||||||
defer mount.Unmount(dir, 0)
|
defer mount.Unmount(dir, 0)
|
||||||
|
|
||||||
r, err := s.store.ReaderAt(ctx, desc.Digest)
|
ra, err := s.store.ReaderAt(ctx, desc.Digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return emptyDesc, errors.Wrap(err, "failed to get reader from content store")
|
return emptyDesc, errors.Wrap(err, "failed to get reader from content store")
|
||||||
}
|
}
|
||||||
defer r.Close()
|
defer ra.Close()
|
||||||
|
|
||||||
// TODO: only decompress stream if media type is compressed
|
r := content.NewReader(ra)
|
||||||
ds, err := compression.DecompressStream(content.NewReader(r))
|
if isCompressed {
|
||||||
if err != nil {
|
ds, err := compression.DecompressStream(r)
|
||||||
return emptyDesc, err
|
if err != nil {
|
||||||
|
return emptyDesc, err
|
||||||
|
}
|
||||||
|
defer ds.Close()
|
||||||
|
r = ds
|
||||||
}
|
}
|
||||||
defer ds.Close()
|
|
||||||
|
|
||||||
digester := digest.Canonical.Digester()
|
digester := digest.Canonical.Digester()
|
||||||
rc := &readCounter{
|
rc := &readCounter{
|
||||||
r: io.TeeReader(ds, digester.Hash()),
|
r: io.TeeReader(r, digester.Hash()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := archive.Apply(ctx, dir, rc); err != nil {
|
if _, err := archive.Apply(ctx, dir, rc); err != nil {
|
||||||
@ -101,7 +118,7 @@ func (s *BaseDiff) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BaseDiff) DiffMounts(ctx context.Context, lower, upper []mount.Mount, media, ref string) (ocispec.Descriptor, error) {
|
func (s *walkingDiff) DiffMounts(ctx context.Context, lower, upper []mount.Mount, media, ref string) (ocispec.Descriptor, error) {
|
||||||
var isCompressed bool
|
var isCompressed bool
|
||||||
switch media {
|
switch media {
|
||||||
case ocispec.MediaTypeImageLayer:
|
case ocispec.MediaTypeImageLayer:
|
||||||
@ -111,7 +128,7 @@ func (s *BaseDiff) DiffMounts(ctx context.Context, lower, upper []mount.Mount, m
|
|||||||
media = ocispec.MediaTypeImageLayerGzip
|
media = ocispec.MediaTypeImageLayerGzip
|
||||||
isCompressed = true
|
isCompressed = true
|
||||||
default:
|
default:
|
||||||
return emptyDesc, errors.Errorf("unsupported diff media type: %v", media)
|
return emptyDesc, errors.Wrapf(errdefs.ErrNotSupported, "unsupported diff media type: %v", media)
|
||||||
}
|
}
|
||||||
aDir, err := ioutil.TempDir("", "left-")
|
aDir, err := ioutil.TempDir("", "left-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -26,6 +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
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsInvalidArgument(err error) bool {
|
func IsInvalidArgument(err error) bool {
|
||||||
@ -52,3 +53,7 @@ func IsFailedPrecondition(err error) bool {
|
|||||||
func IsUnavailable(err error) bool {
|
func IsUnavailable(err error) bool {
|
||||||
return errors.Cause(err) == ErrUnavailable
|
return errors.Cause(err) == ErrUnavailable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsNotSupported(err error) bool {
|
||||||
|
return errors.Cause(err) == ErrNotSupported
|
||||||
|
}
|
||||||
|
@ -38,6 +38,8 @@ 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):
|
||||||
|
return grpc.Errorf(codes.Unimplemented, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@ -69,6 +71,8 @@ func FromGRPC(err error) error {
|
|||||||
cls = ErrUnavailable
|
cls = ErrUnavailable
|
||||||
case codes.FailedPrecondition:
|
case codes.FailedPrecondition:
|
||||||
cls = ErrFailedPrecondition
|
cls = ErrFailedPrecondition
|
||||||
|
case codes.Unimplemented:
|
||||||
|
cls = ErrNotSupported
|
||||||
default:
|
default:
|
||||||
cls = ErrUnknown
|
cls = ErrUnknown
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,22 @@ import (
|
|||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/plugin"
|
"github.com/containerd/containerd/plugin"
|
||||||
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
// Order is the order of preference in which to try diff algorithms, the
|
||||||
|
// first differ which is supported is used.
|
||||||
|
// Note when multiple differs may be supported, this order will be
|
||||||
|
// respected for which is choosen. Each differ should return the same
|
||||||
|
// correct output, allowing any ordering to be used to prefer
|
||||||
|
// more optimimal implementations.
|
||||||
|
Order []string `toml:"default,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
plugin.Register(&plugin.Registration{
|
plugin.Register(&plugin.Registration{
|
||||||
Type: plugin.GRPCPlugin,
|
Type: plugin.GRPCPlugin,
|
||||||
@ -17,20 +29,34 @@ func init() {
|
|||||||
Requires: []plugin.PluginType{
|
Requires: []plugin.PluginType{
|
||||||
plugin.DiffPlugin,
|
plugin.DiffPlugin,
|
||||||
},
|
},
|
||||||
|
Config: &config{
|
||||||
|
Order: []string{"walking"},
|
||||||
|
},
|
||||||
Init: func(ic *plugin.InitContext) (interface{}, error) {
|
Init: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
d, err := ic.Get(plugin.DiffPlugin)
|
differs, err := ic.GetAll(plugin.DiffPlugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderedNames := ic.Config.(*config).Order
|
||||||
|
ordered := make([]plugin.Differ, len(orderedNames))
|
||||||
|
for i, n := range orderedNames {
|
||||||
|
differ, ok := differs[n]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("needed differ not loaded: %s", n)
|
||||||
|
}
|
||||||
|
ordered[i] = differ.(plugin.Differ)
|
||||||
|
}
|
||||||
|
|
||||||
return &service{
|
return &service{
|
||||||
diff: d.(plugin.Differ),
|
differs: ordered,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
diff plugin.Differ
|
differs []plugin.Differ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Register(gs *grpc.Server) error {
|
func (s *service) Register(gs *grpc.Server) error {
|
||||||
@ -39,12 +65,20 @@ func (s *service) Register(gs *grpc.Server) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Apply(ctx context.Context, er *diffapi.ApplyRequest) (*diffapi.ApplyResponse, error) {
|
func (s *service) Apply(ctx context.Context, er *diffapi.ApplyRequest) (*diffapi.ApplyResponse, error) {
|
||||||
desc := toDescriptor(er.Diff)
|
var (
|
||||||
// TODO: Check for supported media types
|
ocidesc ocispec.Descriptor
|
||||||
|
err error
|
||||||
|
desc = toDescriptor(er.Diff)
|
||||||
|
mounts = toMounts(er.Mounts)
|
||||||
|
)
|
||||||
|
|
||||||
mounts := toMounts(er.Mounts)
|
for _, differ := range s.differs {
|
||||||
|
ocidesc, err = differ.Apply(ctx, desc, mounts)
|
||||||
|
if !errdefs.IsNotSupported(err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ocidesc, err := s.diff.Apply(ctx, desc, mounts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errdefs.ToGRPC(err)
|
return nil, errdefs.ToGRPC(err)
|
||||||
}
|
}
|
||||||
@ -56,10 +90,19 @@ func (s *service) Apply(ctx context.Context, er *diffapi.ApplyRequest) (*diffapi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Diff(ctx context.Context, dr *diffapi.DiffRequest) (*diffapi.DiffResponse, error) {
|
func (s *service) Diff(ctx context.Context, dr *diffapi.DiffRequest) (*diffapi.DiffResponse, error) {
|
||||||
aMounts := toMounts(dr.Left)
|
var (
|
||||||
bMounts := toMounts(dr.Right)
|
ocidesc ocispec.Descriptor
|
||||||
|
err error
|
||||||
|
aMounts = toMounts(dr.Left)
|
||||||
|
bMounts = toMounts(dr.Right)
|
||||||
|
)
|
||||||
|
|
||||||
ocidesc, err := s.diff.DiffMounts(ctx, aMounts, bMounts, dr.MediaType, dr.Ref)
|
for _, differ := range s.differs {
|
||||||
|
ocidesc, err = differ.DiffMounts(ctx, aMounts, bMounts, dr.MediaType, dr.Ref)
|
||||||
|
if !errdefs.IsNotSupported(err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errdefs.ToGRPC(err)
|
return nil, errdefs.ToGRPC(err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user