github.com/skf/moby@v1.13.1/distribution/registry.go (about) 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 distreference "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.FullName() 59 // If endpoint does not support CanonicalName, use the RemoteName instead 60 if endpoint.TrimHostname { 61 repoName = repoInfo.RemoteName() 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 := distreference.ParseNamed(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 }