31047c8113
The current implementation of digest.FromBytes returns an error. This error can never be non-nil, but its presence in the function signature means each call site needs error handling code for an error that is always nil. I verified that none of the hash.Hash implementations in the standard library can return an error on Write. Nor can any of the hash.Hash implementations vendored in distribution. This commit changes digest.FromBytes not to return an error. If Write returns an error, it will panic, but as discussed above, this should never happen. This commit also avoids using a bytes.Reader to feed data into the hash function in FromBytes. This makes the hypothetical case that would panic a bit more explicit, and should also be more performant. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
150 lines
3.7 KiB
Go
150 lines
3.7 KiB
Go
package proxy
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/docker/distribution"
|
|
"github.com/docker/distribution/context"
|
|
"github.com/docker/distribution/digest"
|
|
"github.com/docker/distribution/manifest/schema1"
|
|
"github.com/docker/distribution/registry/client"
|
|
"github.com/docker/distribution/registry/proxy/scheduler"
|
|
)
|
|
|
|
// todo(richardscothern): from cache control header or config
|
|
const repositoryTTL = time.Duration(24 * 7 * time.Hour)
|
|
|
|
type proxyManifestStore struct {
|
|
ctx context.Context
|
|
localManifests distribution.ManifestService
|
|
remoteManifests distribution.ManifestService
|
|
repositoryName string
|
|
scheduler *scheduler.TTLExpirationScheduler
|
|
}
|
|
|
|
var _ distribution.ManifestService = &proxyManifestStore{}
|
|
|
|
func (pms proxyManifestStore) Exists(dgst digest.Digest) (bool, error) {
|
|
exists, err := pms.localManifests.Exists(dgst)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if exists {
|
|
return true, nil
|
|
}
|
|
|
|
return pms.remoteManifests.Exists(dgst)
|
|
}
|
|
|
|
func (pms proxyManifestStore) Get(dgst digest.Digest) (*schema1.SignedManifest, error) {
|
|
sm, err := pms.localManifests.Get(dgst)
|
|
if err == nil {
|
|
proxyMetrics.ManifestPush(uint64(len(sm.Raw)))
|
|
return sm, err
|
|
}
|
|
|
|
sm, err = pms.remoteManifests.Get(dgst)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
proxyMetrics.ManifestPull(uint64(len(sm.Raw)))
|
|
err = pms.localManifests.Put(sm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Schedule the repo for removal
|
|
pms.scheduler.AddManifest(pms.repositoryName, repositoryTTL)
|
|
|
|
// Ensure the manifest blob is cleaned up
|
|
pms.scheduler.AddBlob(dgst.String(), repositoryTTL)
|
|
|
|
proxyMetrics.ManifestPush(uint64(len(sm.Raw)))
|
|
|
|
return sm, err
|
|
}
|
|
|
|
func (pms proxyManifestStore) Tags() ([]string, error) {
|
|
return pms.localManifests.Tags()
|
|
}
|
|
|
|
func (pms proxyManifestStore) ExistsByTag(tag string) (bool, error) {
|
|
exists, err := pms.localManifests.ExistsByTag(tag)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if exists {
|
|
return true, nil
|
|
}
|
|
|
|
return pms.remoteManifests.ExistsByTag(tag)
|
|
}
|
|
|
|
func (pms proxyManifestStore) GetByTag(tag string, options ...distribution.ManifestServiceOption) (*schema1.SignedManifest, error) {
|
|
var localDigest digest.Digest
|
|
|
|
localManifest, err := pms.localManifests.GetByTag(tag, options...)
|
|
switch err.(type) {
|
|
case distribution.ErrManifestUnknown, distribution.ErrManifestUnknownRevision:
|
|
goto fromremote
|
|
case nil:
|
|
break
|
|
default:
|
|
return nil, err
|
|
}
|
|
|
|
localDigest, err = manifestDigest(localManifest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fromremote:
|
|
var sm *schema1.SignedManifest
|
|
sm, err = pms.remoteManifests.GetByTag(tag, client.AddEtagToTag(tag, localDigest.String()))
|
|
if err != nil && err != distribution.ErrManifestNotModified {
|
|
return nil, err
|
|
}
|
|
|
|
if err == distribution.ErrManifestNotModified {
|
|
context.GetLogger(pms.ctx).Debugf("Local manifest for %q is latest, dgst=%s", tag, localDigest.String())
|
|
return localManifest, nil
|
|
}
|
|
context.GetLogger(pms.ctx).Debugf("Updated manifest for %q, dgst=%s", tag, localDigest.String())
|
|
|
|
err = pms.localManifests.Put(sm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dgst, err := manifestDigest(sm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pms.scheduler.AddBlob(dgst.String(), repositoryTTL)
|
|
pms.scheduler.AddManifest(pms.repositoryName, repositoryTTL)
|
|
|
|
proxyMetrics.ManifestPull(uint64(len(sm.Raw)))
|
|
proxyMetrics.ManifestPush(uint64(len(sm.Raw)))
|
|
|
|
return sm, err
|
|
}
|
|
|
|
func manifestDigest(sm *schema1.SignedManifest) (digest.Digest, error) {
|
|
payload, err := sm.Payload()
|
|
if err != nil {
|
|
return "", err
|
|
|
|
}
|
|
|
|
return digest.FromBytes(payload), nil
|
|
}
|
|
|
|
func (pms proxyManifestStore) Put(manifest *schema1.SignedManifest) error {
|
|
return distribution.ErrUnsupported
|
|
}
|
|
|
|
func (pms proxyManifestStore) Delete(dgst digest.Digest) error {
|
|
return distribution.ErrUnsupported
|
|
}
|