Files
docker-cli/cli/registry/client/endpoint.go
Sebastiaan van Stijn f6b90bc253 add internal fork of docker/docker/registry
This adds an internal fork of [github.com/docker/docker/registry], taken
at commit [moby@f651a5d]. Git history  was not preserved in this fork,
but can be found using the URLs provided.

This fork was created to remove the dependency on the "Moby" codebase,
and because the CLI only needs a subset of its features. The original
package was written specifically for use in the daemon code, and includes
functionality that cannot be used in the CLI.

[github.com/docker/docker/registry]: https://pkg.go.dev/github.com/docker/docker@v28.3.2+incompatible/registry
[moby@49306c6]: 49306c607b/registry

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-07-24 19:59:17 +02:00

120 lines
3.5 KiB
Go

package client
import (
"net"
"net/http"
"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"
"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)
repoInfo, _ := registry.ParseRepositoryInfo(ref)
indexInfo := repoInfo.Index
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.LookupPushEndpoints(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 := registry.NewStaticCredentialStore(&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"
}