| package registryclient |
| |
| import ( |
| "context" |
| "fmt" |
| "net" |
| "net/http" |
| "net/url" |
| "time" |
| |
| "github.com/distribution/reference" |
| "github.com/docker/cli/internal/registry" |
| "github.com/docker/distribution/registry/client/auth" |
| "github.com/docker/distribution/registry/client/transport" |
| registrytypes "github.com/moby/moby/api/types/registry" |
| ) |
| |
| type repositoryEndpoint struct { |
| repoName string |
| indexInfo *registrytypes.IndexInfo |
| endpoint registry.APIEndpoint |
| actions []string |
| } |
| |
| // BaseURL returns the endpoint url |
| func (r repositoryEndpoint) BaseURL() string { |
| return r.endpoint.URL.String() |
| } |
| |
| func newDefaultRepositoryEndpoint(ref reference.Named, insecure bool) (repositoryEndpoint, error) { |
| indexInfo := registry.NewIndexInfo(ref) |
| endpoint, err := getDefaultEndpoint(ref, !indexInfo.Secure) |
| if err != nil { |
| return repositoryEndpoint{}, err |
| } |
| if insecure { |
| endpoint.TLSConfig.InsecureSkipVerify = true |
| } |
| return repositoryEndpoint{ |
| repoName: reference.Path(reference.TrimNamed(ref)), |
| indexInfo: indexInfo, |
| endpoint: endpoint, |
| }, nil |
| } |
| |
| func getDefaultEndpoint(repoName reference.Named, insecure bool) (registry.APIEndpoint, error) { |
| registryService, err := registry.NewService(registry.ServiceOptions{}) |
| if err != nil { |
| return registry.APIEndpoint{}, err |
| } |
| endpoints, err := registryService.Endpoints(context.TODO(), reference.Domain(repoName)) |
| if err != nil { |
| return registry.APIEndpoint{}, err |
| } |
| // Default to the highest priority endpoint to return |
| endpoint := endpoints[0] |
| if insecure { |
| for _, ep := range endpoints { |
| if ep.URL.Scheme == "http" { |
| endpoint = ep |
| } |
| } |
| } |
| return endpoint, nil |
| } |
| |
| // getHTTPTransport builds a transport for use in communicating with a registry |
| func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.APIEndpoint, repoName, userAgent string, actions []string) (http.RoundTripper, error) { |
| // get the http transport, this will be used in a client to upload manifest |
| base := &http.Transport{ |
| Proxy: http.ProxyFromEnvironment, |
| Dial: (&net.Dialer{ |
| Timeout: 30 * time.Second, |
| KeepAlive: 30 * time.Second, |
| }).Dial, |
| TLSHandshakeTimeout: 10 * time.Second, |
| TLSClientConfig: endpoint.TLSConfig, |
| DisableKeepAlives: true, |
| } |
| |
| modifiers := registry.Headers(userAgent, http.Header{}) |
| authTransport := transport.NewTransport(base, modifiers...) |
| challengeManager, err := registry.PingV2Registry(endpoint.URL, authTransport) |
| if err != nil { |
| return nil, fmt.Errorf("error pinging v2 registry: %w", err) |
| } |
| if authConfig.RegistryToken != "" { |
| passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} |
| modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) |
| } else { |
| if len(actions) == 0 { |
| actions = []string{"pull"} |
| } |
| creds := &staticCredentialStore{authConfig: &authConfig} |
| tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...) |
| basicHandler := auth.NewBasicHandler(creds) |
| modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) |
| } |
| return transport.NewTransport(base, modifiers...), nil |
| } |
| |
| type existingTokenHandler struct { |
| token string |
| } |
| |
| func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, _ map[string]string) error { |
| req.Header.Set("Authorization", "Bearer "+th.token) |
| return nil |
| } |
| |
| func (*existingTokenHandler) Scheme() string { |
| return "bearer" |
| } |
| |
| type staticCredentialStore struct { |
| authConfig *registrytypes.AuthConfig |
| } |
| |
| func (scs staticCredentialStore) Basic(*url.URL) (string, string) { |
| if scs.authConfig == nil { |
| return "", "" |
| } |
| return scs.authConfig.Username, scs.authConfig.Password |
| } |
| |
| func (scs staticCredentialStore) RefreshToken(*url.URL, string) string { |
| if scs.authConfig == nil { |
| return "" |
| } |
| return scs.authConfig.IdentityToken |
| } |
| |
| func (staticCredentialStore) SetRefreshToken(*url.URL, string, string) {} |