143 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
| package proxy
 | |
| 
 | |
| import (
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 
 | |
| 	"github.com/docker/distribution"
 | |
| 	"github.com/docker/distribution/configuration"
 | |
| 	"github.com/docker/distribution/context"
 | |
| 	"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)
 | |
| 	})
 | |
| 
 | |
| 	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)
 | |
| }
 | |
| 
 | |
| func (pr *proxyingRegistry) Repository(ctx context.Context, name string) (distribution.Repository, error) {
 | |
| 	tr := transport.NewTransport(http.DefaultTransport,
 | |
| 		auth.NewAuthorizer(pr.challengeManager, auth.NewTokenHandler(http.DefaultTransport, pr.credentialStore, name, "pull")))
 | |
| 
 | |
| 	localRepo, err := pr.embedded.Repository(ctx, name)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	localManifests, err := localRepo.Manifests(ctx, storage.SkipLayerVerification())
 | |
| 	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{
 | |
| 		blobStore: &proxyBlobStore{
 | |
| 			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,
 | |
| 		},
 | |
| 		name: name,
 | |
| 		tags: proxyTagService{
 | |
| 			localTags:  localRepo.Tags(ctx),
 | |
| 			remoteTags: remoteRepo.Tags(ctx),
 | |
| 		},
 | |
| 	}, 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 {
 | |
| 	blobStore distribution.BlobStore
 | |
| 	manifests distribution.ManifestService
 | |
| 	name      string
 | |
| 	tags      distribution.TagService
 | |
| }
 | |
| 
 | |
| 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
 | |
| }
 | |
| 
 | |
| func (pr *proxiedRepository) Name() string {
 | |
| 	return pr.name
 | |
| }
 | |
| 
 | |
| func (pr *proxiedRepository) Tags(ctx context.Context) distribution.TagService {
 | |
| 	return pr.tags
 | |
| }
 |