Tizen_4.0 base
[platform/upstream/docker-engine.git] / distribution / registry.go
1 package distribution
2
3 import (
4         "fmt"
5         "net"
6         "net/http"
7         "time"
8
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"
20 )
21
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",
29         "application/json",
30         "text/html",
31         // Treat defaulted values as images, newer types cannot be implied
32         "",
33 }
34
35 // PluginTypes represents the schema2 config types for plugins
36 var PluginTypes = []string{
37         schema2.MediaTypePluginConfig,
38 }
39
40 var mediaTypeClasses map[string]string
41
42 func init() {
43         // initialize media type classes with all know types for
44         // plugin
45         mediaTypeClasses = map[string]string{}
46         for _, t := range ImageTypes {
47                 mediaTypeClasses[t] = "image"
48         }
49         for _, t := range PluginTypes {
50                 mediaTypeClasses[t] = "plugin"
51         }
52 }
53
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)
62         }
63
64         direct := &net.Dialer{
65                 Timeout:   30 * time.Second,
66                 KeepAlive: 30 * time.Second,
67                 DualStack: true,
68         }
69
70         // TODO(dmcgowan): Call close idle connections when complete, use keep alive
71         base := &http.Transport{
72                 Proxy:               http.ProxyFromEnvironment,
73                 Dial:                direct.Dial,
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,
78         }
79
80         proxyDialer, err := sockets.DialerFromEnvironment(direct)
81         if err == nil {
82                 base.Dial = proxyDialer.Dial
83         }
84
85         modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), metaHeaders)
86         authTransport := transport.NewTransport(base, modifiers...)
87
88         challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport)
89         if err != nil {
90                 transportOK := false
91                 if responseErr, ok := err.(registry.PingResponseError); ok {
92                         transportOK = true
93                         err = responseErr.Err
94                 }
95                 return nil, foundVersion, fallbackError{
96                         err:         err,
97                         confirmedV2: foundVersion,
98                         transportOK: transportOK,
99                 }
100         }
101
102         if authConfig.RegistryToken != "" {
103                 passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
104                 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
105         } else {
106                 scope := auth.RepositoryScope{
107                         Repository: repoName,
108                         Actions:    actions,
109                         Class:      repoInfo.Class,
110                 }
111
112                 creds := registry.NewStaticCredentialStore(authConfig)
113                 tokenHandlerOptions := auth.TokenHandlerOptions{
114                         Transport:   authTransport,
115                         Credentials: creds,
116                         Scopes:      []auth.Scope{scope},
117                         ClientID:    registry.AuthClientID,
118                 }
119                 tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
120                 basicHandler := auth.NewBasicHandler(creds)
121                 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
122         }
123         tr := transport.NewTransport(base, modifiers...)
124
125         repoNameRef, err := reference.WithName(repoName)
126         if err != nil {
127                 return nil, foundVersion, fallbackError{
128                         err:         err,
129                         confirmedV2: foundVersion,
130                         transportOK: true,
131                 }
132         }
133
134         repo, err = client.NewRepository(ctx, repoNameRef, endpoint.URL.String(), tr)
135         if err != nil {
136                 err = fallbackError{
137                         err:         err,
138                         confirmedV2: foundVersion,
139                         transportOK: true,
140                 }
141         }
142         return
143 }
144
145 type existingTokenHandler struct {
146         token string
147 }
148
149 func (th *existingTokenHandler) Scheme() string {
150         return "bearer"
151 }
152
153 func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
154         req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))
155         return nil
156 }