github.com/containerd/Containerd@v1.4.13/services/diff/local.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package diff
    18  
    19  import (
    20  	"context"
    21  
    22  	diffapi "github.com/containerd/containerd/api/services/diff/v1"
    23  	"github.com/containerd/containerd/api/types"
    24  	"github.com/containerd/containerd/diff"
    25  	"github.com/containerd/containerd/errdefs"
    26  	"github.com/containerd/containerd/mount"
    27  	"github.com/containerd/containerd/plugin"
    28  	"github.com/containerd/containerd/services"
    29  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    30  	"github.com/pkg/errors"
    31  	"google.golang.org/grpc"
    32  )
    33  
    34  type config struct {
    35  	// Order is the order of preference in which to try diff algorithms, the
    36  	// first differ which is supported is used.
    37  	// Note when multiple differs may be supported, this order will be
    38  	// respected for which is chosen. Each differ should return the same
    39  	// correct output, allowing any ordering to be used to prefer
    40  	// more optimimal implementations.
    41  	Order []string `toml:"default"`
    42  }
    43  
    44  type differ interface {
    45  	diff.Comparer
    46  	diff.Applier
    47  }
    48  
    49  func init() {
    50  	plugin.Register(&plugin.Registration{
    51  		Type: plugin.ServicePlugin,
    52  		ID:   services.DiffService,
    53  		Requires: []plugin.Type{
    54  			plugin.DiffPlugin,
    55  		},
    56  		Config: defaultDifferConfig,
    57  		InitFn: func(ic *plugin.InitContext) (interface{}, error) {
    58  			differs, err := ic.GetByType(plugin.DiffPlugin)
    59  			if err != nil {
    60  				return nil, err
    61  			}
    62  
    63  			orderedNames := ic.Config.(*config).Order
    64  			ordered := make([]differ, len(orderedNames))
    65  			for i, n := range orderedNames {
    66  				differp, ok := differs[n]
    67  				if !ok {
    68  					return nil, errors.Errorf("needed differ not loaded: %s", n)
    69  				}
    70  				d, err := differp.Instance()
    71  				if err != nil {
    72  					return nil, errors.Wrapf(err, "could not load required differ due plugin init error: %s", n)
    73  				}
    74  
    75  				ordered[i], ok = d.(differ)
    76  				if !ok {
    77  					return nil, errors.Errorf("differ does not implement Comparer and Applier interface: %s", n)
    78  				}
    79  			}
    80  
    81  			return &local{
    82  				differs: ordered,
    83  			}, nil
    84  		},
    85  	})
    86  }
    87  
    88  type local struct {
    89  	differs []differ
    90  }
    91  
    92  var _ diffapi.DiffClient = &local{}
    93  
    94  func (l *local) Apply(ctx context.Context, er *diffapi.ApplyRequest, _ ...grpc.CallOption) (*diffapi.ApplyResponse, error) {
    95  	var (
    96  		ocidesc ocispec.Descriptor
    97  		err     error
    98  		desc    = toDescriptor(er.Diff)
    99  		mounts  = toMounts(er.Mounts)
   100  	)
   101  
   102  	var opts []diff.ApplyOpt
   103  	if er.Payloads != nil {
   104  		opts = append(opts, diff.WithPayloads(er.Payloads))
   105  	}
   106  
   107  	for _, differ := range l.differs {
   108  		ocidesc, err = differ.Apply(ctx, desc, mounts, opts...)
   109  		if !errdefs.IsNotImplemented(err) {
   110  			break
   111  		}
   112  	}
   113  
   114  	if err != nil {
   115  		return nil, errdefs.ToGRPC(err)
   116  	}
   117  
   118  	return &diffapi.ApplyResponse{
   119  		Applied: fromDescriptor(ocidesc),
   120  	}, nil
   121  
   122  }
   123  
   124  func (l *local) Diff(ctx context.Context, dr *diffapi.DiffRequest, _ ...grpc.CallOption) (*diffapi.DiffResponse, error) {
   125  	var (
   126  		ocidesc ocispec.Descriptor
   127  		err     error
   128  		aMounts = toMounts(dr.Left)
   129  		bMounts = toMounts(dr.Right)
   130  	)
   131  
   132  	var opts []diff.Opt
   133  	if dr.MediaType != "" {
   134  		opts = append(opts, diff.WithMediaType(dr.MediaType))
   135  	}
   136  	if dr.Ref != "" {
   137  		opts = append(opts, diff.WithReference(dr.Ref))
   138  	}
   139  	if dr.Labels != nil {
   140  		opts = append(opts, diff.WithLabels(dr.Labels))
   141  	}
   142  
   143  	for _, d := range l.differs {
   144  		ocidesc, err = d.Compare(ctx, aMounts, bMounts, opts...)
   145  		if !errdefs.IsNotImplemented(err) {
   146  			break
   147  		}
   148  	}
   149  	if err != nil {
   150  		return nil, errdefs.ToGRPC(err)
   151  	}
   152  
   153  	return &diffapi.DiffResponse{
   154  		Diff: fromDescriptor(ocidesc),
   155  	}, nil
   156  }
   157  
   158  func toMounts(apim []*types.Mount) []mount.Mount {
   159  	mounts := make([]mount.Mount, len(apim))
   160  	for i, m := range apim {
   161  		mounts[i] = mount.Mount{
   162  			Type:    m.Type,
   163  			Source:  m.Source,
   164  			Options: m.Options,
   165  		}
   166  	}
   167  	return mounts
   168  }
   169  
   170  func toDescriptor(d *types.Descriptor) ocispec.Descriptor {
   171  	return ocispec.Descriptor{
   172  		MediaType:   d.MediaType,
   173  		Digest:      d.Digest,
   174  		Size:        d.Size_,
   175  		Annotations: d.Annotations,
   176  	}
   177  }
   178  
   179  func fromDescriptor(d ocispec.Descriptor) *types.Descriptor {
   180  	return &types.Descriptor{
   181  		MediaType:   d.MediaType,
   182  		Digest:      d.Digest,
   183  		Size_:       d.Size,
   184  		Annotations: d.Annotations,
   185  	}
   186  }