d8fcbeeb71
The registry uses partial Named values which the named parsers no longer support. To allow the registry service to continue to operate without canonicalization, switch to use WithName. In the future, the registry should start using fully canonical values on the backend and WithName should no longer support creating partial values. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
180 lines
5.1 KiB
Go
180 lines
5.1 KiB
Go
package memory
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/docker/distribution"
|
|
"github.com/docker/distribution/context"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/distribution/registry/storage/cache"
|
|
"github.com/opencontainers/go-digest"
|
|
)
|
|
|
|
type inMemoryBlobDescriptorCacheProvider struct {
|
|
global *mapBlobDescriptorCache
|
|
repositories map[string]*mapBlobDescriptorCache
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewInMemoryBlobDescriptorCacheProvider returns a new mapped-based cache for
|
|
// storing blob descriptor data.
|
|
func NewInMemoryBlobDescriptorCacheProvider() cache.BlobDescriptorCacheProvider {
|
|
return &inMemoryBlobDescriptorCacheProvider{
|
|
global: newMapBlobDescriptorCache(),
|
|
repositories: make(map[string]*mapBlobDescriptorCache),
|
|
}
|
|
}
|
|
|
|
func (imbdcp *inMemoryBlobDescriptorCacheProvider) RepositoryScoped(repo string) (distribution.BlobDescriptorService, error) {
|
|
if _, err := reference.ParseNormalizedNamed(repo); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
imbdcp.mu.RLock()
|
|
defer imbdcp.mu.RUnlock()
|
|
|
|
return &repositoryScopedInMemoryBlobDescriptorCache{
|
|
repo: repo,
|
|
parent: imbdcp,
|
|
repository: imbdcp.repositories[repo],
|
|
}, nil
|
|
}
|
|
|
|
func (imbdcp *inMemoryBlobDescriptorCacheProvider) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
|
return imbdcp.global.Stat(ctx, dgst)
|
|
}
|
|
|
|
func (imbdcp *inMemoryBlobDescriptorCacheProvider) Clear(ctx context.Context, dgst digest.Digest) error {
|
|
return imbdcp.global.Clear(ctx, dgst)
|
|
}
|
|
|
|
func (imbdcp *inMemoryBlobDescriptorCacheProvider) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
|
_, err := imbdcp.Stat(ctx, dgst)
|
|
if err == distribution.ErrBlobUnknown {
|
|
|
|
if dgst.Algorithm() != desc.Digest.Algorithm() && dgst != desc.Digest {
|
|
// if the digests differ, set the other canonical mapping
|
|
if err := imbdcp.global.SetDescriptor(ctx, desc.Digest, desc); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// unknown, just set it
|
|
return imbdcp.global.SetDescriptor(ctx, dgst, desc)
|
|
}
|
|
|
|
// we already know it, do nothing
|
|
return err
|
|
}
|
|
|
|
// repositoryScopedInMemoryBlobDescriptorCache provides the request scoped
|
|
// repository cache. Instances are not thread-safe but the delegated
|
|
// operations are.
|
|
type repositoryScopedInMemoryBlobDescriptorCache struct {
|
|
repo string
|
|
parent *inMemoryBlobDescriptorCacheProvider // allows lazy allocation of repo's map
|
|
repository *mapBlobDescriptorCache
|
|
}
|
|
|
|
func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
|
rsimbdcp.parent.mu.Lock()
|
|
repo := rsimbdcp.repository
|
|
rsimbdcp.parent.mu.Unlock()
|
|
|
|
if repo == nil {
|
|
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
|
}
|
|
|
|
return repo.Stat(ctx, dgst)
|
|
}
|
|
|
|
func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) Clear(ctx context.Context, dgst digest.Digest) error {
|
|
rsimbdcp.parent.mu.Lock()
|
|
repo := rsimbdcp.repository
|
|
rsimbdcp.parent.mu.Unlock()
|
|
|
|
if repo == nil {
|
|
return distribution.ErrBlobUnknown
|
|
}
|
|
|
|
return repo.Clear(ctx, dgst)
|
|
}
|
|
|
|
func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
|
rsimbdcp.parent.mu.Lock()
|
|
repo := rsimbdcp.repository
|
|
if repo == nil {
|
|
// allocate map since we are setting it now.
|
|
var ok bool
|
|
// have to read back value since we may have allocated elsewhere.
|
|
repo, ok = rsimbdcp.parent.repositories[rsimbdcp.repo]
|
|
if !ok {
|
|
repo = newMapBlobDescriptorCache()
|
|
rsimbdcp.parent.repositories[rsimbdcp.repo] = repo
|
|
}
|
|
rsimbdcp.repository = repo
|
|
}
|
|
rsimbdcp.parent.mu.Unlock()
|
|
|
|
if err := repo.SetDescriptor(ctx, dgst, desc); err != nil {
|
|
return err
|
|
}
|
|
|
|
return rsimbdcp.parent.SetDescriptor(ctx, dgst, desc)
|
|
}
|
|
|
|
// mapBlobDescriptorCache provides a simple map-based implementation of the
|
|
// descriptor cache.
|
|
type mapBlobDescriptorCache struct {
|
|
descriptors map[digest.Digest]distribution.Descriptor
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
var _ distribution.BlobDescriptorService = &mapBlobDescriptorCache{}
|
|
|
|
func newMapBlobDescriptorCache() *mapBlobDescriptorCache {
|
|
return &mapBlobDescriptorCache{
|
|
descriptors: make(map[digest.Digest]distribution.Descriptor),
|
|
}
|
|
}
|
|
|
|
func (mbdc *mapBlobDescriptorCache) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
|
if err := dgst.Validate(); err != nil {
|
|
return distribution.Descriptor{}, err
|
|
}
|
|
|
|
mbdc.mu.RLock()
|
|
defer mbdc.mu.RUnlock()
|
|
|
|
desc, ok := mbdc.descriptors[dgst]
|
|
if !ok {
|
|
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
|
}
|
|
|
|
return desc, nil
|
|
}
|
|
|
|
func (mbdc *mapBlobDescriptorCache) Clear(ctx context.Context, dgst digest.Digest) error {
|
|
mbdc.mu.Lock()
|
|
defer mbdc.mu.Unlock()
|
|
|
|
delete(mbdc.descriptors, dgst)
|
|
return nil
|
|
}
|
|
|
|
func (mbdc *mapBlobDescriptorCache) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error {
|
|
if err := dgst.Validate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := cache.ValidateDescriptor(desc); err != nil {
|
|
return err
|
|
}
|
|
|
|
mbdc.mu.Lock()
|
|
defer mbdc.mu.Unlock()
|
|
|
|
mbdc.descriptors[dgst] = desc
|
|
return nil
|
|
}
|