github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/images/store.go (about)

     1  package images
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	"github.com/containerd/containerd/content"
     8  	c8derrdefs "github.com/containerd/containerd/errdefs"
     9  	"github.com/containerd/containerd/leases"
    10  	"github.com/containerd/containerd/log"
    11  	"github.com/containerd/containerd/namespaces"
    12  	"github.com/docker/docker/distribution"
    13  	"github.com/docker/docker/image"
    14  	"github.com/docker/docker/layer"
    15  	"github.com/opencontainers/go-digest"
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  func imageKey(dgst digest.Digest) string {
    21  	return "moby-image-" + dgst.String()
    22  }
    23  
    24  // imageStoreWithLease wraps the configured image store with one that deletes the lease
    25  // reigstered for a given image ID, if one exists
    26  //
    27  // This is used by the main image service to wrap delete calls to the real image store.
    28  type imageStoreWithLease struct {
    29  	image.Store
    30  	leases leases.Manager
    31  
    32  	// Normally we'd pass namespace down through a context.Context, however...
    33  	// The interface for image store doesn't allow this, so we store it here.
    34  	ns string
    35  }
    36  
    37  func (s *imageStoreWithLease) Delete(id image.ID) ([]layer.Metadata, error) {
    38  	ctx := namespaces.WithNamespace(context.TODO(), s.ns)
    39  	if err := s.leases.Delete(ctx, leases.Lease{ID: imageKey(digest.Digest(id))}); err != nil && !c8derrdefs.IsNotFound(err) {
    40  		return nil, errors.Wrap(err, "error deleting lease")
    41  	}
    42  	return s.Store.Delete(id)
    43  }
    44  
    45  // iamgeStoreForPull is created for each pull It wraps an underlying image store
    46  // to handle registering leases for content fetched in a single image pull.
    47  type imageStoreForPull struct {
    48  	distribution.ImageConfigStore
    49  	leases   leases.Manager
    50  	ingested *contentStoreForPull
    51  }
    52  
    53  func (s *imageStoreForPull) Put(ctx context.Context, config []byte) (digest.Digest, error) {
    54  	id, err := s.ImageConfigStore.Put(ctx, config)
    55  	if err != nil {
    56  		return "", err
    57  	}
    58  	return id, s.updateLease(ctx, id)
    59  }
    60  
    61  func (s *imageStoreForPull) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
    62  	id, err := s.ImageConfigStore.Get(ctx, dgst)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	return id, s.updateLease(ctx, dgst)
    67  }
    68  
    69  func (s *imageStoreForPull) updateLease(ctx context.Context, dgst digest.Digest) error {
    70  	leaseID := imageKey(dgst)
    71  	lease, err := s.leases.Create(ctx, leases.WithID(leaseID))
    72  	if err != nil {
    73  		if !c8derrdefs.IsAlreadyExists(err) {
    74  			return errors.Wrap(err, "error creating lease")
    75  		}
    76  		lease = leases.Lease{ID: leaseID}
    77  	}
    78  
    79  	digested := s.ingested.getDigested()
    80  	resource := leases.Resource{
    81  		Type: "content",
    82  	}
    83  	for _, dgst := range digested {
    84  		log.G(ctx).WithFields(logrus.Fields{
    85  			"digest": dgst,
    86  			"lease":  lease.ID,
    87  		}).Debug("Adding content digest to lease")
    88  
    89  		resource.ID = dgst.String()
    90  		if err := s.leases.AddResource(ctx, lease, resource); err != nil {
    91  			return errors.Wrapf(err, "error adding content digest to lease: %s", dgst)
    92  		}
    93  	}
    94  	return nil
    95  }
    96  
    97  // contentStoreForPull is used to wrap the configured content store to
    98  // add lease management for a single `pull`
    99  // It stores all committed digests so that `imageStoreForPull` can add
   100  // the digsted resources to the lease for an image.
   101  type contentStoreForPull struct {
   102  	distribution.ContentStore
   103  	leases leases.Manager
   104  
   105  	mu       sync.Mutex
   106  	digested []digest.Digest
   107  }
   108  
   109  func (c *contentStoreForPull) addDigested(dgst digest.Digest) {
   110  	c.mu.Lock()
   111  	c.digested = append(c.digested, dgst)
   112  	c.mu.Unlock()
   113  }
   114  
   115  func (c *contentStoreForPull) getDigested() []digest.Digest {
   116  	c.mu.Lock()
   117  	digested := make([]digest.Digest, len(c.digested))
   118  	copy(digested, c.digested)
   119  	c.mu.Unlock()
   120  	return digested
   121  }
   122  
   123  func (c *contentStoreForPull) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
   124  	w, err := c.ContentStore.Writer(ctx, opts...)
   125  	if err != nil {
   126  		if c8derrdefs.IsAlreadyExists(err) {
   127  			var cfg content.WriterOpts
   128  			for _, o := range opts {
   129  				if err := o(&cfg); err != nil {
   130  					return nil, err
   131  				}
   132  			}
   133  			c.addDigested(cfg.Desc.Digest)
   134  		}
   135  		return nil, err
   136  	}
   137  	return &contentWriter{
   138  		cs:     c,
   139  		Writer: w,
   140  	}, nil
   141  }
   142  
   143  type contentWriter struct {
   144  	cs *contentStoreForPull
   145  	content.Writer
   146  }
   147  
   148  func (w *contentWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error {
   149  	err := w.Writer.Commit(ctx, size, expected, opts...)
   150  	if err == nil || c8derrdefs.IsAlreadyExists(err) {
   151  		w.cs.addDigested(expected)
   152  	}
   153  	return err
   154  }