From 3784c1c9170d8f3af247f95a34817250a1ca2b93 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Wed, 12 Apr 2023 15:57:01 -0700 Subject: [PATCH] Add proxy differ Signed-off-by: Derek McGowan --- diff.go | 105 +----------------------------- diff/proxy/differ.go | 131 ++++++++++++++++++++++++++++++++++++++ services/server/server.go | 7 ++ 3 files changed, 140 insertions(+), 103 deletions(-) create mode 100644 diff/proxy/differ.go diff --git a/diff.go b/diff.go index 6e3ae2f5e..0b1d44ed5 100644 --- a/diff.go +++ b/diff.go @@ -17,19 +17,9 @@ package containerd import ( - "context" - diffapi "github.com/containerd/containerd/api/services/diff/v1" - "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/diff" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/mount" - "github.com/containerd/containerd/pkg/epoch" - "github.com/containerd/containerd/protobuf" - ptypes "github.com/containerd/containerd/protobuf/types" - "github.com/opencontainers/go-digest" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "google.golang.org/protobuf/types/known/timestamppb" + "github.com/containerd/containerd/diff/proxy" ) // DiffService handles the computation and application of diffs @@ -41,96 +31,5 @@ type DiffService interface { // NewDiffServiceFromClient returns a new diff service which communicates // over a GRPC connection. func NewDiffServiceFromClient(client diffapi.DiffClient) DiffService { - return &diffRemote{ - client: client, - } -} - -type diffRemote struct { - client diffapi.DiffClient -} - -func (r *diffRemote) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount, opts ...diff.ApplyOpt) (ocispec.Descriptor, error) { - var config diff.ApplyConfig - for _, opt := range opts { - if err := opt(ctx, desc, &config); err != nil { - return ocispec.Descriptor{}, err - } - } - - payloads := make(map[string]*ptypes.Any) - for k, v := range config.ProcessorPayloads { - payloads[k] = protobuf.FromAny(v) - } - - req := &diffapi.ApplyRequest{ - Diff: fromDescriptor(desc), - Mounts: fromMounts(mounts), - Payloads: payloads, - } - resp, err := r.client.Apply(ctx, req) - if err != nil { - return ocispec.Descriptor{}, errdefs.FromGRPC(err) - } - return toDescriptor(resp.Applied), nil -} - -func (r *diffRemote) Compare(ctx context.Context, a, b []mount.Mount, opts ...diff.Opt) (ocispec.Descriptor, error) { - var config diff.Config - for _, opt := range opts { - if err := opt(&config); err != nil { - return ocispec.Descriptor{}, err - } - } - if tm := epoch.FromContext(ctx); tm != nil && config.SourceDateEpoch == nil { - config.SourceDateEpoch = tm - } - var sourceDateEpoch *timestamppb.Timestamp - if config.SourceDateEpoch != nil { - sourceDateEpoch = timestamppb.New(*config.SourceDateEpoch) - } - req := &diffapi.DiffRequest{ - Left: fromMounts(a), - Right: fromMounts(b), - MediaType: config.MediaType, - Ref: config.Reference, - Labels: config.Labels, - SourceDateEpoch: sourceDateEpoch, - } - resp, err := r.client.Diff(ctx, req) - if err != nil { - return ocispec.Descriptor{}, errdefs.FromGRPC(err) - } - return toDescriptor(resp.Diff), nil -} - -func toDescriptor(d *types.Descriptor) ocispec.Descriptor { - return ocispec.Descriptor{ - MediaType: d.MediaType, - Digest: digest.Digest(d.Digest), - Size: d.Size, - Annotations: d.Annotations, - } -} - -func fromDescriptor(d ocispec.Descriptor) *types.Descriptor { - return &types.Descriptor{ - MediaType: d.MediaType, - Digest: d.Digest.String(), - Size: d.Size, - Annotations: d.Annotations, - } -} - -func fromMounts(mounts []mount.Mount) []*types.Mount { - apiMounts := make([]*types.Mount, len(mounts)) - for i, m := range mounts { - apiMounts[i] = &types.Mount{ - Type: m.Type, - Source: m.Source, - Target: m.Target, - Options: m.Options, - } - } - return apiMounts + return proxy.NewDiffApplier(client).(DiffService) } diff --git a/diff/proxy/differ.go b/diff/proxy/differ.go new file mode 100644 index 000000000..8ed8bdf4d --- /dev/null +++ b/diff/proxy/differ.go @@ -0,0 +1,131 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package proxy + +import ( + "context" + + diffapi "github.com/containerd/containerd/api/services/diff/v1" + "github.com/containerd/containerd/api/types" + "github.com/containerd/containerd/diff" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/pkg/epoch" + "github.com/containerd/containerd/protobuf" + ptypes "github.com/containerd/containerd/protobuf/types" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + + "google.golang.org/protobuf/types/known/timestamppb" +) + +// NewDiffApplier returns a new comparer and applier which communicates +// over a GRPC connection. +func NewDiffApplier(client diffapi.DiffClient) interface{} { + return &diffRemote{ + client: client, + } +} + +type diffRemote struct { + client diffapi.DiffClient +} + +func (r *diffRemote) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount, opts ...diff.ApplyOpt) (ocispec.Descriptor, error) { + var config diff.ApplyConfig + for _, opt := range opts { + if err := opt(ctx, desc, &config); err != nil { + return ocispec.Descriptor{}, err + } + } + + payloads := make(map[string]*ptypes.Any) + for k, v := range config.ProcessorPayloads { + payloads[k] = protobuf.FromAny(v) + } + + req := &diffapi.ApplyRequest{ + Diff: fromDescriptor(desc), + Mounts: fromMounts(mounts), + Payloads: payloads, + } + resp, err := r.client.Apply(ctx, req) + if err != nil { + return ocispec.Descriptor{}, errdefs.FromGRPC(err) + } + return toDescriptor(resp.Applied), nil +} + +func (r *diffRemote) Compare(ctx context.Context, a, b []mount.Mount, opts ...diff.Opt) (ocispec.Descriptor, error) { + var config diff.Config + for _, opt := range opts { + if err := opt(&config); err != nil { + return ocispec.Descriptor{}, err + } + } + if tm := epoch.FromContext(ctx); tm != nil && config.SourceDateEpoch == nil { + config.SourceDateEpoch = tm + } + var sourceDateEpoch *timestamppb.Timestamp + if config.SourceDateEpoch != nil { + sourceDateEpoch = timestamppb.New(*config.SourceDateEpoch) + } + req := &diffapi.DiffRequest{ + Left: fromMounts(a), + Right: fromMounts(b), + MediaType: config.MediaType, + Ref: config.Reference, + Labels: config.Labels, + SourceDateEpoch: sourceDateEpoch, + } + resp, err := r.client.Diff(ctx, req) + if err != nil { + return ocispec.Descriptor{}, errdefs.FromGRPC(err) + } + return toDescriptor(resp.Diff), nil +} + +func toDescriptor(d *types.Descriptor) ocispec.Descriptor { + return ocispec.Descriptor{ + MediaType: d.MediaType, + Digest: digest.Digest(d.Digest), + Size: d.Size, + Annotations: d.Annotations, + } +} + +func fromDescriptor(d ocispec.Descriptor) *types.Descriptor { + return &types.Descriptor{ + MediaType: d.MediaType, + Digest: d.Digest.String(), + Size: d.Size, + Annotations: d.Annotations, + } +} + +func fromMounts(mounts []mount.Mount) []*types.Mount { + apiMounts := make([]*types.Mount, len(mounts)) + for i, m := range mounts { + apiMounts[i] = &types.Mount{ + Type: m.Type, + Source: m.Source, + Target: m.Target, + Options: m.Options, + } + } + return apiMounts +} diff --git a/services/server/server.go b/services/server/server.go index d6aba65f9..e35c4a53a 100644 --- a/services/server/server.go +++ b/services/server/server.go @@ -34,11 +34,13 @@ import ( "time" csapi "github.com/containerd/containerd/api/services/content/v1" + diffapi "github.com/containerd/containerd/api/services/diff/v1" ssapi "github.com/containerd/containerd/api/services/snapshots/v1" "github.com/containerd/containerd/content/local" csproxy "github.com/containerd/containerd/content/proxy" "github.com/containerd/containerd/defaults" "github.com/containerd/containerd/diff" + diffproxy "github.com/containerd/containerd/diff/proxy" "github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/log" "github.com/containerd/containerd/pkg/dialer" @@ -409,6 +411,11 @@ func LoadPlugins(ctx context.Context, config *srvconfig.Config) ([]*plugin.Regis f = func(conn *grpc.ClientConn) interface{} { return csproxy.NewContentStore(csapi.NewContentClient(conn)) } + case string(plugin.DiffPlugin), "diff": + t = plugin.DiffPlugin + f = func(conn *grpc.ClientConn) interface{} { + return diffproxy.NewDiffApplier(diffapi.NewDiffClient(conn)) + } default: log.G(ctx).WithField("type", pp.Type).Warn("unknown proxy plugin type") }