github.com/containerd/Containerd@v1.4.13/diff/apply/apply.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 apply
    18  
    19  import (
    20  	"context"
    21  	"io"
    22  	"io/ioutil"
    23  	"time"
    24  
    25  	"github.com/containerd/containerd/content"
    26  	"github.com/containerd/containerd/diff"
    27  	"github.com/containerd/containerd/log"
    28  	"github.com/containerd/containerd/mount"
    29  	digest "github.com/opencontainers/go-digest"
    30  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    31  	"github.com/pkg/errors"
    32  	"github.com/sirupsen/logrus"
    33  )
    34  
    35  // NewFileSystemApplier returns an applier which simply mounts
    36  // and applies diff onto the mounted filesystem.
    37  func NewFileSystemApplier(cs content.Provider) diff.Applier {
    38  	return &fsApplier{
    39  		store: cs,
    40  	}
    41  }
    42  
    43  type fsApplier struct {
    44  	store content.Provider
    45  }
    46  
    47  var emptyDesc = ocispec.Descriptor{}
    48  
    49  // Apply applies the content associated with the provided digests onto the
    50  // provided mounts. Archive content will be extracted and decompressed if
    51  // necessary.
    52  func (s *fsApplier) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount, opts ...diff.ApplyOpt) (d ocispec.Descriptor, err error) {
    53  	t1 := time.Now()
    54  	defer func() {
    55  		if err == nil {
    56  			log.G(ctx).WithFields(logrus.Fields{
    57  				"d":     time.Since(t1),
    58  				"dgst":  desc.Digest,
    59  				"size":  desc.Size,
    60  				"media": desc.MediaType,
    61  			}).Debugf("diff applied")
    62  		}
    63  	}()
    64  
    65  	var config diff.ApplyConfig
    66  	for _, o := range opts {
    67  		if err := o(ctx, desc, &config); err != nil {
    68  			return emptyDesc, errors.Wrap(err, "failed to apply config opt")
    69  		}
    70  	}
    71  
    72  	ra, err := s.store.ReaderAt(ctx, desc)
    73  	if err != nil {
    74  		return emptyDesc, errors.Wrap(err, "failed to get reader from content store")
    75  	}
    76  	defer ra.Close()
    77  
    78  	var processors []diff.StreamProcessor
    79  	processor := diff.NewProcessorChain(desc.MediaType, content.NewReader(ra))
    80  	processors = append(processors, processor)
    81  	for {
    82  		if processor, err = diff.GetProcessor(ctx, processor, config.ProcessorPayloads); err != nil {
    83  			return emptyDesc, errors.Wrapf(err, "failed to get stream processor for %s", desc.MediaType)
    84  		}
    85  		processors = append(processors, processor)
    86  		if processor.MediaType() == ocispec.MediaTypeImageLayer {
    87  			break
    88  		}
    89  	}
    90  	defer processor.Close()
    91  
    92  	digester := digest.Canonical.Digester()
    93  	rc := &readCounter{
    94  		r: io.TeeReader(processor, digester.Hash()),
    95  	}
    96  
    97  	if err := apply(ctx, mounts, rc); err != nil {
    98  		return emptyDesc, err
    99  	}
   100  
   101  	// Read any trailing data
   102  	if _, err := io.Copy(ioutil.Discard, rc); err != nil {
   103  		return emptyDesc, err
   104  	}
   105  
   106  	for _, p := range processors {
   107  		if ep, ok := p.(interface {
   108  			Err() error
   109  		}); ok {
   110  			if err := ep.Err(); err != nil {
   111  				return emptyDesc, err
   112  			}
   113  		}
   114  	}
   115  	return ocispec.Descriptor{
   116  		MediaType: ocispec.MediaTypeImageLayer,
   117  		Size:      rc.c,
   118  		Digest:    digester.Digest(),
   119  	}, nil
   120  }
   121  
   122  type readCounter struct {
   123  	r io.Reader
   124  	c int64
   125  }
   126  
   127  func (rc *readCounter) Read(p []byte) (n int, err error) {
   128  	n, err = rc.r.Read(p)
   129  	rc.c += int64(n)
   130  	return
   131  }