Skip to content

Commit

Permalink
Merge pull request moby#43365 from thaJeztah/cleanup_distribution
Browse files Browse the repository at this point in the history
distribution: remove v1 leftovers, and refactor to reduce public api/interface
  • Loading branch information
thaJeztah authored Apr 26, 2022
2 parents 104ac2c + 2b0da89 commit 9184f0b
Show file tree
Hide file tree
Showing 15 changed files with 305 additions and 372 deletions.
36 changes: 6 additions & 30 deletions daemon/images/image_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference
ReferenceStore: i.referenceStore,
},
DownloadManager: i.downloadManager,
Schema2Types: distribution.ImageTypes,
Platform: platform,
}

Expand All @@ -134,35 +133,12 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference

// GetRepository returns a repository from the registry.
func (i *ImageService) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, error) {
// get repository info
repoInfo, err := i.registryService.ResolveRepository(ref)
if err != nil {
return nil, errdefs.InvalidParameter(err)
}
// makes sure name is not empty or `scratch`
if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
return nil, errdefs.InvalidParameter(err)
}

// get endpoints
endpoints, err := i.registryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
if err != nil {
return nil, err
}

// retrieve repository
var (
repository dist.Repository
lastError error
)

for _, endpoint := range endpoints {
repository, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull")
if lastError == nil {
break
}
}
return repository, lastError
return distribution.GetRepository(ctx, ref, &distribution.ImagePullConfig{
Config: distribution.Config{
AuthConfig: authConfig,
RegistryService: i.registryService,
},
})
}

func tempLease(ctx context.Context, mgr leases.Manager) (context.Context, func(context.Context) error, error) {
Expand Down
18 changes: 11 additions & 7 deletions distribution/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ type ImagePullConfig struct {

// DownloadManager manages concurrent pulls.
DownloadManager *xfer.LayerDownloadManager
// Schema2Types is the valid schema2 configuration types allowed
// by the pull operation.
// Schema2Types is an optional list of valid schema2 configuration types
// allowed by the pull operation. If omitted, the default list of accepted
// types is used.
Schema2Types []string
// Platform is the requested platform of the image being pulled
Platform *specs.Platform
Expand Down Expand Up @@ -86,8 +87,6 @@ type ImagePushConfig struct {
type ImageConfigStore interface {
Put(context.Context, []byte) (digest.Digest, error)
Get(context.Context, digest.Digest) ([]byte, error)
RootFSFromConfig([]byte) (*image.RootFS, error)
PlatformFromConfig([]byte) (*specs.Platform, error)
}

// PushLayerProvider provides layers to be pushed by ChainID.
Expand Down Expand Up @@ -132,15 +131,15 @@ func (s *imageConfigStore) Get(_ context.Context, d digest.Digest) ([]byte, erro
return img.RawJSON(), nil
}

func (s *imageConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) {
func rootFSFromConfig(c []byte) (*image.RootFS, error) {
var unmarshalledConfig image.Image
if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
return nil, err
}
return unmarshalledConfig.RootFS, nil
}

func (s *imageConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) {
func platformFromConfig(c []byte) (*specs.Platform, error) {
var unmarshalledConfig image.Image
if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
return nil, err
Expand All @@ -153,7 +152,12 @@ func (s *imageConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error)
if !system.IsOSSupported(os) {
return nil, errors.Wrapf(system.ErrNotSupportedOperatingSystem, "image operating system %q cannot be used on this platform", os)
}
return &specs.Platform{OS: os, Architecture: unmarshalledConfig.Architecture, Variant: unmarshalledConfig.Variant, OSVersion: unmarshalledConfig.OSVersion}, nil
return &specs.Platform{
OS: os,
Architecture: unmarshalledConfig.Architecture,
Variant: unmarshalledConfig.Variant,
OSVersion: unmarshalledConfig.OSVersion,
}, nil
}

type storeLayerProvider struct {
Expand Down
27 changes: 7 additions & 20 deletions distribution/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,6 @@ import (
"github.com/sirupsen/logrus"
)

// ErrNoSupport is an error type used for errors indicating that an operation
// is not supported. It encapsulates a more specific error.
type ErrNoSupport struct{ Err error }

func (e ErrNoSupport) Error() string {
if e.Err == nil {
return "not supported"
}
return e.Err.Error()
}

// fallbackError wraps an error that can possibly allow fallback to a different
// endpoint.
type fallbackError struct {
Expand Down Expand Up @@ -74,26 +63,26 @@ func (e notFoundError) Cause() error {
return e.cause
}

// TranslatePullError is used to convert an error from a registry pull
// translatePullError is used to convert an error from a registry pull
// operation to an error representing the entire pull operation. Any error
// information which is not used by the returned error gets output to
// log at info level.
func TranslatePullError(err error, ref reference.Named) error {
func translatePullError(err error, ref reference.Named) error {
switch v := err.(type) {
case errcode.Errors:
if len(v) != 0 {
for _, extra := range v[1:] {
logrus.Infof("Ignoring extra error returned from registry: %v", extra)
logrus.WithError(extra).Infof("Ignoring extra error returned from registry")
}
return TranslatePullError(v[0], ref)
return translatePullError(v[0], ref)
}
case errcode.Error:
switch v.Code {
case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
return notFoundError{v, ref}
}
case xfer.DoNotRetry:
return TranslatePullError(v.Err, ref)
return translatePullError(v.Err, ref)
}

return errdefs.Unknown(err)
Expand Down Expand Up @@ -125,14 +114,12 @@ func continueOnError(err error, mirrorEndpoint bool) bool {
return true
}
return continueOnError(v[0], mirrorEndpoint)
case ErrNoSupport:
return continueOnError(v.Err, mirrorEndpoint)
case errcode.Error:
return mirrorEndpoint
case *client.UnexpectedHTTPResponseError:
return true
case ImageConfigPullError:
// ImageConfigPullError only happens with v2 images, v1 fallback is
case imageConfigPullError:
// imageConfigPullError only happens with v2 images, v1 fallback is
// unnecessary.
// Failures from a mirror endpoint should result in fallback to the
// canonical repo.
Expand Down
4 changes: 1 addition & 3 deletions distribution/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ var alwaysContinue = []error{
errUnexpected,
// nested
errcode.Errors{errUnexpected},
ErrNoSupport{Err: errUnexpected},
}

var continueFromMirrorEndpoint = []error{
ImageConfigPullError{},
imageConfigPullError{},
errcode.Error{},
// nested
errcode.Errors{errcode.Error{}},
ErrNoSupport{Err: errcode.Error{}},
}

var neverContinue = []error{
Expand Down
91 changes: 12 additions & 79 deletions distribution/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,73 +6,34 @@ import (

"github.com/docker/distribution/reference"
"github.com/docker/docker/api"
"github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/pkg/progress"
refstore "github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

// Puller is an interface that abstracts pulling for different API versions.
type Puller interface {
// Pull tries to pull the image referenced by `tag`
// Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint.
//
Pull(ctx context.Context, ref reference.Named) error
}

// newPuller returns a Puller interface that will pull from a v2 registry.
func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, local ContentStore) (Puller, error) {
switch endpoint.Version {
case registry.APIVersion2:
return &v2Puller{
V2MetadataService: metadata.NewV2MetadataService(imagePullConfig.MetadataStore),
endpoint: endpoint,
config: imagePullConfig,
repoInfo: repoInfo,
manifestStore: &manifestStore{
local: local,
},
}, nil
case registry.APIVersion1:
return nil, fmt.Errorf("protocol version %d no longer supported. Please contact admins of registry %s", endpoint.Version, endpoint.URL)
}
return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
}

// Pull initiates a pull operation. image is the repository name to pull, and
// tag may be either empty, or indicate a specific tag to pull.
func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullConfig, local ContentStore) error {
func Pull(ctx context.Context, ref reference.Named, config *ImagePullConfig, local ContentStore) error {
// Resolve the Repository name from fqn to RepositoryInfo
repoInfo, err := imagePullConfig.RegistryService.ResolveRepository(ref)
repoInfo, err := config.RegistryService.ResolveRepository(ref)
if err != nil {
return err
}

// makes sure name is not `scratch`
if err := ValidateRepoName(repoInfo.Name); err != nil {
if err := validateRepoName(repoInfo.Name); err != nil {
return err
}

endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
endpoints, err := config.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
if err != nil {
return err
}

var (
lastErr error

// discardNoSupportErrors is used to track whether an endpoint encountered an error of type registry.ErrNoSupport
// By default it is false, which means that if an ErrNoSupport error is encountered, it will be saved in lastErr.
// As soon as another kind of error is encountered, discardNoSupportErrors is set to true, avoiding the saving of
// any subsequent ErrNoSupport errors in lastErr.
// It's needed for pull-by-digest on v1 endpoints: if there are only v1 endpoints configured, the error should be
// returned and displayed, but if there was a v2 endpoint which supports pull-by-digest, then the last relevant
// error is the ones from v2 endpoints not v1.
discardNoSupportErrors bool

// confirmedTLSRegistries is a map indicating which registries
// are known to be using TLS. There should never be a plaintext
// retry for any of these.
Expand All @@ -86,15 +47,9 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
}
}

logrus.Debugf("Trying to pull %s from %s %s", reference.FamiliarName(repoInfo.Name), endpoint.URL, endpoint.Version)
logrus.Debugf("Trying to pull %s from %s", reference.FamiliarName(repoInfo.Name), endpoint.URL)

puller, err := newPuller(endpoint, repoInfo, imagePullConfig, local)
if err != nil {
lastErr = err
continue
}

if err := puller.Pull(ctx, ref); err != nil {
if err := newPuller(endpoint, repoInfo, config, local).pull(ctx, ref); err != nil {
// Was this pull cancelled? If so, don't try to fall
// back.
fallback := false
Expand All @@ -110,49 +65,27 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
}
}
if fallback {
if _, ok := err.(ErrNoSupport); !ok {
// Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors.
discardNoSupportErrors = true
// append subsequent errors
lastErr = err
} else if !discardNoSupportErrors {
// Save the ErrNoSupport error, because it's either the first error or all encountered errors
// were also ErrNoSupport errors.
// append subsequent errors
lastErr = err
}
lastErr = err
logrus.Infof("Attempting next endpoint for pull after error: %v", err)
continue
}
logrus.Errorf("Not continuing with pull after error: %v", err)
return TranslatePullError(err, ref)
return translatePullError(err, ref)
}

imagePullConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "pull")
config.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "pull")
return nil
}

if lastErr == nil {
lastErr = fmt.Errorf("no endpoints found for %s", reference.FamiliarString(ref))
}

return TranslatePullError(lastErr, ref)
}

// writeStatus writes a status message to out. If layersDownloaded is true, the
// status message indicates that a newer image was downloaded. Otherwise, it
// indicates that the image is up to date. requestedTag is the tag the message
// will refer to.
func writeStatus(requestedTag string, out progress.Output, layersDownloaded bool) {
if layersDownloaded {
progress.Message(out, "", "Status: Downloaded newer image for "+requestedTag)
} else {
progress.Message(out, "", "Status: Image is up to date for "+requestedTag)
}
return translatePullError(lastErr, ref)
}

// ValidateRepoName validates the name of a repository.
func ValidateRepoName(name reference.Named) error {
// validateRepoName validates the name of a repository.
func validateRepoName(name reference.Named) error {
if reference.FamiliarName(name) == api.NoBaseImageSpecifier {
return errors.WithStack(reservedNameError(api.NoBaseImageSpecifier))
}
Expand Down
Loading

0 comments on commit 9184f0b

Please sign in to comment.