github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/distribution/registry.go (about) 1 package distribution // import "github.com/docker/docker/distribution" 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "net/http" 8 "time" 9 10 "github.com/docker/distribution" 11 "github.com/docker/distribution/manifest/schema2" 12 "github.com/docker/distribution/reference" 13 "github.com/docker/distribution/registry/client" 14 "github.com/docker/distribution/registry/client/auth" 15 "github.com/docker/distribution/registry/client/transport" 16 "github.com/docker/docker/api/types" 17 "github.com/docker/docker/dockerversion" 18 "github.com/docker/docker/registry" 19 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 20 ) 21 22 // ImageTypes represents the schema2 config types for images 23 var ImageTypes = []string{ 24 schema2.MediaTypeImageConfig, 25 ocispec.MediaTypeImageConfig, 26 // Handle unexpected values from https://github.com/docker/distribution/issues/1621 27 // (see also https://github.com/docker/docker/issues/22378, 28 // https://github.com/docker/docker/issues/30083) 29 "application/octet-stream", 30 "application/json", 31 "text/html", 32 // Treat defaulted values as images, newer types cannot be implied 33 "", 34 } 35 36 // PluginTypes represents the schema2 config types for plugins 37 var PluginTypes = []string{ 38 schema2.MediaTypePluginConfig, 39 } 40 41 var mediaTypeClasses map[string]string 42 43 func init() { 44 // initialize media type classes with all know types for 45 // plugin 46 mediaTypeClasses = map[string]string{} 47 for _, t := range ImageTypes { 48 mediaTypeClasses[t] = "image" 49 } 50 for _, t := range PluginTypes { 51 mediaTypeClasses[t] = "plugin" 52 } 53 } 54 55 // NewV2Repository returns a repository (v2 only). It creates an HTTP transport 56 // providing timeout settings and authentication support, and also verifies the 57 // remote API version. 58 func NewV2Repository( 59 ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, 60 metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string, 61 ) (repo distribution.Repository, foundVersion bool, err error) { 62 repoName := repoInfo.Name.Name() 63 // If endpoint does not support CanonicalName, use the RemoteName instead 64 if endpoint.TrimHostname { 65 repoName = reference.Path(repoInfo.Name) 66 } 67 68 direct := &net.Dialer{ 69 Timeout: 30 * time.Second, 70 KeepAlive: 30 * time.Second, 71 DualStack: true, 72 } 73 74 // TODO(dmcgowan): Call close idle connections when complete, use keep alive 75 base := &http.Transport{ 76 Proxy: http.ProxyFromEnvironment, 77 DialContext: direct.DialContext, 78 TLSHandshakeTimeout: 10 * time.Second, 79 TLSClientConfig: endpoint.TLSConfig, 80 // TODO(dmcgowan): Call close idle connections when complete and use keep alive 81 DisableKeepAlives: true, 82 } 83 84 modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders) 85 authTransport := transport.NewTransport(base, modifiers...) 86 87 challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport) 88 if err != nil { 89 transportOK := false 90 if responseErr, ok := err.(registry.PingResponseError); ok { 91 transportOK = true 92 err = responseErr.Err 93 } 94 return nil, foundVersion, fallbackError{ 95 err: err, 96 confirmedV2: foundVersion, 97 transportOK: transportOK, 98 } 99 } 100 101 if authConfig.RegistryToken != "" { 102 passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} 103 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) 104 } else { 105 scope := auth.RepositoryScope{ 106 Repository: repoName, 107 Actions: actions, 108 Class: repoInfo.Class, 109 } 110 111 creds := registry.NewStaticCredentialStore(authConfig) 112 tokenHandlerOptions := auth.TokenHandlerOptions{ 113 Transport: authTransport, 114 Credentials: creds, 115 Scopes: []auth.Scope{scope}, 116 ClientID: registry.AuthClientID, 117 } 118 tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) 119 basicHandler := auth.NewBasicHandler(creds) 120 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) 121 } 122 tr := transport.NewTransport(base, modifiers...) 123 124 repoNameRef, err := reference.WithName(repoName) 125 if err != nil { 126 return nil, foundVersion, fallbackError{ 127 err: err, 128 confirmedV2: foundVersion, 129 transportOK: true, 130 } 131 } 132 133 repo, err = client.NewRepository(repoNameRef, endpoint.URL.String(), tr) 134 if err != nil { 135 err = fallbackError{ 136 err: err, 137 confirmedV2: foundVersion, 138 transportOK: true, 139 } 140 } 141 return 142 } 143 144 type existingTokenHandler struct { 145 token string 146 } 147 148 func (th *existingTokenHandler) Scheme() string { 149 return "bearer" 150 } 151 152 func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error { 153 req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token)) 154 return nil 155 }