diff --git a/pkg/cli/admin/catalog/mirror.go b/pkg/cli/admin/catalog/mirror.go index ec351ed325..fc5aa5277f 100644 --- a/pkg/cli/admin/catalog/mirror.go +++ b/pkg/cli/admin/catalog/mirror.go @@ -182,6 +182,7 @@ func (o *MirrorCatalogOptions) Complete(cmd *cobra.Command, args []string) error a.SecurityOptions = o.SecurityOptions a.FilterOptions = o.FilterOptions a.ParallelOptions = o.ParallelOptions + a.ForceManifestList = true a.Mappings = []imgmirror.Mapping{{ Source: fromRef[0], Destination: toRef, diff --git a/pkg/cli/image/manifest/manifest.go b/pkg/cli/image/manifest/manifest.go index 238b1d515b..7cea303b82 100644 --- a/pkg/cli/image/manifest/manifest.go +++ b/pkg/cli/image/manifest/manifest.go @@ -252,7 +252,7 @@ func FirstManifest(ctx context.Context, from imagereference.DockerImageReference } originalSrcDigest := srcDigest - srcManifests, srcManifest, srcDigest, err := ProcessManifestList(ctx, srcDigest, srcManifest, manifests, from, filterFn) + srcManifests, srcManifest, srcDigest, err := ProcessManifestList(ctx, srcDigest, srcManifest, manifests, from, filterFn, false) if err != nil { return nil, ManifestLocation{}, err } @@ -330,7 +330,7 @@ func ManifestToImageConfig(ctx context.Context, srcManifest distribution.Manifes } } -func ProcessManifestList(ctx context.Context, srcDigest digest.Digest, srcManifest distribution.Manifest, manifests distribution.ManifestService, ref imagereference.DockerImageReference, filterFn FilterFunc) ([]distribution.Manifest, distribution.Manifest, digest.Digest, error) { +func ProcessManifestList(ctx context.Context, srcDigest digest.Digest, srcManifest distribution.Manifest, manifests distribution.ManifestService, ref imagereference.DockerImageReference, filterFn FilterFunc, forceManifestList bool) ([]distribution.Manifest, distribution.Manifest, digest.Digest, error) { var srcManifests []distribution.Manifest switch t := srcManifest.(type) { case *manifestlist.DeserializedManifestList: @@ -379,7 +379,7 @@ func ProcessManifestList(ctx context.Context, srcDigest digest.Digest, srcManife } switch { - case len(srcManifests) == 1: + case len(srcManifests) == 1 && !forceManifestList: manifestDigest, err := registryclient.ContentDigestForManifest(srcManifests[0], srcDigest.Algorithm()) if err != nil { return nil, nil, "", err diff --git a/pkg/cli/image/mirror/mirror.go b/pkg/cli/image/mirror/mirror.go index f0e996318e..50e7a41685 100644 --- a/pkg/cli/image/mirror/mirror.go +++ b/pkg/cli/image/mirror/mirror.go @@ -100,6 +100,7 @@ type MirrorImageOptions struct { SkipMultipleScopes bool SkipMissing bool Force bool + ForceManifestList bool MaxRegistry int ParallelOptions imagemanifest.ParallelOptions @@ -144,6 +145,9 @@ func NewCmdMirrorImage(name string, streams genericclioptions.IOStreams) *cobra. o.FilterOptions.Bind(flag) o.ParallelOptions.Bind(flag) + // Always mirror manifestlist if available + o.ForceManifestList = true + flag.BoolVar(&o.DryRun, "dry-run", o.DryRun, "Print the actions that would be taken and exit without writing to the destinations.") flag.BoolVar(&o.SkipMissing, "skip-missing", o.SkipMissing, "If an input image is not found, skip them.") flag.BoolVar(&o.SkipMount, "skip-mount", o.SkipMount, "Always push layers instead of cross-mounting them") @@ -462,7 +466,7 @@ func (o *MirrorImageOptions) plan() (*plan, error) { // filter or load manifest list as appropriate originalSrcDigest := srcDigest - srcManifests, srcManifest, srcDigest, err := imagemanifest.ProcessManifestList(ctx, srcDigest, srcManifest, manifests, src.ref.Ref, o.FilterOptions.IncludeAll) + srcManifests, srcManifest, srcDigest, err := imagemanifest.ProcessManifestList(ctx, srcDigest, srcManifest, manifests, src.ref.Ref, o.FilterOptions.IncludeAll, o.ForceManifestList) if err != nil { plan.AddError(retrieverError{src: src.ref, err: err}) return