distribution/registry.go
694df3ff
 package distribution
 
 import (
8dce8e99
 	"fmt"
694df3ff
 	"net"
 	"net/http"
 	"time"
 
 	"github.com/docker/distribution"
3c7676a0
 	"github.com/docker/distribution/manifest/schema2"
3a127939
 	"github.com/docker/distribution/reference"
694df3ff
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/transport"
91e197d6
 	"github.com/docker/docker/api/types"
61a49bb6
 	"github.com/docker/docker/dockerversion"
694df3ff
 	"github.com/docker/docker/registry"
20702708
 	"github.com/docker/go-connections/sockets"
694df3ff
 	"golang.org/x/net/context"
 )
 
3c7676a0
 // ImageTypes represents the schema2 config types for images
 var ImageTypes = []string{
 	schema2.MediaTypeImageConfig,
 	// Handle unexpected values from https://github.com/docker/distribution/issues/1621
a215e15c
 	// (see also https://github.com/docker/docker/issues/22378,
 	// https://github.com/docker/docker/issues/30083)
3c7676a0
 	"application/octet-stream",
a215e15c
 	"application/json",
 	"text/html",
3c7676a0
 	// Treat defaulted values as images, newer types cannot be implied
 	"",
 }
 
 // PluginTypes represents the schema2 config types for plugins
 var PluginTypes = []string{
 	schema2.MediaTypePluginConfig,
 }
 
 var mediaTypeClasses map[string]string
 
 func init() {
 	// initialize media type classes with all know types for
 	// plugin
 	mediaTypeClasses = map[string]string{}
 	for _, t := range ImageTypes {
 		mediaTypeClasses[t] = "image"
 	}
 	for _, t := range PluginTypes {
 		mediaTypeClasses[t] = "plugin"
 	}
 }
 
c1be45fa
 // NewV2Repository returns a repository (v2 only). It creates an HTTP transport
694df3ff
 // providing timeout settings and authentication support, and also verifies the
 // remote API version.
a57478d6
 func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (repo distribution.Repository, foundVersion bool, err error) {
3a127939
 	repoName := repoInfo.Name.Name()
694df3ff
 	// If endpoint does not support CanonicalName, use the RemoteName instead
 	if endpoint.TrimHostname {
3a127939
 		repoName = reference.Path(repoInfo.Name)
694df3ff
 	}
 
20702708
 	direct := &net.Dialer{
 		Timeout:   30 * time.Second,
 		KeepAlive: 30 * time.Second,
 		DualStack: true,
 	}
 
694df3ff
 	// TODO(dmcgowan): Call close idle connections when complete, use keep alive
 	base := &http.Transport{
20702708
 		Proxy:               http.ProxyFromEnvironment,
 		Dial:                direct.Dial,
694df3ff
 		TLSHandshakeTimeout: 10 * time.Second,
 		TLSClientConfig:     endpoint.TLSConfig,
 		// TODO(dmcgowan): Call close idle connections when complete and use keep alive
 		DisableKeepAlives: true,
 	}
 
20702708
 	proxyDialer, err := sockets.DialerFromEnvironment(direct)
 	if err == nil {
 		base.Dial = proxyDialer.Dial
 	}
 
de5c80b4
 	modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders)
694df3ff
 	authTransport := transport.NewTransport(base, modifiers...)
5e8af46f
 
19d48f0b
 	challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport)
f2d481a2
 	if err != nil {
 		transportOK := false
 		if responseErr, ok := err.(registry.PingResponseError); ok {
 			transportOK = true
 			err = responseErr.Err
694df3ff
 		}
5e8af46f
 		return nil, foundVersion, fallbackError{
 			err:         err,
 			confirmedV2: foundVersion,
f2d481a2
 			transportOK: transportOK,
5e8af46f
 		}
694df3ff
 	}
 
8dce8e99
 	if authConfig.RegistryToken != "" {
 		passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
 		modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
 	} else {
a12b4661
 		scope := auth.RepositoryScope{
 			Repository: repoName,
 			Actions:    actions,
3d86b0c7
 			Class:      repoInfo.Class,
a12b4661
 		}
 
19d48f0b
 		creds := registry.NewStaticCredentialStore(authConfig)
e896d1d7
 		tokenHandlerOptions := auth.TokenHandlerOptions{
 			Transport:   authTransport,
 			Credentials: creds,
a12b4661
 			Scopes:      []auth.Scope{scope},
 			ClientID:    registry.AuthClientID,
e896d1d7
 		}
 		tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
8dce8e99
 		basicHandler := auth.NewBasicHandler(creds)
 		modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
 	}
694df3ff
 	tr := transport.NewTransport(base, modifiers...)
 
3a127939
 	repoNameRef, err := reference.WithName(repoName)
4d437a29
 	if err != nil {
5e8af46f
 		return nil, foundVersion, fallbackError{
 			err:         err,
 			confirmedV2: foundVersion,
 			transportOK: true,
 		}
4d437a29
 	}
 
79db131a
 	repo, err = client.NewRepository(ctx, repoNameRef, endpoint.URL.String(), tr)
5e8af46f
 	if err != nil {
 		err = fallbackError{
 			err:         err,
 			confirmedV2: foundVersion,
 			transportOK: true,
 		}
 	}
 	return
694df3ff
 }
 
8dce8e99
 type existingTokenHandler struct {
 	token string
 }
 
 func (th *existingTokenHandler) Scheme() string {
 	return "bearer"
 }
 
 func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
 	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))
 	return nil
 }