github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/registry/client/endpoint.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "net" 6 "net/http" 7 "time" 8 9 "github.com/docker/distribution/reference" 10 "github.com/docker/distribution/registry/client/auth" 11 "github.com/docker/distribution/registry/client/transport" 12 authtypes "github.com/docker/docker/api/types" 13 "github.com/docker/docker/registry" 14 "github.com/pkg/errors" 15 ) 16 17 type repositoryEndpoint struct { 18 info *registry.RepositoryInfo 19 endpoint registry.APIEndpoint 20 } 21 22 // Name returns the repository name 23 func (r repositoryEndpoint) Name() string { 24 repoName := r.info.Name.Name() 25 // If endpoint does not support CanonicalName, use the RemoteName instead 26 if r.endpoint.TrimHostname { 27 repoName = reference.Path(r.info.Name) 28 } 29 return repoName 30 } 31 32 // BaseURL returns the endpoint url 33 func (r repositoryEndpoint) BaseURL() string { 34 return r.endpoint.URL.String() 35 } 36 37 func newDefaultRepositoryEndpoint(ref reference.Named, insecure bool) (repositoryEndpoint, error) { 38 repoInfo, err := registry.ParseRepositoryInfo(ref) 39 if err != nil { 40 return repositoryEndpoint{}, err 41 } 42 endpoint, err := getDefaultEndpointFromRepoInfo(repoInfo) 43 if err != nil { 44 return repositoryEndpoint{}, err 45 } 46 if insecure { 47 endpoint.TLSConfig.InsecureSkipVerify = true 48 } 49 return repositoryEndpoint{info: repoInfo, endpoint: endpoint}, nil 50 } 51 52 func getDefaultEndpointFromRepoInfo(repoInfo *registry.RepositoryInfo) (registry.APIEndpoint, error) { 53 var err error 54 55 options := registry.ServiceOptions{} 56 registryService, err := registry.NewService(options) 57 if err != nil { 58 return registry.APIEndpoint{}, err 59 } 60 endpoints, err := registryService.LookupPushEndpoints(reference.Domain(repoInfo.Name)) 61 if err != nil { 62 return registry.APIEndpoint{}, err 63 } 64 // Default to the highest priority endpoint to return 65 endpoint := endpoints[0] 66 if !repoInfo.Index.Secure { 67 for _, ep := range endpoints { 68 if ep.URL.Scheme == "http" { 69 endpoint = ep 70 } 71 } 72 } 73 return endpoint, nil 74 } 75 76 // getHTTPTransport builds a transport for use in communicating with a registry 77 func getHTTPTransport(authConfig authtypes.AuthConfig, endpoint registry.APIEndpoint, repoName string, userAgent string) (http.RoundTripper, error) { 78 // get the http transport, this will be used in a client to upload manifest 79 base := &http.Transport{ 80 Proxy: http.ProxyFromEnvironment, 81 Dial: (&net.Dialer{ 82 Timeout: 30 * time.Second, 83 KeepAlive: 30 * time.Second, 84 DualStack: true, 85 }).Dial, 86 TLSHandshakeTimeout: 10 * time.Second, 87 TLSClientConfig: endpoint.TLSConfig, 88 DisableKeepAlives: true, 89 } 90 91 modifiers := registry.Headers(userAgent, http.Header{}) 92 authTransport := transport.NewTransport(base, modifiers...) 93 challengeManager, confirmedV2, err := registry.PingV2Registry(endpoint.URL, authTransport) 94 if err != nil { 95 return nil, errors.Wrap(err, "error pinging v2 registry") 96 } 97 if !confirmedV2 { 98 return nil, fmt.Errorf("unsupported registry version") 99 } 100 if authConfig.RegistryToken != "" { 101 passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} 102 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) 103 } else { 104 creds := registry.NewStaticCredentialStore(&authConfig) 105 tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, "push", "pull") 106 basicHandler := auth.NewBasicHandler(creds) 107 modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) 108 } 109 return transport.NewTransport(base, modifiers...), nil 110 } 111 112 // RepoNameForReference returns the repository name from a reference 113 func RepoNameForReference(ref reference.Named) (string, error) { 114 // insecure is fine since this only returns the name 115 repo, err := newDefaultRepositoryEndpoint(ref, false) 116 if err != nil { 117 return "", err 118 } 119 return repo.Name(), nil 120 } 121 122 type existingTokenHandler struct { 123 token string 124 } 125 126 func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error { 127 req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token)) 128 return nil 129 } 130 131 func (th *existingTokenHandler) Scheme() string { 132 return "bearer" 133 }