func pinImages(ctx context.Context, dc *declcfg.DeclarativeConfig, resolverConfigPath string, insecure bool) error {
	resolver, err := containerdregistry.NewResolver(resolverConfigPath, insecure, nil)
	if err != nil {
		return fmt.Errorf("error creating image resolver: %v", err)
	}
	var errs []error
	for i, b := range dc.Bundles {
		if !image.IsImagePinned(b.Image) {
			if !image.IsImageTagged(b.Image) {
				logrus.Warnf("bundle %s: bundle image tag not set", b.Name)
				continue
			}
			if dc.Bundles[i].Image, err = image.ResolveToPin(ctx, resolver, b.Image); err != nil {
				errs = append(errs, err)
				continue
			}
		}
		for j, ri := range b.RelatedImages {
			if !image.IsImagePinned(ri.Image) {
				if !image.IsImageTagged(ri.Image) {
					logrus.Warnf("bundle %s: related image tag not set", b.Name)
					continue
				}
				if b.RelatedImages[j].Image, err = image.ResolveToPin(ctx, resolver, ri.Image); err != nil {
					errs = append(errs, err)
					continue
				}
			}
		}
	}
	return utilerrors.NewAggregate(errs)
}
pinImages resolves every image in dc to it's canonical name (includes digest).
package operator
import (
	"context"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
	"github.com/joelanford/ignore"
	imgreference "github.com/openshift/library-go/pkg/image/reference"
	"github.com/openshift/oc/pkg/cli/admin/catalog"
	"github.com/openshift/oc/pkg/cli/image/imagesource"
	imagemanifest "github.com/openshift/oc/pkg/cli/image/manifest"
	imgmirror "github.com/openshift/oc/pkg/cli/image/mirror"
	"github.com/operator-framework/operator-registry/alpha/action"
	"github.com/operator-framework/operator-registry/alpha/declcfg"
	"github.com/operator-framework/operator-registry/pkg/image/containerdregistry"
	"github.com/sirupsen/logrus"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/yaml"
	"github.com/RedHatGov/bundle/pkg/bundle"
	"github.com/RedHatGov/bundle/pkg/cli"
	"github.com/RedHatGov/bundle/pkg/config"
	"github.com/RedHatGov/bundle/pkg/config/v1alpha1"
	"github.com/RedHatGov/bundle/pkg/image"
)
var (
	OPMImage = "quay.io/operator-framework/opm@sha256:038007c1c5d5f0efa50961cbcc097c6e63655a2ab4126547e3c4eb620ad0346e"
)
type MirrorOptions struct {
	cli.RootOptions
	SkipImagePin bool
	Logger       *logrus.Entry
	tmp string
}
func NewMirrorOptions(ro cli.RootOptions) *MirrorOptions {
	return &MirrorOptions{RootOptions: ro}
}
func (o *MirrorOptions) complete() {
	if o.Dir == "" {
		o.Dir = "create"
	}
	if o.Logger == nil {
		o.Logger = logrus.NewEntry(logrus.New())
	}
}
func (o *MirrorOptions) mktempDir() (func(), error) {
	o.tmp = filepath.Join(o.Dir, fmt.Sprintf("operators.%d", time.Now().Unix()))
	return func() {
		if err := os.RemoveAll(o.tmp); err != nil {
			o.Logger.Error(err)
		}
	}, os.MkdirAll(o.tmp, os.ModePerm)
}
func (o *MirrorOptions) createRegistry() (*containerdregistry.Registry, error) {
	cacheDir, err := os.MkdirTemp("", "imageset-catalog-registry-")
	if err != nil {
		return nil, err
	}
	logger := logrus.New()
	logger.SetOutput(ioutil.Discard)
	nullLogger := logrus.NewEntry(logger)
	return containerdregistry.NewRegistry(
		containerdregistry.WithCacheDir(cacheDir),
		containerdregistry.SkipTLS(o.SkipTLS),
		containerdregistry.WithLog(nullLogger),
	)
}
func (o *MirrorOptions) Full(ctx context.Context, cfg v1alpha1.ImageSetConfiguration) (image.AssociationSet, error) {
	o.complete()
	cleanup, err := o.mktempDir()
	if err != nil {
		return nil, err
	}
	if !o.SkipCleanup {
		defer cleanup()
	}
	reg, err := o.createRegistry()
	if err != nil {
		return nil, fmt.Errorf("error creating container registry: %v", err)
	}
	defer reg.Destroy()
	allAssocs := image.AssociationSet{}
	for _, ctlg := range cfg.Mirror.Operators {
		ctlgRef, err := imagesource.ParseReference(ctlg.Catalog)
		if err != nil {
			return nil, fmt.Errorf("error parsing catalog: %v", err)
		}
		ctlgRef.Ref = ctlgRef.Ref.DockerClientDefaults()
		catLogger := o.Logger.WithField("catalog", ctlg.Catalog)
		var dc *declcfg.DeclarativeConfig
		if ctlg.HeadsOnly {
			dc, err = action.Diff{
				Registry:      reg,
				NewRefs:       []string{ctlg.Catalog},
				Logger:        catLogger,
				IncludeConfig: ctlg.DiffIncludeConfig,
			}.Run(ctx)
		} else {
			dc, err = action.Render{
				Registry: reg,
				Refs:     []string{ctlg.Catalog},
			}.Run(ctx)
		}
		if err != nil {
			return nil, err
		}
		isBlocked := func(ref imgreference.DockerImageReference) bool {
			return bundle.IsBlocked(cfg, ref)
		}
		mappings, err := o.mirror(ctx, dc, ctlgRef, ctlg, isBlocked)
		if err != nil {
			return nil, err
		}
		assocs, err := o.associateDeclarativeConfigImageLayers(ctlgRef, dc, mappings)
		if err != nil {
			return nil, err
		}
		allAssocs.Merge(assocs)
	}
	return allAssocs, nil
}
func (o *MirrorOptions) Diff(ctx context.Context, cfg v1alpha1.ImageSetConfiguration, lastRun v1alpha1.PastMirror) (image.AssociationSet, error) {
	o.complete()
	cleanup, err := o.mktempDir()
	if err != nil {
		return nil, err
	}
	if !o.SkipCleanup {
		defer cleanup()
	}
	reg, err := o.createRegistry()
	if err != nil {
		return nil, fmt.Errorf("error creating container registry: %v", err)
	}
	defer reg.Destroy()
	allAssocs := image.AssociationSet{}
	for _, ctlg := range cfg.Mirror.Operators {
		catLogger := o.Logger.WithField("catalog", ctlg.Catalog)
		a := action.Diff{
			Registry:      reg,
			NewRefs:       []string{ctlg.Catalog},
			Logger:        catLogger,
			IncludeConfig: ctlg.DiffIncludeConfig,
		}
		for _, operator := range lastRun.Operators {
			if operator.Catalog != ctlg.Catalog {
				continue
			}
			switch {
			case operator.RelIndexPath != "":
			case len(operator.Index) != 0:
			case operator.ImagePin != "":
				a.OldRefs = []string{operator.ImagePin}
			default:
				return nil, fmt.Errorf("metadata sequence %d catalog %q: at least one of RelIndexPath, Index, or ImagePin must be set", lastRun.Sequence, ctlg.Catalog)
			}
			break
		}
		dc, err := a.Run(ctx)
		if err != nil {
			return nil, err
		}
		ctlgRef, err := imagesource.ParseReference(ctlg.Catalog)
		if err != nil {
			return nil, fmt.Errorf("error parsing catalog: %v", err)
		}
		ctlgRef.Ref = ctlgRef.Ref.DockerClientDefaults()
		isBlocked := func(ref imgreference.DockerImageReference) bool {
			return bundle.IsBlocked(cfg, ref)
		}
		mappings, err := o.mirror(ctx, dc, ctlgRef, ctlg, isBlocked)
		if err != nil {
			return nil, err
		}
		assocs, err := o.associateDeclarativeConfigImageLayers(ctlgRef, dc, mappings)
		if err != nil {
			return nil, err
		}
		allAssocs.Merge(assocs)
	}
	return allAssocs, nil
}
func (o *MirrorOptions) mirror(ctx context.Context, dc *declcfg.DeclarativeConfig, ctlgRef imagesource.TypedImageReference, ctlg v1alpha1.Operator, isBlocked ...blockedFunc) (map[string]string, error) {
	o.Logger.Debugf("Mirroring catalog %q bundle and related images", ctlgRef.Ref.Exact())
	opts, err := o.newMirrorCatalogOptions(ctlgRef.Ref, filepath.Join(o.Dir, config.SourceDir), []byte(ctlg.PullSecret))
	if err != nil {
		return nil, err
	}
	if !o.SkipImagePin {
		if err := pinImages(ctx, dc, "", o.SkipTLS); err != nil {
			return nil, fmt.Errorf("error pinning images in catalog %s: %v", ctlgRef, err)
		}
	}
	indexDir, err := o.writeDC(dc, ctlgRef.Ref)
	if err != nil {
		return nil, err
	}
	opts.ManifestOnly = true
	opts.ImageMirrorer = catalog.ImageMirrorerFunc(func(mapping map[imagesource.TypedImageReference]imagesource.TypedImageReference) error {
		return nil
	})
	opts.IndexExtractor = catalog.IndexExtractorFunc(func(imagesource.TypedImageReference) (string, error) {
		o.Logger.Debugf("returning index dir in extractor: %s", indexDir)
		return indexDir, nil
	})
	opts.IndexPath = indexDir
	opts.RelatedImagesParser = catalog.RelatedImagesParserFunc(parseRelatedImages)
	opts.SourceRef = ctlgRef
	opts.DestRef = imagesource.TypedImageReference{
		Type: imagesource.DestinationFile,
	}
	if err := opts.Validate(); err != nil {
		return nil, fmt.Errorf("invalid catalog mirror options: %v", err)
	}
	if err := opts.Run(); err != nil {
		return nil, fmt.Errorf("error running catalog mirror: %v", err)
	}
	mappings, err := image.ReadImageMapping(filepath.Join(opts.ManifestDir, mappingFile))
	if err != nil {
		return nil, err
	}
	delete(mappings, ctlgRef.Ref.Exact())
	for src, dst := range mappings {
		dstRef, err := imagesource.ParseReference(dst)
		if err != nil {
			return nil, err
		}
		newRepoName := strings.TrimPrefix(dstRef.Ref.RepositoryName(), ctlgRef.Ref.RepositoryName())
		newRepoName = strings.TrimPrefix(newRepoName, "/")
		tmpRef, err := imgreference.Parse(newRepoName)
		if err != nil {
			return nil, err
		}
		dstRef.Ref.Namespace = tmpRef.Namespace
		dstRef.Ref.Name = tmpRef.Name
		mappings[src] = dstRef.String()
	}
	return mappings, mirrorMappings(opts, mappings, isBlocked...)
}