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