9 "github.com/docker/distribution"
10 "github.com/docker/distribution/manifest/schema2"
11 "github.com/docker/distribution/reference"
12 "github.com/docker/distribution/registry/client"
13 "github.com/docker/distribution/registry/client/auth"
14 "github.com/docker/distribution/registry/client/transport"
15 "github.com/docker/docker/api/types"
16 "github.com/docker/docker/dockerversion"
17 "github.com/docker/docker/registry"
18 "github.com/docker/go-connections/sockets"
19 "golang.org/x/net/context"
22 // ImageTypes represents the schema2 config types for images
23 var ImageTypes = []string{
24 schema2.MediaTypeImageConfig,
25 // Handle unexpected values from https://github.com/docker/distribution/issues/1621
26 // (see also https://github.com/docker/docker/issues/22378,
27 // https://github.com/docker/docker/issues/30083)
28 "application/octet-stream",
31 // Treat defaulted values as images, newer types cannot be implied
35 // PluginTypes represents the schema2 config types for plugins
36 var PluginTypes = []string{
37 schema2.MediaTypePluginConfig,
40 var mediaTypeClasses map[string]string
43 // initialize media type classes with all know types for
45 mediaTypeClasses = map[string]string{}
46 for _, t := range ImageTypes {
47 mediaTypeClasses[t] = "image"
49 for _, t := range PluginTypes {
50 mediaTypeClasses[t] = "plugin"
54 // NewV2Repository returns a repository (v2 only). It creates an HTTP transport
55 // providing timeout settings and authentication support, and also verifies the
56 // remote API version.
57 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) {
58 repoName := repoInfo.Name.Name()
59 // If endpoint does not support CanonicalName, use the RemoteName instead
60 if endpoint.TrimHostname {
61 repoName = reference.Path(repoInfo.Name)
64 direct := &net.Dialer{
65 Timeout: 30 * time.Second,
66 KeepAlive: 30 * time.Second,
70 // TODO(dmcgowan): Call close idle connections when complete, use keep alive
71 base := &http.Transport{
72 Proxy: http.ProxyFromEnvironment,
74 TLSHandshakeTimeout: 10 * time.Second,
75 TLSClientConfig: endpoint.TLSConfig,
76 // TODO(dmcgowan): Call close idle connections when complete and use keep alive
77 DisableKeepAlives: true,
80 proxyDialer, err := sockets.DialerFromEnvironment(direct)
82 base.Dial = proxyDialer.Dial
85 modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), metaHeaders)
86 authTransport := transport.NewTransport(base, modifiers...)
88 challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport)
91 if responseErr, ok := err.(registry.PingResponseError); ok {
95 return nil, foundVersion, fallbackError{
97 confirmedV2: foundVersion,
98 transportOK: transportOK,
102 if authConfig.RegistryToken != "" {
103 passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
104 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
106 scope := auth.RepositoryScope{
107 Repository: repoName,
109 Class: repoInfo.Class,
112 creds := registry.NewStaticCredentialStore(authConfig)
113 tokenHandlerOptions := auth.TokenHandlerOptions{
114 Transport: authTransport,
116 Scopes: []auth.Scope{scope},
117 ClientID: registry.AuthClientID,
119 tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
120 basicHandler := auth.NewBasicHandler(creds)
121 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
123 tr := transport.NewTransport(base, modifiers...)
125 repoNameRef, err := reference.WithName(repoName)
127 return nil, foundVersion, fallbackError{
129 confirmedV2: foundVersion,
134 repo, err = client.NewRepository(ctx, repoNameRef, endpoint.URL.String(), tr)
138 confirmedV2: foundVersion,
145 type existingTokenHandler struct {
149 func (th *existingTokenHandler) Scheme() string {
153 func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
154 req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))