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