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 }