Most places only use IndexInfo (and may not even need that), so replace
the use of ParseRepositoryInfo for NewIndexInfo, and move the RepositoryInfo
type to the trust package, which uses it as part of its ImageRefAndAuth
struct.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 21e8bbc8a2)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
141 lines
4.0 KiB
Go
141 lines
4.0 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"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/docker/docker/api/types/registry"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type repositoryEndpoint struct {
|
|
repoName reference.Named
|
|
indexInfo *registrytypes.IndexInfo
|
|
endpoint registry.APIEndpoint
|
|
actions []string
|
|
}
|
|
|
|
// Name returns the repository name
|
|
func (r repositoryEndpoint) Name() string {
|
|
return reference.Path(r.repoName)
|
|
}
|
|
|
|
// BaseURL returns the endpoint url
|
|
func (r repositoryEndpoint) BaseURL() string {
|
|
return r.endpoint.URL.String()
|
|
}
|
|
|
|
func newDefaultRepositoryEndpoint(ref reference.Named, insecure bool) (repositoryEndpoint, error) {
|
|
repoName := reference.TrimNamed(ref)
|
|
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: repoName,
|
|
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, errors.Wrap(err, "error pinging v2 registry")
|
|
}
|
|
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) {}
|