2015-07-29 20:12:01 +02:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
|
|
|
|
"github.com/docker/distribution"
|
|
|
|
"github.com/docker/distribution/configuration"
|
|
|
|
"github.com/docker/distribution/context"
|
2015-12-15 23:35:23 +01:00
|
|
|
"github.com/docker/distribution/reference"
|
2015-07-29 20:12:01 +02:00
|
|
|
"github.com/docker/distribution/registry/client"
|
|
|
|
"github.com/docker/distribution/registry/client/auth"
|
|
|
|
"github.com/docker/distribution/registry/client/transport"
|
|
|
|
"github.com/docker/distribution/registry/proxy/scheduler"
|
|
|
|
"github.com/docker/distribution/registry/storage"
|
|
|
|
"github.com/docker/distribution/registry/storage/driver"
|
|
|
|
)
|
|
|
|
|
|
|
|
// proxyingRegistry fetches content from a remote registry and caches it locally
|
|
|
|
type proxyingRegistry struct {
|
|
|
|
embedded distribution.Namespace // provides local registry functionality
|
|
|
|
|
|
|
|
scheduler *scheduler.TTLExpirationScheduler
|
|
|
|
|
|
|
|
remoteURL string
|
|
|
|
credentialStore auth.CredentialStore
|
|
|
|
challengeManager auth.ChallengeManager
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRegistryPullThroughCache creates a registry acting as a pull through cache
|
|
|
|
func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Namespace, driver driver.StorageDriver, config configuration.Proxy) (distribution.Namespace, error) {
|
|
|
|
_, err := url.Parse(config.RemoteURL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
v := storage.NewVacuum(ctx, driver)
|
|
|
|
|
|
|
|
s := scheduler.New(ctx, driver, "/scheduler-state.json")
|
|
|
|
s.OnBlobExpire(func(digest string) error {
|
|
|
|
return v.RemoveBlob(digest)
|
|
|
|
})
|
|
|
|
s.OnManifestExpire(func(repoName string) error {
|
|
|
|
return v.RemoveRepository(repoName)
|
|
|
|
})
|
2015-08-21 06:50:15 +02:00
|
|
|
|
2015-07-29 20:12:01 +02:00
|
|
|
err = s.Start()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
challengeManager := auth.NewSimpleChallengeManager()
|
|
|
|
cs, err := ConfigureAuth(config.RemoteURL, config.Username, config.Password, challengeManager)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &proxyingRegistry{
|
|
|
|
embedded: registry,
|
|
|
|
scheduler: s,
|
|
|
|
challengeManager: challengeManager,
|
|
|
|
credentialStore: cs,
|
|
|
|
remoteURL: config.RemoteURL,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pr *proxyingRegistry) Scope() distribution.Scope {
|
|
|
|
return distribution.GlobalScope
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pr *proxyingRegistry) Repositories(ctx context.Context, repos []string, last string) (n int, err error) {
|
|
|
|
return pr.embedded.Repositories(ctx, repos, last)
|
|
|
|
}
|
|
|
|
|
2015-12-15 23:35:23 +01:00
|
|
|
func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named) (distribution.Repository, error) {
|
2015-07-29 20:12:01 +02:00
|
|
|
tr := transport.NewTransport(http.DefaultTransport,
|
2015-12-15 23:35:23 +01:00
|
|
|
auth.NewAuthorizer(pr.challengeManager, auth.NewTokenHandler(http.DefaultTransport, pr.credentialStore, name.Name(), "pull")))
|
2015-07-29 20:12:01 +02:00
|
|
|
|
|
|
|
localRepo, err := pr.embedded.Repository(ctx, name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-08-21 06:50:15 +02:00
|
|
|
localManifests, err := localRepo.Manifests(ctx, storage.SkipLayerVerification())
|
2015-07-29 20:12:01 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
remoteRepo, err := client.NewRepository(ctx, name, pr.remoteURL, tr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
remoteManifests, err := remoteRepo.Manifests(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &proxiedRepository{
|
2015-09-19 01:11:35 +02:00
|
|
|
blobStore: &proxyBlobStore{
|
2015-07-29 20:12:01 +02:00
|
|
|
localStore: localRepo.Blobs(ctx),
|
|
|
|
remoteStore: remoteRepo.Blobs(ctx),
|
|
|
|
scheduler: pr.scheduler,
|
|
|
|
},
|
|
|
|
manifests: proxyManifestStore{
|
|
|
|
repositoryName: name,
|
|
|
|
localManifests: localManifests, // Options?
|
|
|
|
remoteManifests: remoteManifests,
|
|
|
|
ctx: ctx,
|
|
|
|
scheduler: pr.scheduler,
|
|
|
|
},
|
2015-08-21 06:50:15 +02:00
|
|
|
name: name,
|
|
|
|
tags: proxyTagService{
|
|
|
|
localTags: localRepo.Tags(ctx),
|
|
|
|
remoteTags: remoteRepo.Tags(ctx),
|
|
|
|
},
|
2015-07-29 20:12:01 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// proxiedRepository uses proxying blob and manifest services to serve content
|
|
|
|
// locally, or pulling it through from a remote and caching it locally if it doesn't
|
|
|
|
// already exist
|
|
|
|
type proxiedRepository struct {
|
2015-08-21 06:50:15 +02:00
|
|
|
blobStore distribution.BlobStore
|
|
|
|
manifests distribution.ManifestService
|
2015-12-15 23:35:23 +01:00
|
|
|
name reference.Named
|
2015-08-21 06:50:15 +02:00
|
|
|
tags distribution.TagService
|
2015-07-29 20:12:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (pr *proxiedRepository) Manifests(ctx context.Context, options ...distribution.ManifestServiceOption) (distribution.ManifestService, error) {
|
|
|
|
return pr.manifests, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pr *proxiedRepository) Blobs(ctx context.Context) distribution.BlobStore {
|
|
|
|
return pr.blobStore
|
|
|
|
}
|
|
|
|
|
2015-12-15 23:35:23 +01:00
|
|
|
func (pr *proxiedRepository) Name() reference.Named {
|
2015-07-29 20:12:01 +02:00
|
|
|
return pr.name
|
|
|
|
}
|
|
|
|
|
2015-08-21 06:50:15 +02:00
|
|
|
func (pr *proxiedRepository) Tags(ctx context.Context) distribution.TagService {
|
|
|
|
return pr.tags
|
2015-07-29 20:12:01 +02:00
|
|
|
}
|