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  }