mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Remove more of registry v1 code.
Signed-off-by: Tibor Vass <tibor@docker.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
4a054ec00f
commit
7a50fe8a52
16 changed files with 43 additions and 199 deletions
|
@ -11,5 +11,5 @@ import (
|
||||||
// Backend is all the methods that need to be implemented
|
// Backend is all the methods that need to be implemented
|
||||||
// to provide image specific functionality.
|
// to provide image specific functionality.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error)
|
GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res
|
||||||
return errdefs.InvalidParameter(errors.Errorf("unknown image reference format: %s", image))
|
return errdefs.InvalidParameter(errors.Errorf("unknown image reference format: %s", image))
|
||||||
}
|
}
|
||||||
|
|
||||||
distrepo, _, err := s.backend.GetRepository(ctx, namedRef, config)
|
distrepo, err := s.backend.GetRepository(ctx, namedRef, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,6 @@ type VolumeBackend interface {
|
||||||
// ImageBackend is used by an executor to perform image operations
|
// ImageBackend is used by an executor to perform image operations
|
||||||
type ImageBackend interface {
|
type ImageBackend interface {
|
||||||
PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
|
PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
|
||||||
GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error)
|
GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, error)
|
||||||
LookupImage(name string) (*types.ImageInspect, error)
|
LookupImage(name string) (*types.ImageInspect, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -633,7 +633,7 @@ func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authC
|
||||||
return "", errors.Errorf("image reference not tagged: %s", image)
|
return "", errors.Errorf("image reference not tagged: %s", image)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo, _, err := c.config.ImageBackend.GetRepository(ctx, taggedRef, authConfig)
|
repo, err := c.config.ImageBackend.GetRepository(ctx, taggedRef, authConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
progressutils "github.com/docker/docker/distribution/utils"
|
progressutils "github.com/docker/docker/distribution/utils"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
"github.com/docker/docker/pkg/progress"
|
"github.com/docker/docker/pkg/progress"
|
||||||
"github.com/docker/docker/registry"
|
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -110,41 +109,36 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRepository returns a repository from the registry.
|
// GetRepository returns a repository from the registry.
|
||||||
func (i *ImageService) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
|
func (i *ImageService) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, error) {
|
||||||
// get repository info
|
// get repository info
|
||||||
repoInfo, err := i.registryService.ResolveRepository(ref)
|
repoInfo, err := i.registryService.ResolveRepository(ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, errdefs.InvalidParameter(err)
|
return nil, errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
// makes sure name is not empty or `scratch`
|
// makes sure name is not empty or `scratch`
|
||||||
if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
|
if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
|
||||||
return nil, false, errdefs.InvalidParameter(err)
|
return nil, errdefs.InvalidParameter(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get endpoints
|
// get endpoints
|
||||||
endpoints, err := i.registryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
|
endpoints, err := i.registryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve repository
|
// retrieve repository
|
||||||
var (
|
var (
|
||||||
confirmedV2 bool
|
|
||||||
repository dist.Repository
|
repository dist.Repository
|
||||||
lastError error
|
lastError error
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
if endpoint.Version == registry.APIVersion1 {
|
repository, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull")
|
||||||
continue
|
if lastError == nil {
|
||||||
}
|
|
||||||
|
|
||||||
repository, confirmedV2, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull")
|
|
||||||
if lastError == nil && confirmedV2 {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return repository, confirmedV2, lastError
|
return repository, lastError
|
||||||
}
|
}
|
||||||
|
|
||||||
func tempLease(ctx context.Context, mgr leases.Manager) (context.Context, func(context.Context) error, error) {
|
func tempLease(ctx context.Context, mgr leases.Manager) (context.Context, func(context.Context) error, error) {
|
||||||
|
|
|
@ -34,10 +34,6 @@ func (e ErrNoSupport) Error() string {
|
||||||
type fallbackError struct {
|
type fallbackError struct {
|
||||||
// err is the error being wrapped.
|
// err is the error being wrapped.
|
||||||
err error
|
err error
|
||||||
// confirmedV2 is set to true if it was confirmed that the registry
|
|
||||||
// supports the v2 protocol. This is used to limit fallbacks to the v1
|
|
||||||
// protocol.
|
|
||||||
confirmedV2 bool
|
|
||||||
// transportOK is set to true if we managed to speak HTTP with the
|
// transportOK is set to true if we managed to speak HTTP with the
|
||||||
// registry. This confirms that we're using appropriate TLS settings
|
// registry. This confirms that we're using appropriate TLS settings
|
||||||
// (or lack of TLS).
|
// (or lack of TLS).
|
||||||
|
@ -53,15 +49,6 @@ func (f fallbackError) Cause() error {
|
||||||
return f.err
|
return f.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldV2Fallback returns true if this error is a reason to fall back to v1.
|
|
||||||
func shouldV2Fallback(err errcode.Error) bool {
|
|
||||||
switch err.Code {
|
|
||||||
case errcode.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type notFoundError struct {
|
type notFoundError struct {
|
||||||
cause errcode.Error
|
cause errcode.Error
|
||||||
ref reference.Named
|
ref reference.Named
|
||||||
|
@ -141,7 +128,7 @@ func continueOnError(err error, mirrorEndpoint bool) bool {
|
||||||
case ErrNoSupport:
|
case ErrNoSupport:
|
||||||
return continueOnError(v.Err, mirrorEndpoint)
|
return continueOnError(v.Err, mirrorEndpoint)
|
||||||
case errcode.Error:
|
case errcode.Error:
|
||||||
return mirrorEndpoint || shouldV2Fallback(v)
|
return mirrorEndpoint
|
||||||
case *client.UnexpectedHTTPResponseError:
|
case *client.UnexpectedHTTPResponseError:
|
||||||
return true
|
return true
|
||||||
case ImageConfigPullError:
|
case ImageConfigPullError:
|
||||||
|
|
|
@ -24,11 +24,7 @@ type Puller interface {
|
||||||
Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) error
|
Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPuller returns a Puller interface that will pull from either a v1 or v2
|
// newPuller returns a Puller interface that will pull from a v2 registry.
|
||||||
// registry. The endpoint argument contains a Version field that determines
|
|
||||||
// whether a v1 or v2 puller will be created. The other parameters are passed
|
|
||||||
// through to the underlying puller implementation for use during the actual
|
|
||||||
// pull operation.
|
|
||||||
func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, local ContentStore) (Puller, error) {
|
func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, local ContentStore) (Puller, error) {
|
||||||
switch endpoint.Version {
|
switch endpoint.Version {
|
||||||
case registry.APIVersion2:
|
case registry.APIVersion2:
|
||||||
|
@ -78,26 +74,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
|
||||||
// error is the ones from v2 endpoints not v1.
|
// error is the ones from v2 endpoints not v1.
|
||||||
discardNoSupportErrors bool
|
discardNoSupportErrors bool
|
||||||
|
|
||||||
// confirmedV2 is set to true if a pull attempt managed to
|
|
||||||
// confirm that it was talking to a v2 registry. This will
|
|
||||||
// prevent fallback to the v1 protocol.
|
|
||||||
confirmedV2 bool
|
|
||||||
|
|
||||||
// confirmedTLSRegistries is a map indicating which registries
|
// confirmedTLSRegistries is a map indicating which registries
|
||||||
// are known to be using TLS. There should never be a plaintext
|
// are known to be using TLS. There should never be a plaintext
|
||||||
// retry for any of these.
|
// retry for any of these.
|
||||||
confirmedTLSRegistries = make(map[string]struct{})
|
confirmedTLSRegistries = make(map[string]struct{})
|
||||||
)
|
)
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
if imagePullConfig.RequireSchema2 && endpoint.Version == registry.APIVersion1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if confirmedV2 && endpoint.Version == registry.APIVersion1 {
|
|
||||||
logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if endpoint.URL.Scheme != "https" {
|
if endpoint.URL.Scheme != "https" {
|
||||||
if _, confirmedTLS := confirmedTLSRegistries[endpoint.URL.Host]; confirmedTLS {
|
if _, confirmedTLS := confirmedTLSRegistries[endpoint.URL.Host]; confirmedTLS {
|
||||||
logrus.Debugf("Skipping non-TLS endpoint %s for host/port that appears to use TLS", endpoint.URL)
|
logrus.Debugf("Skipping non-TLS endpoint %s for host/port that appears to use TLS", endpoint.URL)
|
||||||
|
@ -122,7 +104,6 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
|
||||||
default:
|
default:
|
||||||
if fallbackErr, ok := err.(fallbackError); ok {
|
if fallbackErr, ok := err.(fallbackError); ok {
|
||||||
fallback = true
|
fallback = true
|
||||||
confirmedV2 = confirmedV2 || fallbackErr.confirmedV2
|
|
||||||
if fallbackErr.transportOK && endpoint.URL.Scheme == "https" {
|
if fallbackErr.transportOK && endpoint.URL.Scheme == "https" {
|
||||||
confirmedTLSRegistries[endpoint.URL.Host] = struct{}{}
|
confirmedTLSRegistries[endpoint.URL.Host] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -19,8 +18,6 @@ import (
|
||||||
"github.com/docker/distribution/manifest/schema1"
|
"github.com/docker/distribution/manifest/schema1"
|
||||||
"github.com/docker/distribution/manifest/schema2"
|
"github.com/docker/distribution/manifest/schema2"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
|
||||||
"github.com/docker/distribution/registry/client/auth"
|
|
||||||
"github.com/docker/distribution/registry/client/transport"
|
"github.com/docker/distribution/registry/client/transport"
|
||||||
"github.com/docker/docker/distribution/metadata"
|
"github.com/docker/docker/distribution/metadata"
|
||||||
"github.com/docker/docker/distribution/xfer"
|
"github.com/docker/docker/distribution/xfer"
|
||||||
|
@ -61,15 +58,12 @@ type v2Puller struct {
|
||||||
config *ImagePullConfig
|
config *ImagePullConfig
|
||||||
repoInfo *registry.RepositoryInfo
|
repoInfo *registry.RepositoryInfo
|
||||||
repo distribution.Repository
|
repo distribution.Repository
|
||||||
// confirmedV2 is set to true if we confirm we're talking to a v2
|
|
||||||
// registry. This is used to limit fallbacks to the v1 protocol.
|
|
||||||
confirmedV2 bool
|
|
||||||
manifestStore *manifestStore
|
manifestStore *manifestStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
|
func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
|
||||||
// TODO(tiborvass): was ReceiveTimeout
|
// TODO(tiborvass): was ReceiveTimeout
|
||||||
p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
|
p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("Error getting v2 registry: %v", err)
|
logrus.Warnf("Error getting v2 registry: %v", err)
|
||||||
return err
|
return err
|
||||||
|
@ -87,7 +81,6 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *spec
|
||||||
if continueOnError(err, p.endpoint.Mirror) {
|
if continueOnError(err, p.endpoint.Mirror) {
|
||||||
return fallbackError{
|
return fallbackError{
|
||||||
err: err,
|
err: err,
|
||||||
confirmedV2: p.confirmedV2,
|
|
||||||
transportOK: true,
|
transportOK: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,16 +98,9 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, pl
|
||||||
} else {
|
} else {
|
||||||
tags, err := p.repo.Tags(ctx).All(ctx)
|
tags, err := p.repo.Tags(ctx).All(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If this repository doesn't exist on V2, we should
|
return err
|
||||||
// permit a fallback to V1.
|
|
||||||
return allowV1Fallback(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The v2 registry knows about this repository, so we will not
|
|
||||||
// allow fallback to the v1 protocol even if we encounter an
|
|
||||||
// error later on.
|
|
||||||
p.confirmedV2 = true
|
|
||||||
|
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
tagRef, err := reference.WithTag(ref, tag)
|
tagRef, err := reference.WithTag(ref, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -353,7 +339,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform
|
||||||
tagService := p.repo.Tags(ctx)
|
tagService := p.repo.Tags(ctx)
|
||||||
desc, err := tagService.Get(ctx, tagged.Tag())
|
desc, err := tagService.Get(ctx, tagged.Tag())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, allowV1Fallback(err)
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dgst = desc.Digest
|
dgst = desc.Digest
|
||||||
|
@ -427,10 +413,6 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If manSvc.Get succeeded, we can be confident that the registry on
|
|
||||||
// the other side speaks the v2 protocol.
|
|
||||||
p.confirmedV2 = true
|
|
||||||
|
|
||||||
logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
|
logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
|
||||||
progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
|
progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
|
||||||
|
|
||||||
|
@ -945,39 +927,6 @@ func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (dig
|
||||||
return digest.FromBytes(canonical), nil
|
return digest.FromBytes(canonical), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// allowV1Fallback checks if the error is a possible reason to fallback to v1
|
|
||||||
// (even if confirmedV2 has been set already), and if so, wraps the error in
|
|
||||||
// a fallbackError with confirmedV2 set to false. Otherwise, it returns the
|
|
||||||
// error unmodified.
|
|
||||||
func allowV1Fallback(err error) error {
|
|
||||||
switch v := err.(type) {
|
|
||||||
case errcode.Errors:
|
|
||||||
if len(v) != 0 {
|
|
||||||
if v0, ok := v[0].(errcode.Error); ok && shouldV2Fallback(v0) {
|
|
||||||
return fallbackError{
|
|
||||||
err: err,
|
|
||||||
confirmedV2: false,
|
|
||||||
transportOK: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case errcode.Error:
|
|
||||||
if shouldV2Fallback(v) {
|
|
||||||
return fallbackError{
|
|
||||||
err: err,
|
|
||||||
confirmedV2: false,
|
|
||||||
transportOK: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *url.Error:
|
|
||||||
if v.Err == auth.ErrNoBasicAuthCredentials {
|
|
||||||
return fallbackError{err: err, confirmedV2: false}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Reference) (m *schema1.Manifest, err error) {
|
func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Reference) (m *schema1.Manifest, err error) {
|
||||||
// If pull by digest, then verify the manifest digest. NOTE: It is
|
// If pull by digest, then verify the manifest digest. NOTE: It is
|
||||||
// important to do this first, before any other content validation. If the
|
// important to do this first, before any other content validation. If the
|
||||||
|
|
|
@ -73,11 +73,6 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
|
||||||
var (
|
var (
|
||||||
lastErr error
|
lastErr error
|
||||||
|
|
||||||
// confirmedV2 is set to true if a push attempt managed to
|
|
||||||
// confirm that it was talking to a v2 registry. This will
|
|
||||||
// prevent fallback to the v1 protocol.
|
|
||||||
confirmedV2 bool
|
|
||||||
|
|
||||||
// confirmedTLSRegistries is a map indicating which registries
|
// confirmedTLSRegistries is a map indicating which registries
|
||||||
// are known to be using TLS. There should never be a plaintext
|
// are known to be using TLS. There should never be a plaintext
|
||||||
// retry for any of these.
|
// retry for any of these.
|
||||||
|
@ -85,14 +80,6 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
if imagePushConfig.RequireSchema2 && endpoint.Version == registry.APIVersion1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if confirmedV2 && endpoint.Version == registry.APIVersion1 {
|
|
||||||
logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if endpoint.URL.Scheme != "https" {
|
if endpoint.URL.Scheme != "https" {
|
||||||
if _, confirmedTLS := confirmedTLSRegistries[endpoint.URL.Host]; confirmedTLS {
|
if _, confirmedTLS := confirmedTLSRegistries[endpoint.URL.Host]; confirmedTLS {
|
||||||
logrus.Debugf("Skipping non-TLS endpoint %s for host/port that appears to use TLS", endpoint.URL)
|
logrus.Debugf("Skipping non-TLS endpoint %s for host/port that appears to use TLS", endpoint.URL)
|
||||||
|
@ -114,7 +101,6 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
default:
|
default:
|
||||||
if fallbackErr, ok := err.(fallbackError); ok {
|
if fallbackErr, ok := err.(fallbackError); ok {
|
||||||
confirmedV2 = confirmedV2 || fallbackErr.confirmedV2
|
|
||||||
if fallbackErr.transportOK && endpoint.URL.Scheme == "https" {
|
if fallbackErr.transportOK && endpoint.URL.Scheme == "https" {
|
||||||
confirmedTLSRegistries[endpoint.URL.Host] = struct{}{}
|
confirmedTLSRegistries[endpoint.URL.Host] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,16 +53,13 @@ type pushState struct {
|
||||||
// involve the same layers. It is also used to fill in digest and size
|
// involve the same layers. It is also used to fill in digest and size
|
||||||
// information when building the manifest.
|
// information when building the manifest.
|
||||||
remoteLayers map[layer.DiffID]distribution.Descriptor
|
remoteLayers map[layer.DiffID]distribution.Descriptor
|
||||||
// confirmedV2 is set to true if we confirm we're talking to a v2
|
|
||||||
// registry. This is used to limit fallbacks to the v1 protocol.
|
|
||||||
confirmedV2 bool
|
|
||||||
hasAuthInfo bool
|
hasAuthInfo bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *v2Pusher) Push(ctx context.Context) (err error) {
|
func (p *v2Pusher) Push(ctx context.Context) (err error) {
|
||||||
p.pushState.remoteLayers = make(map[layer.DiffID]distribution.Descriptor)
|
p.pushState.remoteLayers = make(map[layer.DiffID]distribution.Descriptor)
|
||||||
|
|
||||||
p.repo, p.pushState.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "push", "pull")
|
p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "push", "pull")
|
||||||
p.pushState.hasAuthInfo = p.config.AuthConfig.RegistryToken != "" || (p.config.AuthConfig.Username != "" && p.config.AuthConfig.Password != "")
|
p.pushState.hasAuthInfo = p.config.AuthConfig.RegistryToken != "" || (p.config.AuthConfig.Username != "" && p.config.AuthConfig.Password != "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debugf("Error getting v2 registry: %v", err)
|
logrus.Debugf("Error getting v2 registry: %v", err)
|
||||||
|
@ -73,7 +70,6 @@ func (p *v2Pusher) Push(ctx context.Context) (err error) {
|
||||||
if continueOnError(err, p.endpoint.Mirror) {
|
if continueOnError(err, p.endpoint.Mirror) {
|
||||||
return fallbackError{
|
return fallbackError{
|
||||||
err: err,
|
err: err,
|
||||||
confirmedV2: p.pushState.confirmedV2,
|
|
||||||
transportOK: true,
|
transportOK: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,7 +366,6 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.
|
||||||
err.Descriptor.MediaType = schema2.MediaTypeLayer
|
err.Descriptor.MediaType = schema2.MediaTypeLayer
|
||||||
|
|
||||||
pd.pushState.Lock()
|
pd.pushState.Lock()
|
||||||
pd.pushState.confirmedV2 = true
|
|
||||||
pd.pushState.remoteLayers[diffID] = err.Descriptor
|
pd.pushState.remoteLayers[diffID] = err.Descriptor
|
||||||
pd.pushState.Unlock()
|
pd.pushState.Unlock()
|
||||||
|
|
||||||
|
@ -510,8 +505,6 @@ func (pd *v2PushDescriptor) uploadUsingSession(
|
||||||
}
|
}
|
||||||
|
|
||||||
pd.pushState.Lock()
|
pd.pushState.Lock()
|
||||||
// If Commit succeeded, that's an indication that the remote registry speaks the v2 protocol.
|
|
||||||
pd.pushState.confirmedV2 = true
|
|
||||||
pd.pushState.remoteLayers[diffID] = desc
|
pd.pushState.remoteLayers[diffID] = desc
|
||||||
pd.pushState.Unlock()
|
pd.pushState.Unlock()
|
||||||
|
|
||||||
|
|
|
@ -532,7 +532,7 @@ func TestWhenEmptyAuthConfig(t *testing.T) {
|
||||||
Scheme: "https",
|
Scheme: "https",
|
||||||
Host: "index.docker.io",
|
Host: "index.docker.io",
|
||||||
},
|
},
|
||||||
Version: registry.APIVersion1,
|
Version: registry.APIVersion2,
|
||||||
TrimHostname: true,
|
TrimHostname: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ func init() {
|
||||||
func NewV2Repository(
|
func NewV2Repository(
|
||||||
ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint,
|
ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint,
|
||||||
metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string,
|
metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string,
|
||||||
) (repo distribution.Repository, foundVersion bool, err error) {
|
) (repo distribution.Repository, err error) {
|
||||||
repoName := repoInfo.Name.Name()
|
repoName := repoInfo.Name.Name()
|
||||||
// If endpoint does not support CanonicalName, use the RemoteName instead
|
// If endpoint does not support CanonicalName, use the RemoteName instead
|
||||||
if endpoint.TrimHostname {
|
if endpoint.TrimHostname {
|
||||||
|
@ -84,16 +84,15 @@ func NewV2Repository(
|
||||||
modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders)
|
modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders)
|
||||||
authTransport := transport.NewTransport(base, modifiers...)
|
authTransport := transport.NewTransport(base, modifiers...)
|
||||||
|
|
||||||
challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport)
|
challengeManager, err := registry.PingV2Registry(endpoint.URL, authTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
transportOK := false
|
transportOK := false
|
||||||
if responseErr, ok := err.(registry.PingResponseError); ok {
|
if responseErr, ok := err.(registry.PingResponseError); ok {
|
||||||
transportOK = true
|
transportOK = true
|
||||||
err = responseErr.Err
|
err = responseErr.Err
|
||||||
}
|
}
|
||||||
return nil, foundVersion, fallbackError{
|
return nil, fallbackError{
|
||||||
err: err,
|
err: err,
|
||||||
confirmedV2: foundVersion,
|
|
||||||
transportOK: transportOK,
|
transportOK: transportOK,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,9 +122,8 @@ func NewV2Repository(
|
||||||
|
|
||||||
repoNameRef, err := reference.WithName(repoName)
|
repoNameRef, err := reference.WithName(repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, foundVersion, fallbackError{
|
return nil, fallbackError{
|
||||||
err: err,
|
err: err,
|
||||||
confirmedV2: foundVersion,
|
|
||||||
transportOK: true,
|
transportOK: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +132,6 @@ func NewV2Repository(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fallbackError{
|
err = fallbackError{
|
||||||
err: err,
|
err: err,
|
||||||
confirmedV2: foundVersion,
|
|
||||||
transportOK: true,
|
transportOK: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) {
|
||||||
}
|
}
|
||||||
p := puller.(*v2Puller)
|
p := puller.(*v2Puller)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
p.repo, _, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
|
p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,26 +87,19 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin
|
||||||
|
|
||||||
logrus.Debugf("attempting v2 login to registry endpoint %s", endpointStr)
|
logrus.Debugf("attempting v2 login to registry endpoint %s", endpointStr)
|
||||||
|
|
||||||
loginClient, foundV2, err := v2AuthHTTPClient(endpoint.URL, authTransport, modifiers, creds, nil)
|
loginClient, err := v2AuthHTTPClient(endpoint.URL, authTransport, modifiers, creds, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, endpointStr, nil)
|
req, err := http.NewRequest(http.MethodGet, endpointStr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !foundV2 {
|
|
||||||
err = fallbackError{err: err}
|
|
||||||
}
|
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := loginClient.Do(req)
|
resp, err := loginClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = translateV2AuthError(err)
|
err = translateV2AuthError(err)
|
||||||
if !foundV2 {
|
|
||||||
err = fallbackError{err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
@ -117,19 +110,13 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin
|
||||||
|
|
||||||
// TODO(dmcgowan): Attempt to further interpret result, status code and error code string
|
// TODO(dmcgowan): Attempt to further interpret result, status code and error code string
|
||||||
err = errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
|
err = errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||||
if !foundV2 {
|
|
||||||
err = fallbackError{err: err}
|
|
||||||
}
|
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, bool, error) {
|
func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) {
|
||||||
challengeManager, foundV2, err := PingV2Registry(endpoint, authTransport)
|
challengeManager, err := PingV2Registry(endpoint, authTransport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !foundV2 {
|
return nil, err
|
||||||
err = fallbackError{err: err}
|
|
||||||
}
|
|
||||||
return nil, foundV2, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenHandlerOptions := auth.TokenHandlerOptions{
|
tokenHandlerOptions := auth.TokenHandlerOptions{
|
||||||
|
@ -147,8 +134,7 @@ func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifi
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: tr,
|
Transport: tr,
|
||||||
Timeout: 15 * time.Second,
|
Timeout: 15 * time.Second,
|
||||||
}, foundV2, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertToHostname converts a registry url which has http|https prepended
|
// ConvertToHostname converts a registry url which has http|https prepended
|
||||||
|
@ -197,18 +183,9 @@ func (err PingResponseError) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PingV2Registry attempts to ping a v2 registry and on success return a
|
// PingV2Registry attempts to ping a v2 registry and on success return a
|
||||||
// challenge manager for the supported authentication types and
|
// challenge manager for the supported authentication types.
|
||||||
// whether v2 was confirmed by the response. If a response is received but
|
// If a response is received but cannot be interpreted, a PingResponseError will be returned.
|
||||||
// cannot be interpreted a PingResponseError will be returned.
|
func PingV2Registry(endpoint *url.URL, transport http.RoundTripper) (challenge.Manager, error) {
|
||||||
func PingV2Registry(endpoint *url.URL, transport http.RoundTripper) (challenge.Manager, bool, error) {
|
|
||||||
var (
|
|
||||||
foundV2 = false
|
|
||||||
v2Version = auth.APIVersion{
|
|
||||||
Type: "registry",
|
|
||||||
Version: "2.0",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
pingClient := &http.Client{
|
pingClient := &http.Client{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
Timeout: 15 * time.Second,
|
Timeout: 15 * time.Second,
|
||||||
|
@ -216,32 +193,20 @@ func PingV2Registry(endpoint *url.URL, transport http.RoundTripper) (challenge.M
|
||||||
endpointStr := strings.TrimRight(endpoint.String(), "/") + "/v2/"
|
endpointStr := strings.TrimRight(endpoint.String(), "/") + "/v2/"
|
||||||
req, err := http.NewRequest(http.MethodGet, endpointStr, nil)
|
req, err := http.NewRequest(http.MethodGet, endpointStr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp, err := pingClient.Do(req)
|
resp, err := pingClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
versions := auth.APIVersions(resp, DefaultRegistryVersionHeader)
|
|
||||||
for _, pingVersion := range versions {
|
|
||||||
if pingVersion == v2Version {
|
|
||||||
// The version header indicates we're definitely
|
|
||||||
// talking to a v2 registry. So don't allow future
|
|
||||||
// fallbacks to the v1 protocol.
|
|
||||||
|
|
||||||
foundV2 = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
challengeManager := challenge.NewSimpleManager()
|
challengeManager := challenge.NewSimpleManager()
|
||||||
if err := challengeManager.AddResponse(resp); err != nil {
|
if err := challengeManager.AddResponse(resp); err != nil {
|
||||||
return nil, foundV2, PingResponseError{
|
return nil, PingResponseError{
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return challengeManager, foundV2, nil
|
return challengeManager, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ type V1Endpoint struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewV1Endpoint parses the given address to return a registry endpoint.
|
// NewV1Endpoint parses the given address to return a registry endpoint.
|
||||||
|
// TODO: remove. This is only used by search.
|
||||||
func NewV1Endpoint(index *registrytypes.IndexInfo, userAgent string, metaHeaders http.Header) (*V1Endpoint, error) {
|
func NewV1Endpoint(index *registrytypes.IndexInfo, userAgent string, metaHeaders http.Header) (*V1Endpoint, error) {
|
||||||
tlsConfig, err := newTLSConfig(index.Name, index.Secure)
|
tlsConfig, err := newTLSConfig(index.Name, index.Secure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -131,16 +131,7 @@ func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
status, token, err = loginV2(authConfig, endpoint, userAgent)
|
return loginV2(authConfig, endpoint, userAgent)
|
||||||
if err == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if fErr, ok := err.(fallbackError); ok {
|
|
||||||
logrus.WithError(fErr.err).Infof("Error logging in to endpoint, trying next endpoint")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", "", err
|
return "", "", err
|
||||||
|
@ -194,14 +185,14 @@ func (s *DefaultService) Search(ctx context.Context, term string, limit int, aut
|
||||||
}
|
}
|
||||||
|
|
||||||
modifiers := Headers(userAgent, nil)
|
modifiers := Headers(userAgent, nil)
|
||||||
v2Client, foundV2, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes)
|
v2Client, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if fErr, ok := err.(fallbackError); ok {
|
if fErr, ok := err.(fallbackError); ok {
|
||||||
logrus.Errorf("Cannot use identity token for search, v2 auth not supported: %v", fErr.err)
|
logrus.Errorf("Cannot use identity token for search, v2 auth not supported: %v", fErr.err)
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if foundV2 {
|
} else {
|
||||||
// Copy non transport http client features
|
// Copy non transport http client features
|
||||||
v2Client.Timeout = endpoint.client.Timeout
|
v2Client.Timeout = endpoint.client.Timeout
|
||||||
v2Client.CheckRedirect = endpoint.client.CheckRedirect
|
v2Client.CheckRedirect = endpoint.client.CheckRedirect
|
||||||
|
|
Loading…
Reference in a new issue