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