d7ba1f85ef
This removes the use of the old distribution code in the plugin packages and replaces it with containerd libraries for plugin pushes and pulls. Additionally it uses a content store from containerd which seems like it's compatible with the old "basicBlobStore" in the plugin package. This is being used locally isntead of through the containerd client for now. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
111 lines
3.2 KiB
Go
111 lines
3.2 KiB
Go
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
|
|
}
|
|
}
|