1package memory 2 3import ( 4 "context" 5 "sync" 6 7 "github.com/docker/distribution" 8 "github.com/docker/distribution/reference" 9 "github.com/docker/distribution/registry/storage/cache" 10 "github.com/opencontainers/go-digest" 11) 12 13type inMemoryBlobDescriptorCacheProvider struct { 14 global *mapBlobDescriptorCache 15 repositories map[string]*mapBlobDescriptorCache 16 mu sync.RWMutex 17} 18 19// NewInMemoryBlobDescriptorCacheProvider returns a new mapped-based cache for 20// storing blob descriptor data. 21func NewInMemoryBlobDescriptorCacheProvider() cache.BlobDescriptorCacheProvider { 22 return &inMemoryBlobDescriptorCacheProvider{ 23 global: newMapBlobDescriptorCache(), 24 repositories: make(map[string]*mapBlobDescriptorCache), 25 } 26} 27 28func (imbdcp *inMemoryBlobDescriptorCacheProvider) RepositoryScoped(repo string) (distribution.BlobDescriptorService, error) { 29 if _, err := reference.ParseNormalizedNamed(repo); err != nil { 30 return nil, err 31 } 32 33 imbdcp.mu.RLock() 34 defer imbdcp.mu.RUnlock() 35 36 return &repositoryScopedInMemoryBlobDescriptorCache{ 37 repo: repo, 38 parent: imbdcp, 39 repository: imbdcp.repositories[repo], 40 }, nil 41} 42 43func (imbdcp *inMemoryBlobDescriptorCacheProvider) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) { 44 return imbdcp.global.Stat(ctx, dgst) 45} 46 47func (imbdcp *inMemoryBlobDescriptorCacheProvider) Clear(ctx context.Context, dgst digest.Digest) error { 48 return imbdcp.global.Clear(ctx, dgst) 49} 50 51func (imbdcp *inMemoryBlobDescriptorCacheProvider) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error { 52 _, err := imbdcp.Stat(ctx, dgst) 53 if err == distribution.ErrBlobUnknown { 54 55 if dgst.Algorithm() != desc.Digest.Algorithm() && dgst != desc.Digest { 56 // if the digests differ, set the other canonical mapping 57 if err := imbdcp.global.SetDescriptor(ctx, desc.Digest, desc); err != nil { 58 return err 59 } 60 } 61 62 // unknown, just set it 63 return imbdcp.global.SetDescriptor(ctx, dgst, desc) 64 } 65 66 // we already know it, do nothing 67 return err 68} 69 70// repositoryScopedInMemoryBlobDescriptorCache provides the request scoped 71// repository cache. Instances are not thread-safe but the delegated 72// operations are. 73type repositoryScopedInMemoryBlobDescriptorCache struct { 74 repo string 75 parent *inMemoryBlobDescriptorCacheProvider // allows lazy allocation of repo's map 76 repository *mapBlobDescriptorCache 77} 78 79func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) { 80 rsimbdcp.parent.mu.Lock() 81 repo := rsimbdcp.repository 82 rsimbdcp.parent.mu.Unlock() 83 84 if repo == nil { 85 return distribution.Descriptor{}, distribution.ErrBlobUnknown 86 } 87 88 return repo.Stat(ctx, dgst) 89} 90 91func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) Clear(ctx context.Context, dgst digest.Digest) error { 92 rsimbdcp.parent.mu.Lock() 93 repo := rsimbdcp.repository 94 rsimbdcp.parent.mu.Unlock() 95 96 if repo == nil { 97 return distribution.ErrBlobUnknown 98 } 99 100 return repo.Clear(ctx, dgst) 101} 102 103func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error { 104 rsimbdcp.parent.mu.Lock() 105 repo := rsimbdcp.repository 106 if repo == nil { 107 // allocate map since we are setting it now. 108 var ok bool 109 // have to read back value since we may have allocated elsewhere. 110 repo, ok = rsimbdcp.parent.repositories[rsimbdcp.repo] 111 if !ok { 112 repo = newMapBlobDescriptorCache() 113 rsimbdcp.parent.repositories[rsimbdcp.repo] = repo 114 } 115 rsimbdcp.repository = repo 116 } 117 rsimbdcp.parent.mu.Unlock() 118 119 if err := repo.SetDescriptor(ctx, dgst, desc); err != nil { 120 return err 121 } 122 123 return rsimbdcp.parent.SetDescriptor(ctx, dgst, desc) 124} 125 126// mapBlobDescriptorCache provides a simple map-based implementation of the 127// descriptor cache. 128type mapBlobDescriptorCache struct { 129 descriptors map[digest.Digest]distribution.Descriptor 130 mu sync.RWMutex 131} 132 133var _ distribution.BlobDescriptorService = &mapBlobDescriptorCache{} 134 135func newMapBlobDescriptorCache() *mapBlobDescriptorCache { 136 return &mapBlobDescriptorCache{ 137 descriptors: make(map[digest.Digest]distribution.Descriptor), 138 } 139} 140 141func (mbdc *mapBlobDescriptorCache) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) { 142 if err := dgst.Validate(); err != nil { 143 return distribution.Descriptor{}, err 144 } 145 146 mbdc.mu.RLock() 147 defer mbdc.mu.RUnlock() 148 149 desc, ok := mbdc.descriptors[dgst] 150 if !ok { 151 return distribution.Descriptor{}, distribution.ErrBlobUnknown 152 } 153 154 return desc, nil 155} 156 157func (mbdc *mapBlobDescriptorCache) Clear(ctx context.Context, dgst digest.Digest) error { 158 mbdc.mu.Lock() 159 defer mbdc.mu.Unlock() 160 161 delete(mbdc.descriptors, dgst) 162 return nil 163} 164 165func (mbdc *mapBlobDescriptorCache) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error { 166 if err := dgst.Validate(); err != nil { 167 return err 168 } 169 170 if err := cache.ValidateDescriptor(desc); err != nil { 171 return err 172 } 173 174 mbdc.mu.Lock() 175 defer mbdc.mu.Unlock() 176 177 mbdc.descriptors[dgst] = desc 178 return nil 179} 180