694df3ff |
package distribution
import ( |
8dce8e99 |
"fmt" |
694df3ff |
"net"
"net/http"
"time"
"github.com/docker/distribution" |
3c7676a0 |
"github.com/docker/distribution/manifest/schema2" |
3a127939 |
"github.com/docker/distribution/reference" |
694df3ff |
"github.com/docker/distribution/registry/client"
"github.com/docker/distribution/registry/client/auth"
"github.com/docker/distribution/registry/client/transport" |
91e197d6 |
"github.com/docker/docker/api/types" |
61a49bb6 |
"github.com/docker/docker/dockerversion" |
694df3ff |
"github.com/docker/docker/registry" |
20702708 |
"github.com/docker/go-connections/sockets" |
694df3ff |
"golang.org/x/net/context"
)
|
3c7676a0 |
// ImageTypes represents the schema2 config types for images
var ImageTypes = []string{
schema2.MediaTypeImageConfig,
// Handle unexpected values from https://github.com/docker/distribution/issues/1621 |
a215e15c |
// (see also https://github.com/docker/docker/issues/22378,
// https://github.com/docker/docker/issues/30083) |
3c7676a0 |
"application/octet-stream", |
a215e15c |
"application/json",
"text/html", |
3c7676a0 |
// Treat defaulted values as images, newer types cannot be implied
"",
}
// PluginTypes represents the schema2 config types for plugins
var PluginTypes = []string{
schema2.MediaTypePluginConfig,
}
var mediaTypeClasses map[string]string
func init() {
// initialize media type classes with all know types for
// plugin
mediaTypeClasses = map[string]string{}
for _, t := range ImageTypes {
mediaTypeClasses[t] = "image"
}
for _, t := range PluginTypes {
mediaTypeClasses[t] = "plugin"
}
}
|
c1be45fa |
// NewV2Repository returns a repository (v2 only). It creates an HTTP transport |
694df3ff |
// providing timeout settings and authentication support, and also verifies the
// remote API version. |
a57478d6 |
func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (repo distribution.Repository, foundVersion bool, err error) { |
3a127939 |
repoName := repoInfo.Name.Name() |
694df3ff |
// If endpoint does not support CanonicalName, use the RemoteName instead
if endpoint.TrimHostname { |
3a127939 |
repoName = reference.Path(repoInfo.Name) |
694df3ff |
}
|
20702708 |
direct := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}
|
694df3ff |
// TODO(dmcgowan): Call close idle connections when complete, use keep alive
base := &http.Transport{ |
20702708 |
Proxy: http.ProxyFromEnvironment,
Dial: direct.Dial, |
694df3ff |
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: endpoint.TLSConfig,
// TODO(dmcgowan): Call close idle connections when complete and use keep alive
DisableKeepAlives: true,
}
|
20702708 |
proxyDialer, err := sockets.DialerFromEnvironment(direct)
if err == nil {
base.Dial = proxyDialer.Dial
}
|
de5c80b4 |
modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders) |
694df3ff |
authTransport := transport.NewTransport(base, modifiers...) |
5e8af46f |
|
19d48f0b |
challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport) |
f2d481a2 |
if err != nil {
transportOK := false
if responseErr, ok := err.(registry.PingResponseError); ok {
transportOK = true
err = responseErr.Err |
694df3ff |
} |
5e8af46f |
return nil, foundVersion, fallbackError{
err: err,
confirmedV2: foundVersion, |
f2d481a2 |
transportOK: transportOK, |
5e8af46f |
} |
694df3ff |
}
|
8dce8e99 |
if authConfig.RegistryToken != "" {
passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
} else { |
a12b4661 |
scope := auth.RepositoryScope{
Repository: repoName,
Actions: actions, |
3d86b0c7 |
Class: repoInfo.Class, |
a12b4661 |
}
|
19d48f0b |
creds := registry.NewStaticCredentialStore(authConfig) |
e896d1d7 |
tokenHandlerOptions := auth.TokenHandlerOptions{
Transport: authTransport,
Credentials: creds, |
a12b4661 |
Scopes: []auth.Scope{scope},
ClientID: registry.AuthClientID, |
e896d1d7 |
}
tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) |
8dce8e99 |
basicHandler := auth.NewBasicHandler(creds)
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
} |
694df3ff |
tr := transport.NewTransport(base, modifiers...)
|
3a127939 |
repoNameRef, err := reference.WithName(repoName) |
4d437a29 |
if err != nil { |
5e8af46f |
return nil, foundVersion, fallbackError{
err: err,
confirmedV2: foundVersion,
transportOK: true,
} |
4d437a29 |
}
|
79db131a |
repo, err = client.NewRepository(ctx, repoNameRef, endpoint.URL.String(), tr) |
5e8af46f |
if err != nil {
err = fallbackError{
err: err,
confirmedV2: foundVersion,
transportOK: true,
}
}
return |
694df3ff |
}
|
8dce8e99 |
type existingTokenHandler struct {
token string
}
func (th *existingTokenHandler) Scheme() string {
return "bearer"
}
func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))
return nil
} |