123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- package plugin
- import (
- "context"
- "crypto/tls"
- "net"
- "net/http"
- "time"
- "github.com/containerd/containerd/remotes"
- "github.com/containerd/containerd/remotes/docker"
- "github.com/containerd/log"
- "github.com/distribution/reference"
- "github.com/docker/docker/api/types/registry"
- "github.com/docker/docker/dockerversion"
- "github.com/pkg/errors"
- )
- // 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 *registry.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 *registry.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" {
- log.G(context.TODO()).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
- }),
- ),
- })
- }
- log.G(context.TODO()).WithField("registryHost", hostname).WithField("hosts", hosts).Debug("Resolved registry hosts")
- return hosts, nil
- }
- }
|