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  }