github.com/rawahars/moby@v24.0.4+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 registrytypes "github.com/docker/docker/api/types/registry" 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 var ( 23 // supportedMediaTypes represents acceptable media-type(-prefixes) 24 // we use this list to prevent obscure errors when trying to pull 25 // OCI artifacts. 26 supportedMediaTypes = []string{ 27 // valid prefixes 28 "application/vnd.oci.image", 29 "application/vnd.docker", 30 31 // these types may occur on old images, and are copied from 32 // defaultImageTypes below. 33 "application/octet-stream", 34 "application/json", 35 "text/html", 36 "", 37 } 38 39 // defaultImageTypes represents the schema2 config types for images 40 defaultImageTypes = []string{ 41 schema2.MediaTypeImageConfig, 42 ocispec.MediaTypeImageConfig, 43 // Handle unexpected values from https://github.com/docker/distribution/issues/1621 44 // (see also https://github.com/docker/docker/issues/22378, 45 // https://github.com/docker/docker/issues/30083) 46 "application/octet-stream", 47 "application/json", 48 "text/html", 49 // Treat defaulted values as images, newer types cannot be implied 50 "", 51 } 52 53 // pluginTypes represents the schema2 config types for plugins 54 pluginTypes = []string{ 55 schema2.MediaTypePluginConfig, 56 } 57 58 mediaTypeClasses map[string]string 59 ) 60 61 func init() { 62 // initialize media type classes with all know types for images and plugins. 63 mediaTypeClasses = map[string]string{} 64 for _, t := range defaultImageTypes { 65 mediaTypeClasses[t] = "image" 66 } 67 for _, t := range pluginTypes { 68 mediaTypeClasses[t] = "plugin" 69 } 70 } 71 72 // newRepository returns a repository (v2 only). It creates an HTTP transport 73 // providing timeout settings and authentication support, and also verifies the 74 // remote API version. 75 func newRepository( 76 ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, 77 metaHeaders http.Header, authConfig *registrytypes.AuthConfig, actions ...string, 78 ) (repo distribution.Repository, err error) { 79 repoName := repoInfo.Name.Name() 80 // If endpoint does not support CanonicalName, use the RemoteName instead 81 if endpoint.TrimHostname { 82 repoName = reference.Path(repoInfo.Name) 83 } 84 85 direct := &net.Dialer{ 86 Timeout: 30 * time.Second, 87 KeepAlive: 30 * time.Second, 88 } 89 90 // TODO(dmcgowan): Call close idle connections when complete, use keep alive 91 base := &http.Transport{ 92 Proxy: http.ProxyFromEnvironment, 93 DialContext: direct.DialContext, 94 TLSHandshakeTimeout: 10 * time.Second, 95 TLSClientConfig: endpoint.TLSConfig, 96 // TODO(dmcgowan): Call close idle connections when complete and use keep alive 97 DisableKeepAlives: true, 98 } 99 100 modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders) 101 authTransport := transport.NewTransport(base, modifiers...) 102 103 challengeManager, err := registry.PingV2Registry(endpoint.URL, authTransport) 104 if err != nil { 105 transportOK := false 106 if responseErr, ok := err.(registry.PingResponseError); ok { 107 transportOK = true 108 err = responseErr.Err 109 } 110 return nil, fallbackError{ 111 err: err, 112 transportOK: transportOK, 113 } 114 } 115 116 if authConfig.RegistryToken != "" { 117 passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} 118 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) 119 } else { 120 scope := auth.RepositoryScope{ 121 Repository: repoName, 122 Actions: actions, 123 Class: repoInfo.Class, 124 } 125 126 creds := registry.NewStaticCredentialStore(authConfig) 127 tokenHandlerOptions := auth.TokenHandlerOptions{ 128 Transport: authTransport, 129 Credentials: creds, 130 Scopes: []auth.Scope{scope}, 131 ClientID: registry.AuthClientID, 132 } 133 tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) 134 basicHandler := auth.NewBasicHandler(creds) 135 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) 136 } 137 tr := transport.NewTransport(base, modifiers...) 138 139 repoNameRef, err := reference.WithName(repoName) 140 if err != nil { 141 return nil, fallbackError{ 142 err: err, 143 transportOK: true, 144 } 145 } 146 147 repo, err = client.NewRepository(repoNameRef, endpoint.URL.String(), tr) 148 if err != nil { 149 err = fallbackError{ 150 err: err, 151 transportOK: true, 152 } 153 } 154 return 155 } 156 157 type existingTokenHandler struct { 158 token string 159 } 160 161 func (th *existingTokenHandler) Scheme() string { 162 return "bearer" 163 } 164 165 func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error { 166 req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token)) 167 return nil 168 }