mirror of
https://github.com/distribution/distribution.git
synced 2025-08-19 15:28:16 +00:00
Merge 41e19fe9fe
into 1b01625dae
This commit is contained in:
commit
bddfc81175
@ -51,6 +51,10 @@ proxy:
|
|||||||
remoteurl: https://registry-1.docker.io
|
remoteurl: https://registry-1.docker.io
|
||||||
username: username
|
username: username
|
||||||
password: password
|
password: password
|
||||||
|
remotehostquerykey: ns
|
||||||
|
remotehostconfigmap:
|
||||||
|
registry.k8s.io:
|
||||||
|
remoteurl: https://registry.k8s.io
|
||||||
health:
|
health:
|
||||||
storagedriver:
|
storagedriver:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
@ -595,6 +595,9 @@ type Middleware struct {
|
|||||||
|
|
||||||
// Proxy configures the registry as a pull through cache
|
// Proxy configures the registry as a pull through cache
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
|
RemoteHostQueryKey string `yaml:"remotehostquerykey"`
|
||||||
|
RemoteHostConfigMap map[string]RemoteHostConfig `yaml:"remotehostconfigmap"`
|
||||||
|
|
||||||
// RemoteURL is the URL of the remote registry
|
// RemoteURL is the URL of the remote registry
|
||||||
RemoteURL string `yaml:"remoteurl"`
|
RemoteURL string `yaml:"remoteurl"`
|
||||||
|
|
||||||
@ -614,6 +617,19 @@ type Proxy struct {
|
|||||||
TTL *time.Duration `yaml:"ttl,omitempty"`
|
TTL *time.Duration `yaml:"ttl,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RemoteHostConfig struct {
|
||||||
|
RemoteURL string `yaml:"remoteurl"`
|
||||||
|
// Username of the hub user
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
|
||||||
|
// Password of the hub user
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
|
||||||
|
// Exec specifies a custom exec-based command to retrieve credentials.
|
||||||
|
// If set, Username and Password are ignored.
|
||||||
|
Exec *ExecConfig `yaml:"exec,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type ExecConfig struct {
|
type ExecConfig struct {
|
||||||
// Command is the command to execute.
|
// Command is the command to execute.
|
||||||
Command string `yaml:"command"`
|
Command string `yaml:"command"`
|
||||||
|
15
internal/dcontext/registry_host.go
Normal file
15
internal/dcontext/registry_host.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package dcontext
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type registryHostKey struct{}
|
||||||
|
|
||||||
|
func (registryHostKey) String() string { return "registryHost" }
|
||||||
|
|
||||||
|
func WithRegistryHost(ctx context.Context, host string) context.Context {
|
||||||
|
return context.WithValue(ctx, registryHostKey{}, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRegistryHost(ctx context.Context) string {
|
||||||
|
return GetStringValue(ctx, registryHostKey{})
|
||||||
|
}
|
@ -96,7 +96,7 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
Config: config,
|
Config: config,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
router: v2.RouterWithPrefix(config.HTTP.Prefix),
|
router: v2.RouterWithPrefix(config.HTTP.Prefix),
|
||||||
isCache: config.Proxy.RemoteURL != "",
|
isCache: config.Proxy.RemoteURL != "" || config.Proxy.RemoteHostQueryKey != "",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the handler dispatchers.
|
// Register the handler dispatchers.
|
||||||
@ -347,13 +347,12 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// configure as a pull through cache
|
// configure as a pull through cache
|
||||||
if config.Proxy.RemoteURL != "" {
|
if config.Proxy.RemoteURL != "" || config.Proxy.RemoteHostQueryKey != "" {
|
||||||
app.registry, err = proxy.NewRegistryPullThroughCache(ctx, app.registry, app.driver, config.Proxy)
|
app.registry, err = proxy.NewRegistryPullThroughCache(ctx, app.registry, app.driver, config.Proxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
app.isCache = true
|
app.isCache = true
|
||||||
dcontext.GetLogger(app).Info("Registry configured as a proxy cache to ", config.Proxy.RemoteURL)
|
|
||||||
}
|
}
|
||||||
var ok bool
|
var ok bool
|
||||||
app.repoRemover, ok = app.registry.(distribution.RepositoryRemover)
|
app.repoRemover, ok = app.registry.(distribution.RepositoryRemover)
|
||||||
@ -690,8 +689,13 @@ func (app *App) dispatcher(dispatch dispatchFunc) http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dcontext.WithValues(context)
|
||||||
|
|
||||||
// Add username to request logging
|
// Add username to request logging
|
||||||
context.Context = dcontext.WithLogger(context.Context, dcontext.GetLogger(context.Context, userNameKey))
|
context.Context = dcontext.WithLogger(context.Context, dcontext.GetLogger(context.Context, userNameKey))
|
||||||
|
if registryHost, ok := r.URL.Query()["ns"]; ok {
|
||||||
|
context.Context = dcontext.WithRegistryHost(context.Context, registryHost[0])
|
||||||
|
}
|
||||||
|
|
||||||
// sync up context on the request.
|
// sync up context on the request.
|
||||||
r = r.WithContext(context)
|
r = r.WithContext(context)
|
||||||
|
@ -29,17 +29,16 @@ type proxyingRegistry struct {
|
|||||||
embedded distribution.Namespace // provides local registry functionality
|
embedded distribution.Namespace // provides local registry functionality
|
||||||
scheduler *scheduler.TTLExpirationScheduler
|
scheduler *scheduler.TTLExpirationScheduler
|
||||||
ttl *time.Duration
|
ttl *time.Duration
|
||||||
remoteURL url.URL
|
defaultRemoteURL url.URL
|
||||||
authChallenger authChallenger
|
defaultAuthChallenger authChallenger
|
||||||
basicAuth auth.CredentialStore
|
defaultBasicAuth auth.CredentialStore
|
||||||
|
remoteURLMap map[string]url.URL
|
||||||
|
authChallengerMap map[string]authChallenger
|
||||||
|
basicAuthMap map[string]auth.CredentialStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRegistryPullThroughCache creates a registry acting as a pull through cache
|
// 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) {
|
func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Namespace, driver driver.StorageDriver, config configuration.Proxy) (distribution.Namespace, error) {
|
||||||
remoteURL, err := url.Parse(config.RemoteURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
v := storage.NewVacuum(ctx, driver)
|
v := storage.NewVacuum(ctx, driver)
|
||||||
|
|
||||||
@ -108,37 +107,70 @@ func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Name
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
err = s.Start()
|
err := s.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, b, err := func() (auth.CredentialStore, auth.CredentialStore, error) {
|
getAuth := func(username, password, remoteurl string, exec *configuration.ExecConfig) (auth.CredentialStore, auth.CredentialStore, error) {
|
||||||
switch {
|
switch {
|
||||||
case config.Exec != nil:
|
case exec != nil:
|
||||||
cs, err := configureExecAuth(*config.Exec)
|
cs, err := configureExecAuth(*exec)
|
||||||
return cs, cs, err
|
return cs, cs, err
|
||||||
default:
|
default:
|
||||||
return configureAuth(config.Username, config.Password, config.RemoteURL)
|
return configureAuth(username, password, remoteurl)
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &proxyingRegistry{
|
reg := &proxyingRegistry{
|
||||||
embedded: registry,
|
embedded: registry,
|
||||||
scheduler: s,
|
scheduler: s,
|
||||||
ttl: ttl,
|
ttl: ttl,
|
||||||
remoteURL: *remoteURL,
|
}
|
||||||
authChallenger: &remoteAuthChallenger{
|
if config.RemoteURL != "" {
|
||||||
|
remoteURL, err := url.Parse(config.RemoteURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cs, b, err := getAuth(config.Username, config.Password, config.RemoteURL, config.Exec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reg.defaultRemoteURL = *remoteURL
|
||||||
|
reg.defaultAuthChallenger = &remoteAuthChallenger{
|
||||||
remoteURL: *remoteURL,
|
remoteURL: *remoteURL,
|
||||||
cm: challenge.NewSimpleManager(),
|
cm: challenge.NewSimpleManager(),
|
||||||
cs: cs,
|
cs: cs,
|
||||||
},
|
}
|
||||||
basicAuth: b,
|
reg.defaultBasicAuth = b
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
if config.RemoteHostConfigMap != nil {
|
||||||
|
reg.remoteURLMap = make(map[string]url.URL)
|
||||||
|
reg.authChallengerMap = make(map[string]authChallenger)
|
||||||
|
reg.basicAuthMap = make(map[string]auth.CredentialStore)
|
||||||
|
for key, value := range config.RemoteHostConfigMap {
|
||||||
|
remoteURL, err := url.Parse(value.RemoteURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cs, b, err := getAuth(value.Username, value.Password, value.RemoteURL, value.Exec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reg.remoteURLMap[key] = *remoteURL
|
||||||
|
reg.authChallengerMap[key] = &remoteAuthChallenger{
|
||||||
|
remoteURL: *remoteURL,
|
||||||
|
cm: challenge.NewSimpleManager(),
|
||||||
|
cs: cs,
|
||||||
|
}
|
||||||
|
reg.basicAuthMap[key] = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pr *proxyingRegistry) Scope() distribution.Scope {
|
func (pr *proxyingRegistry) Scope() distribution.Scope {
|
||||||
@ -150,11 +182,26 @@ func (pr *proxyingRegistry) Repositories(ctx context.Context, repos []string, la
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named) (distribution.Repository, error) {
|
func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named) (distribution.Repository, error) {
|
||||||
c := pr.authChallenger
|
var err error
|
||||||
|
localRepoName := name
|
||||||
|
authChallenger := pr.defaultAuthChallenger
|
||||||
|
registryURL := pr.defaultRemoteURL
|
||||||
|
basicAuth := pr.defaultBasicAuth
|
||||||
|
if registryHost := dcontext.GetRegistryHost(ctx); registryHost != "" {
|
||||||
|
if _, ok := pr.remoteURLMap[registryHost]; ok {
|
||||||
|
localRepoName, err = reference.WithName(fmt.Sprintf("%s/%s", registryHost, name.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
registryURL = pr.remoteURLMap[registryHost]
|
||||||
|
authChallenger = pr.authChallengerMap[registryHost]
|
||||||
|
basicAuth = pr.basicAuthMap[registryHost]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tkopts := auth.TokenHandlerOptions{
|
tkopts := auth.TokenHandlerOptions{
|
||||||
Transport: http.DefaultTransport,
|
Transport: http.DefaultTransport,
|
||||||
Credentials: c.credentialStore(),
|
Credentials: authChallenger.credentialStore(),
|
||||||
Scopes: []auth.Scope{
|
Scopes: []auth.Scope{
|
||||||
auth.RepositoryScope{
|
auth.RepositoryScope{
|
||||||
Repository: name.Name(),
|
Repository: name.Name(),
|
||||||
@ -165,11 +212,11 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr := transport.NewTransport(http.DefaultTransport,
|
tr := transport.NewTransport(http.DefaultTransport,
|
||||||
auth.NewAuthorizer(c.challengeManager(),
|
auth.NewAuthorizer(authChallenger.challengeManager(),
|
||||||
auth.NewTokenHandlerWithOptions(tkopts),
|
auth.NewTokenHandlerWithOptions(tkopts),
|
||||||
auth.NewBasicHandler(pr.basicAuth)))
|
auth.NewBasicHandler(basicAuth)))
|
||||||
|
|
||||||
localRepo, err := pr.embedded.Repository(ctx, name)
|
localRepo, err := pr.embedded.Repository(ctx, localRepoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -178,7 +225,7 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteRepo, err := client.NewRepository(name, pr.remoteURL.String(), tr)
|
remoteRepo, err := client.NewRepository(name, registryURL.String(), tr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -195,7 +242,7 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named
|
|||||||
scheduler: pr.scheduler,
|
scheduler: pr.scheduler,
|
||||||
ttl: pr.ttl,
|
ttl: pr.ttl,
|
||||||
repositoryName: name,
|
repositoryName: name,
|
||||||
authChallenger: pr.authChallenger,
|
authChallenger: authChallenger,
|
||||||
},
|
},
|
||||||
manifests: &proxyManifestStore{
|
manifests: &proxyManifestStore{
|
||||||
repositoryName: name,
|
repositoryName: name,
|
||||||
@ -204,13 +251,13 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named
|
|||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
scheduler: pr.scheduler,
|
scheduler: pr.scheduler,
|
||||||
ttl: pr.ttl,
|
ttl: pr.ttl,
|
||||||
authChallenger: pr.authChallenger,
|
authChallenger: authChallenger,
|
||||||
},
|
},
|
||||||
name: name,
|
name: name,
|
||||||
tags: &proxyTagService{
|
tags: &proxyTagService{
|
||||||
localTags: localRepo.Tags(ctx),
|
localTags: localRepo.Tags(ctx),
|
||||||
remoteTags: remoteRepo.Tags(ctx),
|
remoteTags: remoteRepo.Tags(ctx),
|
||||||
authChallenger: pr.authChallenger,
|
authChallenger: authChallenger,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user