From fb5485f5d09443596ca1c49dd84876296a4fc1e3 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 27 Feb 2022 20:33:52 +0100 Subject: [PATCH 1/7] distribution: un-export ImageTypes, make ImagePullConfig.Schema2Types optional Use the default list of accepted mediaTypes if none were passed. Signed-off-by: Sebastiaan van Stijn --- daemon/images/image_pull.go | 1 - distribution/config.go | 5 ++-- distribution/pull_v2.go | 37 +++++++++++++++--------- distribution/pull_v2_test.go | 1 - distribution/registry.go | 45 +++++++++++++++--------------- distribution/registry_unit_test.go | 1 - 6 files changed, 50 insertions(+), 40 deletions(-) diff --git a/daemon/images/image_pull.go b/daemon/images/image_pull.go index d294ea242c2f7..bc8dba42f5e31 100644 --- a/daemon/images/image_pull.go +++ b/daemon/images/image_pull.go @@ -122,7 +122,6 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference ReferenceStore: i.referenceStore, }, DownloadManager: i.downloadManager, - Schema2Types: distribution.ImageTypes, Platform: platform, } diff --git a/distribution/config.go b/distribution/config.go index af6a497b98ca2..52ed9ddbfcf6f 100644 --- a/distribution/config.go +++ b/distribution/config.go @@ -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 diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 864baeda441da..8479a961e4dda 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -397,19 +397,8 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform } if m, ok := manifest.(*schema2.DeserializedManifest); ok { - var allowedMediatype bool - for _, t := range p.config.Schema2Types { - if m.Manifest.Config.MediaType == t { - allowedMediatype = true - break - } - } - if !allowedMediatype { - configClass := mediaTypeClasses[m.Manifest.Config.MediaType] - if configClass == "" { - configClass = "unknown" - } - return false, invalidManifestClassError{m.Manifest.Config.MediaType, configClass} + if err := p.validateMediaType(m.Manifest.Config.MediaType); err != nil { + return false, err } } @@ -486,6 +475,28 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform return true, nil } +// validateMediaType validates if the given mediaType is accepted by the puller's +// configuration. +func (p *v2Puller) validateMediaType(mediaType string) error { + var allowedMediaTypes []string + if len(p.config.Schema2Types) > 0 { + allowedMediaTypes = p.config.Schema2Types + } else { + allowedMediaTypes = defaultImageTypes + } + for _, t := range allowedMediaTypes { + if mediaType == t { + return nil + } + } + + configClass := mediaTypeClasses[mediaType] + if configClass == "" { + configClass = "unknown" + } + return invalidManifestClassError{mediaType, configClass} +} + func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { if platform != nil { // Early bath if the requested OS doesn't match that of the configuration. diff --git a/distribution/pull_v2_test.go b/distribution/pull_v2_test.go index ce1384276d5fc..4918802b26708 100644 --- a/distribution/pull_v2_test.go +++ b/distribution/pull_v2_test.go @@ -356,7 +356,6 @@ func testNewPuller(t *testing.T, rawurl string) *v2Puller { RegistryToken: secretRegistryToken, }, }, - Schema2Types: ImageTypes, } puller, err := newPuller(endpoint, repoInfo, imagePullConfig, nil) diff --git a/distribution/registry.go b/distribution/registry.go index 11083cdd46417..904ffff2becad 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -19,35 +19,36 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) -// ImageTypes represents the schema2 config types for images -var ImageTypes = []string{ - schema2.MediaTypeImageConfig, - ocispec.MediaTypeImageConfig, - // Handle unexpected values from https://github.com/docker/distribution/issues/1621 - // (see also https://github.com/docker/docker/issues/22378, - // https://github.com/docker/docker/issues/30083) - "application/octet-stream", - "application/json", - "text/html", - // Treat defaulted values as images, newer types cannot be implied - "", -} +var ( + // defaultImageTypes represents the schema2 config types for images + defaultImageTypes = []string{ + schema2.MediaTypeImageConfig, + ocispec.MediaTypeImageConfig, + // Handle unexpected values from https://github.com/docker/distribution/issues/1621 + // (see also https://github.com/docker/docker/issues/22378, + // https://github.com/docker/docker/issues/30083) + "application/octet-stream", + "application/json", + "text/html", + // Treat defaulted values as images, newer types cannot be implied + "", + } -// PluginTypes represents the schema2 config types for plugins -var PluginTypes = []string{ - schema2.MediaTypePluginConfig, -} + // pluginTypes represents the schema2 config types for plugins + pluginTypes = []string{ + schema2.MediaTypePluginConfig, + } -var mediaTypeClasses map[string]string + mediaTypeClasses map[string]string +) func init() { - // initialize media type classes with all know types for - // plugin + // initialize media type classes with all know types for images and plugins. mediaTypeClasses = map[string]string{} - for _, t := range ImageTypes { + for _, t := range defaultImageTypes { mediaTypeClasses[t] = "image" } - for _, t := range PluginTypes { + for _, t := range pluginTypes { mediaTypeClasses[t] = "plugin" } } diff --git a/distribution/registry_unit_test.go b/distribution/registry_unit_test.go index 3cbd09f5621bb..136fd516cf096 100644 --- a/distribution/registry_unit_test.go +++ b/distribution/registry_unit_test.go @@ -67,7 +67,6 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) { RegistryToken: secretRegistryToken, }, }, - Schema2Types: ImageTypes, } puller, err := newPuller(endpoint, repoInfo, imagePullConfig, nil) if err != nil { From 074e41679d53d5d0d48c10839d6904edb7e55834 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 27 Feb 2022 20:46:24 +0100 Subject: [PATCH 2/7] distribution: un-export internal errors and error-utilities un-exports errors that were only used internally: - Remove ErrNoSupport as it was not emitted anywhere - ImageConfigPullError -> imageConfigPullError - TranslatePullError() -> translatePullError() Signed-off-by: Sebastiaan van Stijn --- distribution/errors.go | 27 +++++++-------------------- distribution/errors_test.go | 4 +--- distribution/pull.go | 25 +++---------------------- distribution/pull_v2.go | 10 +++++----- 4 files changed, 16 insertions(+), 50 deletions(-) diff --git a/distribution/errors.go b/distribution/errors.go index 6b67d53ff4294..fa1e2214eb2e6 100644 --- a/distribution/errors.go +++ b/distribution/errors.go @@ -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 { @@ -74,18 +63,18 @@ 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 { @@ -93,7 +82,7 @@ func TranslatePullError(err error, ref reference.Named) error { return notFoundError{v, ref} } case xfer.DoNotRetry: - return TranslatePullError(v.Err, ref) + return translatePullError(v.Err, ref) } return errdefs.Unknown(err) @@ -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. diff --git a/distribution/errors_test.go b/distribution/errors_test.go index 164c57a650fd7..f1a3a45785aaa 100644 --- a/distribution/errors_test.go +++ b/distribution/errors_test.go @@ -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{ diff --git a/distribution/pull.go b/distribution/pull.go index e86205bf70636..5e8312d839f58 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -64,15 +64,6 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo 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. @@ -110,22 +101,12 @@ 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") @@ -136,7 +117,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo lastErr = fmt.Errorf("no endpoints found for %s", reference.FamiliarString(ref)) } - return TranslatePullError(lastErr, ref) + return translatePullError(lastErr, ref) } // writeStatus writes a status message to out. If layersDownloaded is true, the diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 8479a961e4dda..ab5ac9b34e8eb 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -41,14 +41,14 @@ var ( errRootFSInvalid = errors.New("invalid rootfs in image configuration") ) -// ImageConfigPullError is an error pulling the image config blob +// imageConfigPullError is an error pulling the image config blob // (only applies to schema2). -type ImageConfigPullError struct { +type imageConfigPullError struct { Err error } -// Error returns the error string for ImageConfigPullError. -func (e ImageConfigPullError) Error() string { +// Error returns the error string for imageConfigPullError. +func (e imageConfigPullError) Error() string { return "error pulling image configuration: " + e.Err.Error() } @@ -619,7 +619,7 @@ func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.De go func() { configJSON, err := p.pullSchema2Config(ctx, target.Digest) if err != nil { - configErrChan <- ImageConfigPullError{Err: err} + configErrChan <- imageConfigPullError{Err: err} cancel() return } From 41999abcbed3474b1e46ec4cdd940db774ecbb1c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 27 Feb 2022 20:57:07 +0100 Subject: [PATCH 3/7] distribution: remove Puller interface, remove redundant V1 checks It's only used internally, so we can refer to the implementation itself. Given that RegistryService.LookupPullEndpoints now only returns V2 endpoints, we no longer need to check if an endpoint is possibly V1. Also rename some types that had "v2" in their name, now that we only support v2. Signed-off-by: Sebastiaan van Stijn --- distribution/pull.go | 52 +++++---------- distribution/pull_v2.go | 101 ++++++++++++++--------------- distribution/pull_v2_test.go | 9 +-- distribution/pull_v2_unix.go | 2 +- distribution/pull_v2_windows.go | 6 +- distribution/registry_unit_test.go | 8 +-- 6 files changed, 74 insertions(+), 104 deletions(-) diff --git a/distribution/pull.go b/distribution/pull.go index 5e8312d839f58..baba8452649c5 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -15,38 +15,24 @@ import ( "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) +// newPuller returns a puller to pull from a v2 registry. +func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, config *ImagePullConfig, local ContentStore) *puller { + return &puller{ + metadataService: metadata.NewV2MetadataService(config.MetadataStore), + endpoint: endpoint, + config: config, + repoInfo: repoInfo, + manifestStore: &manifestStore{ + local: local, + }, } - 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 } @@ -56,7 +42,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo 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 } @@ -77,15 +63,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) - - puller, err := newPuller(endpoint, repoInfo, imagePullConfig, local) - if err != nil { - lastErr = err - continue - } + logrus.Debugf("Trying to pull %s from %s", reference.FamiliarName(repoInfo.Name), endpoint.URL) - 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 @@ -109,7 +89,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo 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 } diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index ab5ac9b34e8eb..090ba10f25970 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -52,16 +52,16 @@ func (e imageConfigPullError) Error() string { return "error pulling image configuration: " + e.Err.Error() } -type v2Puller struct { - V2MetadataService metadata.V2MetadataService - endpoint registry.APIEndpoint - config *ImagePullConfig - repoInfo *registry.RepositoryInfo - repo distribution.Repository - manifestStore *manifestStore +type puller struct { + metadataService metadata.V2MetadataService + endpoint registry.APIEndpoint + config *ImagePullConfig + repoInfo *registry.RepositoryInfo + repo distribution.Repository + manifestStore *manifestStore } -func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) { +func (p *puller) pull(ctx context.Context, ref reference.Named) (err error) { // TODO(tiborvass): was ReceiveTimeout p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { @@ -74,7 +74,7 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) { return err } - if err = p.pullV2Repository(ctx, ref); err != nil { + if err = p.pullRepository(ctx, ref); err != nil { if _, ok := err.(fallbackError); ok { return err } @@ -88,10 +88,10 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) { return err } -func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) { +func (p *puller) pullRepository(ctx context.Context, ref reference.Named) (err error) { var layersDownloaded bool if !reference.IsNameOnly(ref) { - layersDownloaded, err = p.pullV2Tag(ctx, ref, p.config.Platform) + layersDownloaded, err = p.pullTag(ctx, ref, p.config.Platform) if err != nil { return err } @@ -106,7 +106,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e if err != nil { return err } - pulledNew, err := p.pullV2Tag(ctx, tagRef, p.config.Platform) + pulledNew, err := p.pullTag(ctx, tagRef, p.config.Platform) if err != nil { // Since this is the pull-all-tags case, don't // allow an error pulling a particular tag to @@ -127,33 +127,33 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e return nil } -type v2LayerDescriptor struct { - digest digest.Digest - diffID layer.DiffID - repoInfo *registry.RepositoryInfo - repo distribution.Repository - V2MetadataService metadata.V2MetadataService - tmpFile *os.File - verifier digest.Verifier - src distribution.Descriptor +type layerDescriptor struct { + digest digest.Digest + diffID layer.DiffID + repoInfo *registry.RepositoryInfo + repo distribution.Repository + metadataService metadata.V2MetadataService + tmpFile *os.File + verifier digest.Verifier + src distribution.Descriptor } -func (ld *v2LayerDescriptor) Key() string { +func (ld *layerDescriptor) Key() string { return "v2:" + ld.digest.String() } -func (ld *v2LayerDescriptor) ID() string { +func (ld *layerDescriptor) ID() string { return stringid.TruncateID(ld.digest.String()) } -func (ld *v2LayerDescriptor) DiffID() (layer.DiffID, error) { +func (ld *layerDescriptor) DiffID() (layer.DiffID, error) { if ld.diffID != "" { return ld.diffID, nil } - return ld.V2MetadataService.GetDiffID(ld.digest) + return ld.metadataService.GetDiffID(ld.digest) } -func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) { +func (ld *layerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) { logrus.Debugf("pulling blob %q", ld.digest) var ( @@ -291,7 +291,7 @@ func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progre }), size, nil } -func (ld *v2LayerDescriptor) Close() { +func (ld *layerDescriptor) Close() { if ld.tmpFile != nil { ld.tmpFile.Close() if err := os.RemoveAll(ld.tmpFile.Name()); err != nil { @@ -300,7 +300,7 @@ func (ld *v2LayerDescriptor) Close() { } } -func (ld *v2LayerDescriptor) truncateDownloadFile() error { +func (ld *layerDescriptor) truncateDownloadFile() error { // Need a new hash context since we will be redoing the download ld.verifier = nil @@ -317,13 +317,12 @@ func (ld *v2LayerDescriptor) truncateDownloadFile() error { return nil } -func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) { +func (ld *layerDescriptor) Registered(diffID layer.DiffID) { // Cache mapping from this layer's DiffID to the blobsum - ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()}) + _ = ld.metadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()}) } -func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform *specs.Platform) (tagUpdated bool, err error) { - +func (p *puller) pullTag(ctx context.Context, ref reference.Named, platform *specs.Platform) (tagUpdated bool, err error) { var ( tagOrDigest string // Used for logging/progress only dgst digest.Digest @@ -477,7 +476,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform // validateMediaType validates if the given mediaType is accepted by the puller's // configuration. -func (p *v2Puller) validateMediaType(mediaType string) error { +func (p *puller) validateMediaType(mediaType string) error { var allowedMediaTypes []string if len(p.config.Schema2Types) > 0 { allowedMediaTypes = p.config.Schema2Types @@ -497,7 +496,7 @@ func (p *v2Puller) validateMediaType(mediaType string) error { return invalidManifestClassError{mediaType, configClass} } -func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { +func (p *puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { if platform != nil { // Early bath if the requested OS doesn't match that of the configuration. // This avoids doing the download, only to potentially fail later. @@ -550,11 +549,11 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv continue } - layerDescriptor := &v2LayerDescriptor{ - digest: blobSum, - repoInfo: p.repoInfo, - repo: p.repo, - V2MetadataService: p.V2MetadataService, + layerDescriptor := &layerDescriptor{ + digest: blobSum, + repoInfo: p.repoInfo, + repo: p.repo, + metadataService: p.metadataService, } descriptors = append(descriptors, layerDescriptor) @@ -581,7 +580,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv return imageID, manifestDigest, nil } -func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.Descriptor, layers []distribution.Descriptor, platform *specs.Platform) (id digest.Digest, err error) { +func (p *puller) pullSchema2Layers(ctx context.Context, target distribution.Descriptor, layers []distribution.Descriptor, platform *specs.Platform) (id digest.Digest, err error) { if _, err := p.config.ImageStore.Get(ctx, target.Digest); err == nil { // If the image already exists locally, no need to pull // anything. @@ -596,12 +595,12 @@ func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.De if err := d.Digest.Validate(); err != nil { return "", errors.Wrapf(err, "could not validate layer digest %q", d.Digest) } - layerDescriptor := &v2LayerDescriptor{ - digest: d.Digest, - repo: p.repo, - repoInfo: p.repoInfo, - V2MetadataService: p.V2MetadataService, - src: d, + layerDescriptor := &layerDescriptor{ + digest: d.Digest, + repo: p.repo, + repoInfo: p.repoInfo, + metadataService: p.metadataService, + src: d, } descriptors = append(descriptors, layerDescriptor) @@ -674,7 +673,7 @@ func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.De // Populate diff ids in descriptors to avoid downloading foreign layers // which have been side loaded for i := range descriptors { - descriptors[i].(*v2LayerDescriptor).diffID = configRootFS.DiffIDs[i] + descriptors[i].(*layerDescriptor).diffID = configRootFS.DiffIDs[i] } } @@ -756,7 +755,7 @@ func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.De return imageID, nil } -func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { +func (p *puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { manifestDigest, err = schema2ManifestDigest(ref, mfst) if err != nil { return "", "", err @@ -765,7 +764,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s return id, manifestDigest, err } -func (p *v2Puller) pullOCI(ctx context.Context, ref reference.Named, mfst *ocischema.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { +func (p *puller) pullOCI(ctx context.Context, ref reference.Named, mfst *ocischema.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { manifestDigest, err = schema2ManifestDigest(ref, mfst) if err != nil { return "", "", err @@ -795,7 +794,7 @@ func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan // pullManifestList handles "manifest lists" which point to various // platform-specific manifests. -func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, pp *specs.Platform) (id digest.Digest, manifestListDigest digest.Digest, err error) { +func (p *puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, pp *specs.Platform) (id digest.Digest, manifestListDigest digest.Digest, err error) { manifestListDigest, err = schema2ManifestDigest(ref, mfstList) if err != nil { return "", "", err @@ -874,7 +873,7 @@ const ( defaultMaxSchemaPullAttempts = 5 ) -func (p *v2Puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (configJSON []byte, err error) { +func (p *puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (configJSON []byte, err error) { blobs := p.repo.Blobs(ctx) err = retry(ctx, defaultMaxSchemaPullAttempts, defaultSchemaPullBackoff, func(ctx context.Context) (err error) { configJSON, err = blobs.Get(ctx, dgst) diff --git a/distribution/pull_v2_test.go b/distribution/pull_v2_test.go index 4918802b26708..a080c9eadf139 100644 --- a/distribution/pull_v2_test.go +++ b/distribution/pull_v2_test.go @@ -322,7 +322,7 @@ func TestPullSchema2Config(t *testing.T) { } } -func testNewPuller(t *testing.T, rawurl string) *v2Puller { +func testNewPuller(t *testing.T, rawurl string) *puller { t.Helper() uri, err := url.Parse(rawurl) @@ -358,12 +358,7 @@ func testNewPuller(t *testing.T, rawurl string) *v2Puller { }, } - puller, err := newPuller(endpoint, repoInfo, imagePullConfig, nil) - if err != nil { - t.Fatal(err) - } - p := puller.(*v2Puller) - + p := newPuller(endpoint, repoInfo, imagePullConfig, nil) p.repo, err = NewV2Repository(context.Background(), p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { t.Fatal(err) diff --git a/distribution/pull_v2_unix.go b/distribution/pull_v2_unix.go index 2c10bb5ba1822..ca4814a472b97 100644 --- a/distribution/pull_v2_unix.go +++ b/distribution/pull_v2_unix.go @@ -14,7 +14,7 @@ import ( "github.com/sirupsen/logrus" ) -func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) { +func (ld *layerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) { blobs := ld.repo.Blobs(ctx) return blobs.Open(ctx, ld.digest) } diff --git a/distribution/pull_v2_windows.go b/distribution/pull_v2_windows.go index a6db4b9b9eac3..acd67feb596b0 100644 --- a/distribution/pull_v2_windows.go +++ b/distribution/pull_v2_windows.go @@ -22,16 +22,16 @@ import ( "github.com/sirupsen/logrus" ) -var _ distribution.Describable = &v2LayerDescriptor{} +var _ distribution.Describable = &layerDescriptor{} -func (ld *v2LayerDescriptor) Descriptor() distribution.Descriptor { +func (ld *layerDescriptor) Descriptor() distribution.Descriptor { if ld.src.MediaType == schema2.MediaTypeForeignLayer && len(ld.src.URLs) > 0 { return ld.src } return distribution.Descriptor{} } -func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) { +func (ld *layerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) { blobs := ld.repo.Blobs(ctx) rsc, err := blobs.Open(ctx, ld.digest) diff --git a/distribution/registry_unit_test.go b/distribution/registry_unit_test.go index 136fd516cf096..df9e12a99a8ba 100644 --- a/distribution/registry_unit_test.go +++ b/distribution/registry_unit_test.go @@ -68,11 +68,7 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) { }, }, } - puller, err := newPuller(endpoint, repoInfo, imagePullConfig, nil) - if err != nil { - t.Fatal(err) - } - p := puller.(*v2Puller) + p := newPuller(endpoint, repoInfo, imagePullConfig, nil) ctx := context.Background() p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { @@ -82,7 +78,7 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) { logrus.Debug("About to pull") // We expect it to fail, since we haven't mock'd the full registry exchange in our handler above tag, _ := reference.WithTag(n, "tag_goes_here") - _ = p.pullV2Repository(ctx, tag) + _ = p.pullRepository(ctx, tag) } func TestTokenPassThru(t *testing.T) { From 1e75ab0ab9ac2517eba1ac4966b6ad6ade4b71e5 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 27 Feb 2022 21:33:35 +0100 Subject: [PATCH 4/7] distribution: remove Pusher interface, NewPusher(), and redundant V1 checks It's only used internally, so we can refer to the implementation itself. Given that RegistryService.LookupPushEndpoints now only returns V2 endpoints, we no longer need to check if an endpoint is possibly V1. Also rename some types that had "v2" in their name, now that we only support v2. Signed-off-by: Sebastiaan van Stijn --- distribution/push.go | 63 +++++++-------------- distribution/push_v2.go | 107 ++++++++++++++++++----------------- distribution/push_v2_test.go | 20 +++---- 3 files changed, 84 insertions(+), 106 deletions(-) diff --git a/distribution/push.go b/distribution/push.go index f64d0f822c080..026c29f72610d 100644 --- a/distribution/push.go +++ b/distribution/push.go @@ -14,58 +14,40 @@ import ( "github.com/sirupsen/logrus" ) -// Pusher is an interface that abstracts pushing for different API versions. -type Pusher interface { - // Push tries to push the image configured at the creation of Pusher. - // Push returns an error if any, as well as a boolean that determines whether to retry Push on the next configured endpoint. - // - // TODO(tiborvass): have Push() take a reference to repository + tag, so that the pusher itself is repository-agnostic. - Push(ctx context.Context) error -} - const compressionBufSize = 32768 -// NewPusher creates a new Pusher interface that will push to either a v1 or v2 -// registry. The endpoint argument contains a Version field that determines -// whether a v1 or v2 pusher will be created. The other parameters are passed -// through to the underlying pusher implementation for use during the actual -// push operation. -func NewPusher(ref reference.Named, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePushConfig *ImagePushConfig) (Pusher, error) { - switch endpoint.Version { - case registry.APIVersion2: - return &v2Pusher{ - v2MetadataService: metadata.NewV2MetadataService(imagePushConfig.MetadataStore), - ref: ref, - endpoint: endpoint, - repoInfo: repoInfo, - config: imagePushConfig, - }, nil - case registry.APIVersion1: - return nil, fmt.Errorf("protocol version %d no longer supported. Please contact admins of registry %s", endpoint.Version, endpoint.URL) +// newPusher creates a new pusher for pushing to a v2 registry. +// The parameters are passed through to the underlying pusher implementation for +// use during the actual push operation. +func newPusher(ref reference.Named, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, config *ImagePushConfig) *pusher { + return &pusher{ + metadataService: metadata.NewV2MetadataService(config.MetadataStore), + ref: ref, + endpoint: endpoint, + repoInfo: repoInfo, + config: config, } - return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL) } -// Push initiates a push operation on ref. -// ref is the specific variant of the image to be pushed. -// If no tag is provided, all tags will be pushed. -func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushConfig) error { +// Push initiates a push operation on ref. ref is the specific variant of the +// image to push. If no tag is provided, all tags are pushed. +func Push(ctx context.Context, ref reference.Named, config *ImagePushConfig) error { // FIXME: Allow to interrupt current push when new push of same image is done. // Resolve the Repository name from fqn to RepositoryInfo - repoInfo, err := imagePushConfig.RegistryService.ResolveRepository(ref) + repoInfo, err := config.RegistryService.ResolveRepository(ref) if err != nil { return err } - endpoints, err := imagePushConfig.RegistryService.LookupPushEndpoints(reference.Domain(repoInfo.Name)) + endpoints, err := config.RegistryService.LookupPushEndpoints(reference.Domain(repoInfo.Name)) if err != nil { return err } - progress.Messagef(imagePushConfig.ProgressOutput, "", "The push refers to repository [%s]", repoInfo.Name.Name()) + progress.Messagef(config.ProgressOutput, "", "The push refers to repository [%s]", repoInfo.Name.Name()) - associations := imagePushConfig.ReferenceStore.ReferencesByName(repoInfo.Name) + associations := config.ReferenceStore.ReferencesByName(repoInfo.Name) if len(associations) == 0 { return fmt.Errorf("An image does not exist locally with the tag: %s", reference.FamiliarName(repoInfo.Name)) } @@ -87,14 +69,9 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo } } - logrus.Debugf("Trying to push %s to %s %s", repoInfo.Name.Name(), endpoint.URL, endpoint.Version) + logrus.Debugf("Trying to push %s to %s", repoInfo.Name.Name(), endpoint.URL) - pusher, err := NewPusher(ref, endpoint, repoInfo, imagePushConfig) - if err != nil { - lastErr = err - continue - } - if err := pusher.Push(ctx); err != nil { + if err := newPusher(ref, endpoint, repoInfo, config).push(ctx); err != nil { // Was this push cancelled? If so, don't try to fall // back. select { @@ -115,7 +92,7 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo return err } - imagePushConfig.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "push") + config.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "push") return nil } diff --git a/distribution/push_v2.go b/distribution/push_v2.go index 4aa4f601752fd..372705dc9ce33 100644 --- a/distribution/push_v2.go +++ b/distribution/push_v2.go @@ -34,13 +34,13 @@ const ( middleLayerMaximumSize = 10 * (1 << 20) // 10MB ) -type v2Pusher struct { - v2MetadataService metadata.V2MetadataService - ref reference.Named - endpoint registry.APIEndpoint - repoInfo *registry.RepositoryInfo - config *ImagePushConfig - repo distribution.Repository +type pusher struct { + metadataService metadata.V2MetadataService + ref reference.Named + endpoint registry.APIEndpoint + repoInfo *registry.RepositoryInfo + config *ImagePushConfig + repo distribution.Repository // pushState is state built by the Upload functions. pushState pushState @@ -56,7 +56,8 @@ type pushState struct { hasAuthInfo bool } -func (p *v2Pusher) Push(ctx context.Context) (err error) { +// TODO(tiborvass): have push() take a reference to repository + tag, so that the pusher itself is repository-agnostic. +func (p *pusher) push(ctx context.Context) (err error) { p.pushState.remoteLayers = make(map[layer.DiffID]distribution.Descriptor) p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "push", "pull") @@ -66,7 +67,7 @@ func (p *v2Pusher) Push(ctx context.Context) (err error) { return err } - if err = p.pushV2Repository(ctx); err != nil { + if err = p.pushRepository(ctx); err != nil { if continueOnError(err, p.endpoint.Mirror) { return fallbackError{ err: err, @@ -77,14 +78,14 @@ func (p *v2Pusher) Push(ctx context.Context) (err error) { return err } -func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) { +func (p *pusher) pushRepository(ctx context.Context) (err error) { if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged { imageID, err := p.config.ReferenceStore.Get(p.ref) if err != nil { return fmt.Errorf("tag does not exist: %s", reference.FamiliarString(p.ref)) } - return p.pushV2Tag(ctx, namedTagged, imageID) + return p.pushTag(ctx, namedTagged, imageID) } if !reference.IsNameOnly(p.ref) { @@ -96,7 +97,7 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) { for _, association := range p.config.ReferenceStore.ReferencesByName(p.ref) { if namedTagged, isNamedTagged := association.Ref.(reference.NamedTagged); isNamedTagged { pushed++ - if err := p.pushV2Tag(ctx, namedTagged, association.ID); err != nil { + if err := p.pushTag(ctx, namedTagged, association.ID); err != nil { return err } } @@ -109,7 +110,7 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) { return nil } -func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error { +func (p *pusher) pushTag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error { logrus.Debugf("Pushing repository: %s", reference.FamiliarString(ref)) imgConfig, err := p.config.ImageStore.Get(ctx, id) @@ -135,14 +136,14 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id var descriptors []xfer.UploadDescriptor - descriptorTemplate := v2PushDescriptor{ - v2MetadataService: p.v2MetadataService, - hmacKey: hmacKey, - repoInfo: p.repoInfo.Name, - ref: p.ref, - endpoint: p.endpoint, - repo: p.repo, - pushState: &p.pushState, + descriptorTemplate := pushDescriptor{ + metadataService: p.metadataService, + hmacKey: hmacKey, + repoInfo: p.repoInfo.Name, + ref: p.ref, + endpoint: p.endpoint, + repo: p.repo, + pushState: &p.pushState, } // Loop bounds condition is to avoid pushing the base layer on Windows. @@ -243,7 +244,7 @@ func manifestFromBuilder(ctx context.Context, builder distribution.ManifestBuild // descriptors is in reverse order; iterate backwards to get references // appended in the right order. for i := len(descriptors) - 1; i >= 0; i-- { - if err := builder.AppendReference(descriptors[i].(*v2PushDescriptor)); err != nil { + if err := builder.AppendReference(descriptors[i].(*pushDescriptor)); err != nil { return nil, err } } @@ -251,33 +252,33 @@ func manifestFromBuilder(ctx context.Context, builder distribution.ManifestBuild return builder.Build(ctx) } -type v2PushDescriptor struct { - layer PushLayer - v2MetadataService metadata.V2MetadataService - hmacKey []byte - repoInfo reference.Named - ref reference.Named - endpoint registry.APIEndpoint - repo distribution.Repository - pushState *pushState - remoteDescriptor distribution.Descriptor +type pushDescriptor struct { + layer PushLayer + metadataService metadata.V2MetadataService + hmacKey []byte + repoInfo reference.Named + ref reference.Named + endpoint registry.APIEndpoint + repo distribution.Repository + pushState *pushState + remoteDescriptor distribution.Descriptor // a set of digests whose presence has been checked in a target repository checkedDigests map[digest.Digest]struct{} } -func (pd *v2PushDescriptor) Key() string { +func (pd *pushDescriptor) Key() string { return "v2push:" + pd.ref.Name() + " " + pd.layer.DiffID().String() } -func (pd *v2PushDescriptor) ID() string { +func (pd *pushDescriptor) ID() string { return stringid.TruncateID(pd.layer.DiffID().String()) } -func (pd *v2PushDescriptor) DiffID() layer.DiffID { +func (pd *pushDescriptor) DiffID() layer.DiffID { return pd.layer.DiffID() } -func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.Output) (distribution.Descriptor, error) { +func (pd *pushDescriptor) Upload(ctx context.Context, progressOutput progress.Output) (distribution.Descriptor, error) { // Skip foreign layers unless this registry allows nondistributable artifacts. if !pd.endpoint.AllowNondistributableArtifacts { if fs, ok := pd.layer.(distribution.Describable); ok { @@ -303,10 +304,10 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. maxMountAttempts, maxExistenceChecks, checkOtherRepositories := getMaxMountAndExistenceCheckAttempts(pd.layer) // Do we have any metadata associated with this layer's DiffID? - v2Metadata, err := pd.v2MetadataService.GetMetadata(diffID) + metaData, err := pd.metadataService.GetMetadata(diffID) if err == nil { // check for blob existence in the target repository - descriptor, exists, err := pd.layerAlreadyExists(ctx, progressOutput, diffID, true, 1, v2Metadata) + descriptor, exists, err := pd.layerAlreadyExists(ctx, progressOutput, diffID, true, 1, metaData) if exists || err != nil { return descriptor, err } @@ -319,7 +320,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. var layerUpload distribution.BlobWriter // Attempt to find another repository in the same registry to mount the layer from to avoid an unnecessary upload - candidates := getRepositoryMountCandidates(pd.repoInfo, pd.hmacKey, maxMountAttempts, v2Metadata) + candidates := getRepositoryMountCandidates(pd.repoInfo, pd.hmacKey, maxMountAttempts, metaData) isUnauthorizedError := false for _, mc := range candidates { mountCandidate := mc @@ -329,8 +330,8 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. if len(mountCandidate.SourceRepository) > 0 { namedRef, err := reference.ParseNormalizedNamed(mountCandidate.SourceRepository) if err != nil { - logrus.Errorf("failed to parse source repository reference %v: %v", reference.FamiliarString(namedRef), err) - pd.v2MetadataService.Remove(mountCandidate) + logrus.WithError(err).Errorf("failed to parse source repository reference %v", reference.FamiliarString(namedRef)) + _ = pd.metadataService.Remove(mountCandidate) continue } @@ -338,13 +339,13 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. // with only path to set mount from with remoteRef, err := reference.WithName(reference.Path(namedRef)) if err != nil { - logrus.Errorf("failed to make remote reference out of %q: %v", reference.Path(namedRef), err) + logrus.WithError(err).Errorf("failed to make remote reference out of %q", reference.Path(namedRef)) continue } canonicalRef, err := reference.WithDigest(reference.TrimNamed(remoteRef), mountCandidate.Digest) if err != nil { - logrus.Errorf("failed to make canonical reference: %v", err) + logrus.WithError(err).Error("failed to make canonical reference") continue } @@ -366,7 +367,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. pd.pushState.Unlock() // Cache mapping from this layer's DiffID to the blobsum - if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{ + if err := pd.metadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{ Digest: err.Descriptor.Digest, SourceRepository: pd.repoInfo.Name(), }); err != nil { @@ -400,7 +401,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. cause = fmt.Sprintf("an error: %v", err.Error()) } logrus.Debugf("removing association between layer %s and %s due to %s", mountCandidate.Digest, mountCandidate.SourceRepository, cause) - pd.v2MetadataService.Remove(mountCandidate) + _ = pd.metadataService.Remove(mountCandidate) } if lu != nil { @@ -412,7 +413,7 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. if maxExistenceChecks-len(pd.checkedDigests) > 0 { // do additional layer existence checks with other known digests if any - descriptor, exists, err := pd.layerAlreadyExists(ctx, progressOutput, diffID, checkOtherRepositories, maxExistenceChecks-len(pd.checkedDigests), v2Metadata) + descriptor, exists, err := pd.layerAlreadyExists(ctx, progressOutput, diffID, checkOtherRepositories, maxExistenceChecks-len(pd.checkedDigests), metaData) if exists || err != nil { return descriptor, err } @@ -430,15 +431,15 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. return pd.uploadUsingSession(ctx, progressOutput, diffID, layerUpload) } -func (pd *v2PushDescriptor) SetRemoteDescriptor(descriptor distribution.Descriptor) { +func (pd *pushDescriptor) SetRemoteDescriptor(descriptor distribution.Descriptor) { pd.remoteDescriptor = descriptor } -func (pd *v2PushDescriptor) Descriptor() distribution.Descriptor { +func (pd *pushDescriptor) Descriptor() distribution.Descriptor { return pd.remoteDescriptor } -func (pd *v2PushDescriptor) uploadUsingSession( +func (pd *pushDescriptor) uploadUsingSession( ctx context.Context, progressOutput progress.Output, diffID layer.DiffID, @@ -485,7 +486,7 @@ func (pd *v2PushDescriptor) uploadUsingSession( progress.Update(progressOutput, pd.ID(), "Pushed") // Cache mapping from this layer's DiffID to the blobsum - if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{ + if err := pd.metadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{ Digest: pushDigest, SourceRepository: pd.repoInfo.Name(), }); err != nil { @@ -509,7 +510,7 @@ func (pd *v2PushDescriptor) uploadUsingSession( // slice. If it finds one that the registry knows about, it returns the known digest and "true". If // "checkOtherRepositories" is true, stat will be performed also with digests mapped to any other repository // (not just the target one). -func (pd *v2PushDescriptor) layerAlreadyExists( +func (pd *pushDescriptor) layerAlreadyExists( ctx context.Context, progressOutput progress.Output, diffID layer.DiffID, @@ -558,7 +559,7 @@ attempts: case nil: if m, ok := digestToMetadata[desc.Digest]; !ok || m.SourceRepository != pd.repoInfo.Name() || !metadata.CheckV2MetadataHMAC(m, pd.hmacKey) { // cache mapping from this layer's DiffID to the blobsum - if err := pd.v2MetadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{ + if err := pd.metadataService.TagAndAdd(diffID, pd.hmacKey, metadata.V2Metadata{ Digest: desc.Digest, SourceRepository: pd.repoInfo.Name(), }); err != nil { @@ -571,7 +572,7 @@ attempts: case distribution.ErrBlobUnknown: if meta.SourceRepository == pd.repoInfo.Name() { // remove the mapping to the target repository - pd.v2MetadataService.Remove(*meta) + pd.metadataService.Remove(*meta) } default: logrus.WithError(err).Debugf("Failed to check for presence of layer %s (%s) in %s", diffID, dgst, pd.repoInfo.Name()) diff --git a/distribution/push_v2_test.go b/distribution/push_v2_test.go index ea7ce00ec8b96..def4233786f91 100644 --- a/distribution/push_v2_test.go +++ b/distribution/push_v2_test.go @@ -395,16 +395,16 @@ func TestLayerAlreadyExists(t *testing.T) { } ctx := context.Background() ms := &mockV2MetadataService{} - pd := &v2PushDescriptor{ + pd := &pushDescriptor{ hmacKey: []byte(tc.hmacKey), repoInfo: repoInfo, layer: &storeLayer{ Layer: layer.EmptyLayer, }, - repo: repo, - v2MetadataService: ms, - pushState: &pushState{remoteLayers: make(map[layer.DiffID]distribution.Descriptor)}, - checkedDigests: make(map[digest.Digest]struct{}), + repo: repo, + metadataService: ms, + pushState: &pushState{remoteLayers: make(map[layer.DiffID]distribution.Descriptor)}, + checkedDigests: make(map[digest.Digest]struct{}), } desc, exists, err := pd.layerAlreadyExists(ctx, &progressSink{t}, layer.EmptyLayer.DiffID(), tc.checkOtherRepositories, tc.maxExistenceChecks, tc.metadata) @@ -522,7 +522,7 @@ func TestWhenEmptyAuthConfig(t *testing.T) { } imagePushConfig.ReferenceStore = &mockReferenceStore{} repoInfo, _ := reference.ParseNormalizedNamed("xujihui1985/test.img") - pusher := &v2Pusher{ + pusher := &pusher{ config: imagePushConfig, repoInfo: ®istry.RepositoryInfo{ Name: repoInfo, @@ -536,7 +536,7 @@ func TestWhenEmptyAuthConfig(t *testing.T) { TrimHostname: true, }, } - pusher.Push(context.Background()) + pusher.push(context.Background()) if pusher.pushState.hasAuthInfo != authInfo.expected { t.Errorf("hasAuthInfo does not match expected: %t != %t", authInfo.expected, pusher.pushState.hasAuthInfo) } @@ -598,14 +598,14 @@ func TestPushRegistryWhenAuthInfoEmpty(t *testing.T) { requests: []string{}, }, } - pd := &v2PushDescriptor{ + pd := &pushDescriptor{ hmacKey: []byte("abcd"), repoInfo: repoInfo, layer: &storeLayer{ Layer: layer.EmptyLayer, }, - repo: repo, - v2MetadataService: ms, + repo: repo, + metadataService: ms, pushState: &pushState{ remoteLayers: make(map[layer.DiffID]distribution.Descriptor), hasAuthInfo: false, From 572c7e01847b3d1822c5c19ec15a72994ed860f3 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 27 Feb 2022 22:01:24 +0100 Subject: [PATCH 5/7] distribution: remove RootFSFromConfig(), PlatformFromConfig() from ImageConfigStore These functions did not require the ImageConfigStore, so could just be local utilities. Signed-off-by: Sebastiaan van Stijn --- distribution/config.go | 13 ++++++++----- distribution/pull_v2.go | 10 +++++----- distribution/push_v2.go | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/distribution/config.go b/distribution/config.go index 52ed9ddbfcf6f..fa06e3c9c08ac 100644 --- a/distribution/config.go +++ b/distribution/config.go @@ -87,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. @@ -133,7 +131,7 @@ 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 @@ -141,7 +139,7 @@ func (s *imageConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) { 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 @@ -154,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 { diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 090ba10f25970..0495ab414610e 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -647,7 +647,7 @@ func (p *puller) pullSchema2Layers(ctx context.Context, target distribution.Desc // check to block Windows images being pulled on Linux is implemented, it // may be necessary to perform the same type of serialisation. if runtime.GOOS == "windows" { - configJSON, configRootFS, configPlatform, err = receiveConfig(p.config.ImageStore, configChan, configErrChan) + configJSON, configRootFS, configPlatform, err = receiveConfig(configChan, configErrChan) if err != nil { return "", err } @@ -708,7 +708,7 @@ func (p *puller) pullSchema2Layers(ctx context.Context, target distribution.Desc } if configJSON == nil { - configJSON, configRootFS, _, err = receiveConfig(p.config.ImageStore, configChan, configErrChan) + configJSON, configRootFS, _, err = receiveConfig(configChan, configErrChan) if err == nil && configRootFS == nil { err = errRootFSInvalid } @@ -773,14 +773,14 @@ func (p *puller) pullOCI(ctx context.Context, ref reference.Named, mfst *ocische return id, manifestDigest, err } -func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, *specs.Platform, error) { +func receiveConfig(configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, *specs.Platform, error) { select { case configJSON := <-configChan: - rootfs, err := s.RootFSFromConfig(configJSON) + rootfs, err := rootFSFromConfig(configJSON) if err != nil { return nil, nil, nil, err } - platform, err := s.PlatformFromConfig(configJSON) + platform, err := platformFromConfig(configJSON) if err != nil { return nil, nil, nil, err } diff --git a/distribution/push_v2.go b/distribution/push_v2.go index 372705dc9ce33..24824709eeec9 100644 --- a/distribution/push_v2.go +++ b/distribution/push_v2.go @@ -118,7 +118,7 @@ func (p *pusher) pushTag(ctx context.Context, ref reference.NamedTagged, id dige return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err) } - rootfs, err := p.config.ImageStore.RootFSFromConfig(imgConfig) + rootfs, err := rootFSFromConfig(imgConfig) if err != nil { return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err) } From 566c8db66dd01f1848c8e38b9cdff75ae57d9028 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 27 Feb 2022 22:11:13 +0100 Subject: [PATCH 6/7] distribution: add GetRepository(), un-export NewV2Repository, ValidateRepoName These were only exported to facilitate ImageService.GetRepository() (used for the `GET /distribution/{name:.*}/json` endpoint. Moving the core functionality of that to the distribution package makes it more consistent with (e.g.) "pull" operations, and allows us to keep more things internal. Signed-off-by: Sebastiaan van Stijn --- daemon/images/image_pull.go | 35 +++++------------------------- distribution/pull.go | 6 ++--- distribution/pull_v2.go | 2 +- distribution/pull_v2_test.go | 2 +- distribution/push_v2.go | 2 +- distribution/registry.go | 4 ++-- distribution/registry_unit_test.go | 2 +- distribution/repository.go | 34 +++++++++++++++++++++++++++++ 8 files changed, 49 insertions(+), 38 deletions(-) create mode 100644 distribution/repository.go diff --git a/daemon/images/image_pull.go b/daemon/images/image_pull.go index bc8dba42f5e31..596a43b3a9784 100644 --- a/daemon/images/image_pull.go +++ b/daemon/images/image_pull.go @@ -133,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) { diff --git a/distribution/pull.go b/distribution/pull.go index baba8452649c5..cb60f62b6f8b5 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -38,7 +38,7 @@ func Pull(ctx context.Context, ref reference.Named, config *ImagePullConfig, loc } // makes sure name is not `scratch` - if err := ValidateRepoName(repoInfo.Name); err != nil { + if err := validateRepoName(repoInfo.Name); err != nil { return err } @@ -112,8 +112,8 @@ func writeStatus(requestedTag string, out progress.Output, layersDownloaded bool } } -// 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)) } diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 0495ab414610e..37ced60d433bb 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -63,7 +63,7 @@ type puller struct { func (p *puller) pull(ctx context.Context, ref reference.Named) (err error) { // TODO(tiborvass): was ReceiveTimeout - p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") + p.repo, err = newRepository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { logrus.Warnf("Error getting v2 registry: %v", err) return err diff --git a/distribution/pull_v2_test.go b/distribution/pull_v2_test.go index a080c9eadf139..a4824dff082dc 100644 --- a/distribution/pull_v2_test.go +++ b/distribution/pull_v2_test.go @@ -359,7 +359,7 @@ func testNewPuller(t *testing.T, rawurl string) *puller { } p := newPuller(endpoint, repoInfo, imagePullConfig, nil) - p.repo, err = NewV2Repository(context.Background(), p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") + p.repo, err = newRepository(context.Background(), p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { t.Fatal(err) } diff --git a/distribution/push_v2.go b/distribution/push_v2.go index 24824709eeec9..f5168f836b801 100644 --- a/distribution/push_v2.go +++ b/distribution/push_v2.go @@ -60,7 +60,7 @@ type pushState struct { func (p *pusher) push(ctx context.Context) (err error) { p.pushState.remoteLayers = make(map[layer.DiffID]distribution.Descriptor) - p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "push", "pull") + p.repo, err = newRepository(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 != "") if err != nil { logrus.Debugf("Error getting v2 registry: %v", err) diff --git a/distribution/registry.go b/distribution/registry.go index 904ffff2becad..7ff01b5d92f93 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -53,10 +53,10 @@ func init() { } } -// NewV2Repository returns a repository (v2 only). It creates an HTTP transport +// newRepository returns a repository (v2 only). It creates an HTTP transport // providing timeout settings and authentication support, and also verifies the // remote API version. -func NewV2Repository( +func newRepository( ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string, ) (repo distribution.Repository, err error) { diff --git a/distribution/registry_unit_test.go b/distribution/registry_unit_test.go index df9e12a99a8ba..c80357717337d 100644 --- a/distribution/registry_unit_test.go +++ b/distribution/registry_unit_test.go @@ -70,7 +70,7 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) { } p := newPuller(endpoint, repoInfo, imagePullConfig, nil) ctx := context.Background() - p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") + p.repo, err = newRepository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { t.Fatal(err) } diff --git a/distribution/repository.go b/distribution/repository.go new file mode 100644 index 0000000000000..f424c24b658f8 --- /dev/null +++ b/distribution/repository.go @@ -0,0 +1,34 @@ +package distribution + +import ( + "context" + + "github.com/docker/distribution" + "github.com/docker/distribution/reference" + "github.com/docker/docker/errdefs" +) + +// GetRepository returns a repository from the registry. +func GetRepository(ctx context.Context, ref reference.Named, config *ImagePullConfig) (repository distribution.Repository, lastError error) { + repoInfo, err := config.RegistryService.ResolveRepository(ref) + if err != nil { + return nil, errdefs.InvalidParameter(err) + } + // makes sure name is not empty or `scratch` + if err := validateRepoName(repoInfo.Name); err != nil { + return nil, errdefs.InvalidParameter(err) + } + + endpoints, err := config.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name)) + if err != nil { + return nil, err + } + + for _, endpoint := range endpoints { + repository, lastError = newRepository(ctx, repoInfo, endpoint, nil, config.AuthConfig, "pull") + if lastError == nil { + break + } + } + return repository, lastError +} From 2b0da893660089b72d9a99c8f72f6f80cc7d6e09 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sun, 27 Feb 2022 23:45:22 +0100 Subject: [PATCH 7/7] distribution: move newPusher() and newPuller() together with definition Also moving writeStatus() to the puller, which is where it's used, and makes it slightly easier to consume. Signed-off-by: Sebastiaan van Stijn --- distribution/pull.go | 28 ---------------------------- distribution/pull_v2.go | 27 ++++++++++++++++++++++++++- distribution/push.go | 15 --------------- distribution/push_v2.go | 13 +++++++++++++ 4 files changed, 39 insertions(+), 44 deletions(-) diff --git a/distribution/pull.go b/distribution/pull.go index cb60f62b6f8b5..7780ea4c4393e 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -6,28 +6,12 @@ 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" ) -// newPuller returns a puller to pull from a v2 registry. -func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, config *ImagePullConfig, local ContentStore) *puller { - return &puller{ - metadataService: metadata.NewV2MetadataService(config.MetadataStore), - endpoint: endpoint, - config: config, - repoInfo: repoInfo, - manifestStore: &manifestStore{ - local: local, - }, - } -} - // 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, config *ImagePullConfig, local ContentStore) error { @@ -100,18 +84,6 @@ func Pull(ctx context.Context, ref reference.Named, config *ImagePullConfig, loc 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) - } -} - // validateRepoName validates the name of a repository. func validateRepoName(name reference.Named) error { if reference.FamiliarName(name) == api.NoBaseImageSpecifier { diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 37ced60d433bb..f4a55f83e76d2 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -52,6 +52,19 @@ func (e imageConfigPullError) Error() string { return "error pulling image configuration: " + e.Err.Error() } +// newPuller returns a puller to pull from a v2 registry. +func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, config *ImagePullConfig, local ContentStore) *puller { + return &puller{ + metadataService: metadata.NewV2MetadataService(config.MetadataStore), + endpoint: endpoint, + config: config, + repoInfo: repoInfo, + manifestStore: &manifestStore{ + local: local, + }, + } +} + type puller struct { metadataService metadata.V2MetadataService endpoint registry.APIEndpoint @@ -122,11 +135,23 @@ func (p *puller) pullRepository(ctx context.Context, ref reference.Named) (err e } } - writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded) + p.writeStatus(reference.FamiliarString(ref), layersDownloaded) return nil } +// 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 (p *puller) writeStatus(requestedTag string, layersDownloaded bool) { + if layersDownloaded { + progress.Message(p.config.ProgressOutput, "", "Status: Downloaded newer image for "+requestedTag) + } else { + progress.Message(p.config.ProgressOutput, "", "Status: Image is up to date for "+requestedTag) + } +} + type layerDescriptor struct { digest digest.Digest diffID layer.DiffID diff --git a/distribution/push.go b/distribution/push.go index 026c29f72610d..808c5ec31681c 100644 --- a/distribution/push.go +++ b/distribution/push.go @@ -8,27 +8,12 @@ import ( "io" "github.com/docker/distribution/reference" - "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/pkg/progress" - "github.com/docker/docker/registry" "github.com/sirupsen/logrus" ) const compressionBufSize = 32768 -// newPusher creates a new pusher for pushing to a v2 registry. -// The parameters are passed through to the underlying pusher implementation for -// use during the actual push operation. -func newPusher(ref reference.Named, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, config *ImagePushConfig) *pusher { - return &pusher{ - metadataService: metadata.NewV2MetadataService(config.MetadataStore), - ref: ref, - endpoint: endpoint, - repoInfo: repoInfo, - config: config, - } -} - // Push initiates a push operation on ref. ref is the specific variant of the // image to push. If no tag is provided, all tags are pushed. func Push(ctx context.Context, ref reference.Named, config *ImagePushConfig) error { diff --git a/distribution/push_v2.go b/distribution/push_v2.go index f5168f836b801..910123250c7f0 100644 --- a/distribution/push_v2.go +++ b/distribution/push_v2.go @@ -34,6 +34,19 @@ const ( middleLayerMaximumSize = 10 * (1 << 20) // 10MB ) +// newPusher creates a new pusher for pushing to a v2 registry. +// The parameters are passed through to the underlying pusher implementation for +// use during the actual push operation. +func newPusher(ref reference.Named, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, config *ImagePushConfig) *pusher { + return &pusher{ + metadataService: metadata.NewV2MetadataService(config.MetadataStore), + ref: ref, + endpoint: endpoint, + repoInfo: repoInfo, + config: config, + } +} + type pusher struct { metadataService metadata.V2MetadataService ref reference.Named