| package plugin |
| |
| import ( |
| "context" |
| "crypto/tls" |
| "net" |
| "net/http" |
| "time" |
| |
| "github.com/sirupsen/logrus" |
| |
| "github.com/docker/docker/dockerversion" |
| |
| "github.com/pkg/errors" |
| |
| "github.com/containerd/containerd/remotes" |
| "github.com/containerd/containerd/remotes/docker" |
| "github.com/docker/distribution/reference" |
| "github.com/docker/docker/api/types" |
| ) |
| |
| // scope builds the correct auth scope for the registry client to authorize against |
| // By default the client currently only does a "repository:" scope with out a classifier, e.g. "(plugin)" |
| // Without this, the client will not be able to authorize the request |
| func scope(ref reference.Named, push bool) string { |
| scope := "repository(plugin):" + reference.Path(reference.TrimNamed(ref)) + ":pull" |
| if push { |
| scope += ",push" |
| } |
| return scope |
| } |
| |
| func (pm *Manager) newResolver(ctx context.Context, tracker docker.StatusTracker, auth *types.AuthConfig, headers http.Header, httpFallback bool) (remotes.Resolver, error) { |
| if headers == nil { |
| headers = http.Header{} |
| } |
| headers.Add("User-Agent", dockerversion.DockerUserAgent(ctx)) |
| |
| return docker.NewResolver(docker.ResolverOptions{ |
| Tracker: tracker, |
| Headers: headers, |
| Hosts: pm.registryHostsFn(auth, httpFallback), |
| }), nil |
| } |
| |
| func registryHTTPClient(config *tls.Config) *http.Client { |
| return &http.Client{ |
| Transport: &http.Transport{ |
| Proxy: http.ProxyFromEnvironment, |
| DialContext: (&net.Dialer{ |
| Timeout: 30 * time.Second, |
| KeepAlive: 30 * time.Second, |
| }).DialContext, |
| TLSClientConfig: config, |
| TLSHandshakeTimeout: 10 * time.Second, |
| IdleConnTimeout: 30 * time.Second, |
| }, |
| } |
| } |
| |
| func (pm *Manager) registryHostsFn(auth *types.AuthConfig, httpFallback bool) docker.RegistryHosts { |
| return func(hostname string) ([]docker.RegistryHost, error) { |
| eps, err := pm.config.RegistryService.LookupPullEndpoints(hostname) |
| if err != nil { |
| return nil, errors.Wrapf(err, "error resolving repository for %s", hostname) |
| } |
| |
| hosts := make([]docker.RegistryHost, 0, len(eps)) |
| |
| for _, ep := range eps { |
| // forced http fallback is used only for push since the containerd pusher only ever uses the first host we |
| // pass to it. |
| // So it is the callers responsibility to retry with this flag set. |
| if httpFallback && ep.URL.Scheme != "http" { |
| logrus.WithField("registryHost", hostname).WithField("endpoint", ep).Debugf("Skipping non-http endpoint") |
| continue |
| } |
| |
| caps := docker.HostCapabilityPull | docker.HostCapabilityResolve |
| if !ep.Mirror { |
| caps = caps | docker.HostCapabilityPush |
| } |
| |
| host, err := docker.DefaultHost(ep.URL.Host) |
| if err != nil { |
| return nil, err |
| } |
| |
| client := registryHTTPClient(ep.TLSConfig) |
| hosts = append(hosts, docker.RegistryHost{ |
| Host: host, |
| Scheme: ep.URL.Scheme, |
| Client: client, |
| Path: "/v2", |
| Capabilities: caps, |
| Authorizer: docker.NewDockerAuthorizer( |
| docker.WithAuthClient(client), |
| docker.WithAuthCreds(func(_ string) (string, string, error) { |
| if auth.IdentityToken != "" { |
| return "", auth.IdentityToken, nil |
| } |
| return auth.Username, auth.Password, nil |
| }), |
| ), |
| }) |
| } |
| logrus.WithField("registryHost", hostname).WithField("hosts", hosts).Debug("Resolved registry hosts") |
| |
| return hosts, nil |
| } |
| } |