diff --git a/pkg/cli/admin/release/helpers.go b/pkg/cli/admin/release/helpers.go new file mode 100644 index 0000000000..a6d7068e70 --- /dev/null +++ b/pkg/cli/admin/release/helpers.go @@ -0,0 +1,37 @@ +package release + +import ( + "context" + + "k8s.io/klog" + + "github.com/openshift/library-go/pkg/image/reference" + "github.com/openshift/library-go/pkg/image/registryclient" + "github.com/openshift/oc/pkg/cli/image/imagesource" + imagemanifest "github.com/openshift/oc/pkg/cli/image/manifest" +) + +func verifyImageExists(fromContext *registryclient.Context, fileDir string, insecure bool, include imagemanifest.FilterFunc, ref reference.DockerImageReference) bool { + from := imagesource.TypedImageReference{Type: imagesource.DestinationRegistry, Ref: ref} + ctx := context.Background() + fromOptions := &imagesource.Options{ + FileDir: fileDir, + Insecure: insecure, + RegistryContext: fromContext, + } + + repo, err := fromOptions.Repository(ctx, from) + if err != nil { + klog.V(2).Infof("unable to connect to image repository %s: %v", from.String(), err) + return false + } + _, _, err = imagemanifest.FirstManifest(ctx, from.Ref, repo, include) + if err != nil { + if imagemanifest.IsImageNotFound(err) { + return false + } + klog.V(2).Infof("unable to read image %s: %v", from.String(), err) + return false + } + return true +} diff --git a/pkg/cli/admin/release/info.go b/pkg/cli/admin/release/info.go index fc0d5b2f0a..501322a061 100644 --- a/pkg/cli/admin/release/info.go +++ b/pkg/cli/admin/release/info.go @@ -775,6 +775,32 @@ func (o *InfoOptions) LoadReleaseInfo(image string, retrieveImages bool) (*Relea errs = append(errs, err) return true, nil } + // try to verify user-passed image first, in case of mirrored images this will + // exist, and if so, use this instead of the release image-reference. If this doesn't + // exist, proceed with release image-reference from the readReleaseImageReference above + newRef := ref.Ref.AsRepository() + // Try the registry/repo passed from the user first. If image does not exist, + // proceed with the release info from the release image-references, from readReleaseImageReference above + for _, tag := range is.Spec.Tags { + // tagRef is the sha of each component in the release image-reference. + // If can't get digest ID, skip this tag, this happens when user has built a payload by + // replacing component images in the release with a new image + tagRef, err := imagereference.Parse(tag.From.Name) + if err != nil { + continue + } + newRef.ID = tagRef.ID + // If the user-given registry/repo/name:digest exists, replace with that, if not keep the + // is.Spec.Tag from release image-reference + fromContext, err := opts.SecurityOptions.Context() + if err != nil { + return true, nil + } + if verifyImageExists(fromContext, opts.FileDir, o.SecurityOptions.Insecure, opts.FilterOptions.Include, newRef) { + tag.From.Name = newRef.String() + } + } + release.References = is case "release-metadata": data, err := ioutil.ReadAll(r) diff --git a/pkg/cli/admin/release/mirror.go b/pkg/cli/admin/release/mirror.go index dfb1c0b2a8..a22ceac3a2 100644 --- a/pkg/cli/admin/release/mirror.go +++ b/pkg/cli/admin/release/mirror.go @@ -517,10 +517,12 @@ func (o *MirrorOptions) Run() error { if err := imageVerifier.Verify(ctx, releaseDigest); err != nil { fmt.Fprintf(o.ErrOut, "warning: An image was retrieved that failed verification: %v\n", err) } + var srcRef imagesource.TypedImageReference var mappings []mirror.Mapping if len(o.From) > 0 { + var err error src := o.From - srcRef, err := imagesource.ParseReference(src) + srcRef, err = imagesource.ParseReference(src) if err != nil { return fmt.Errorf("invalid --from: %v", err) } @@ -579,6 +581,7 @@ func (o *MirrorOptions) Run() error { repositories := make(map[string]struct{}) + newRef := srcRef.Ref.AsRepository() // build the mapping list for mirroring and rewrite if necessary for i := range is.Spec.Tags { tag := &is.Spec.Tags[i] @@ -593,9 +596,27 @@ func (o *MirrorOptions) Run() error { return fmt.Errorf("image-references should only contain pointers to images by digest: %s", tag.From.Name) } + opts := mirror.NewMirrorImageOptions(genericclioptions.IOStreams{Out: o.Out, ErrOut: o.ErrOut}) + opts.SecurityOptions = o.SecurityOptions + opts.ParallelOptions = o.ParallelOptions + opts.FileDir = o.ToDir // Allow mirror refs to be sourced locally srcMirrorRef := imagesource.TypedImageReference{Ref: from, Type: imagesource.DestinationRegistry} srcMirrorRef = sourceFn(srcMirrorRef) + // Try the registry/repo passed from the user first. If image does not exist, + // proceed with the release info from the release image-references + // ID is the sha of each component in the release image-reference. + newRef.ID = from.ID + // If the user-given registry/repo/name:digest exists, replace with that, if not keep the + // is.Spec.Tag from release image-reference + fromContext, err := opts.SecurityOptions.Context() + if err != nil { + return err + } + if verifyImageExists(fromContext, opts.FileDir, opts.SecurityOptions.Insecure, opts.FilterOptions.Include, newRef) { + srcMirrorRef = imagesource.TypedImageReference{Ref: newRef, Type: imagesource.DestinationRegistry} + srcMirrorRef = sourceFn(srcMirrorRef) + } // Create a unique map of repos as keys currentRepo := from.AsRepository().String()