1d33874951
Go 1.13 and up enforce import paths to be versioned if a project contains a go.mod and has released v2 or up. The current v2.x branches (and releases) do not yet have a go.mod, and therefore are still allowed to be imported with a non-versioned import path (go modules add a `+incompatible` annotation in that case). However, now that this project has a `go.mod` file, incompatible import paths will not be accepted by go modules, and attempting to use code from this repository will fail. This patch uses `v3` for the import-paths (not `v2`), because changing import paths itself is a breaking change, which means that the next release should increment the "major" version to comply with SemVer (as go modules dictate). Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
179 lines
5 KiB
Go
179 lines
5 KiB
Go
package memory
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/distribution/distribution/v3"
|
|
"github.com/distribution/distribution/v3/reference"
|
|
"github.com/distribution/distribution/v3/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
|
|
}
|