func CheckOverlappingEdges(
	edges map[PromotionEdge]interface{}) (map[PromotionEdge]interface{}, error) {
	promotionIntent := make(map[string]map[Digest][]PromotionEdge)
	checked := make(map[PromotionEdge]interface{})
	for edge := range edges {
		if edge.DstImageTag.Tag == "" {
			checked[edge] = nil
			continue
		}
		dstPQIN := ToPQIN(edge.DstRegistry.Name,
			edge.DstImageTag.ImageName,
			edge.DstImageTag.Tag)
		digestToEdges, ok := promotionIntent[dstPQIN]
		if ok {
			digestToEdges[edge.Digest] = append(digestToEdges[edge.Digest], edge)
			promotionIntent[dstPQIN] = digestToEdges
		} else {
			edgeList := make([]PromotionEdge, 0)
			edgeList = append(edgeList, edge)
			digestToEdges := make(map[Digest][]PromotionEdge)
			digestToEdges[edge.Digest] = edgeList
			promotionIntent[dstPQIN] = digestToEdges
		}
	}
	overlapError := false
	emptyEdgeListError := false
	for pqin, digestToEdges := range promotionIntent {
		if len(digestToEdges) < 2 {
			for _, edgeList := range digestToEdges {
				switch len(edgeList) {
				case 0:
					logrus.Errorf("no edges for %v", pqin)
					emptyEdgeListError = true
				case 1:
					checked[edgeList[0]] = nil
				default:
					logrus.Infof("redundant promotion: multiple edges want to promote the same digest to the same destination endpoint %v:", pqin)
					for _, edge := range edgeList {
						logrus.Infof("%v", edge)
					}
					logrus.Infof("using the first one: %v", edgeList[0])
					checked[edgeList[0]] = nil
				}
			}
		} else {
			logrus.Errorf("multiple edges want to promote *different* images (digests) to the same destination endpoint %v:", pqin)
			for digest, edgeList := range digestToEdges {
				logrus.Errorf("  for digest %v:\n", digest)
				for _, edge := range edgeList {
					logrus.Errorf("%v\n", edge)
				}
			}
			overlapError = true
		}
	}
	if overlapError {
		return nil, fmt.Errorf("overlapping edges detected")
	}
	if emptyEdgeListError {
		return nil, fmt.Errorf("empty edgeList(s) detected")
	}
	return checked, nil
}
CheckOverlappingEdges checks to ensure that all the edges taken together as a
whole are consistent. It checks that there are no duplicate promotions
desired to the same destination vertex (same destination PQIN). If the
digests are the same for those edges, ignore because by definition the
digests are cryptographically guaranteed to be the same thing (it doesn't
matter if 2 different parties want the same exact image to become promoted to
the same destination --- in many ways this is actually a good thing because
it's a form of redundancy). However, return an error if the digests are
different, because most likely this is at best just human error and at worst
a malicious attack (someone trying to push an image to an endpoint they
shouldn't own).
package inventory
import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"
	"github.com/cenkalti/backoff/v4"
	"github.com/google/go-containerregistry/pkg/crane"
	ggcrV1 "github.com/google/go-containerregistry/pkg/v1"
	ggcrV1Google "github.com/google/go-containerregistry/pkg/v1/google"
	ggcrV1Types "github.com/google/go-containerregistry/pkg/v1/types"
	"github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
	"sigs.k8s.io/promo-tools/v3/legacy/gcloud"
	cipJson "sigs.k8s.io/promo-tools/v3/legacy/json"
	"sigs.k8s.io/promo-tools/v3/legacy/reqcounter"
	"sigs.k8s.io/promo-tools/v3/legacy/stream"
)
func GetSrcRegistry(rcs []RegistryContext) (*RegistryContext, error) {
	for _, registry := range rcs {
		registry := registry
		if registry.Src {
			return &registry, nil
		}
	}
	return nil, fmt.Errorf("could not find source registry")
}
func MakeSyncContext(
	mfests []Manifest,
	threads int,
	confirm, useSvcAcc bool,
) (SyncContext, error) {
	sc := SyncContext{
		Threads:           threads,
		Confirm:           confirm,
		UseServiceAccount: useSvcAcc,
		Inv:               make(MasterInventory),
		InvIgnore:         []ImageName{},
		Tokens:            make(map[RootRepo]gcloud.Token),
		RegistryContexts:  make([]RegistryContext, 0),
		DigestMediaType:   make(DigestMediaType),
		DigestImageSize:   make(DigestImageSize),
		ParentDigest:      make(ParentDigest),
	}
	registriesSeen := make(map[RegistryContext]interface{})
	for _, mfest := range mfests {
		for _, r := range mfest.Registries {
			registriesSeen[r] = nil
		}
	}
	for r := range registriesSeen {
		sc.RegistryContexts = append(sc.RegistryContexts, r)
	}
	sort.Slice(
		sc.RegistryContexts,
		func(i, j int) bool {
			return sc.RegistryContexts[i].Name < sc.RegistryContexts[j].Name
		},
	)
	sort.Slice(
		sc.RegistryContexts,
		func(i, j int) bool {
			return len(sc.RegistryContexts[i].Name) > len(sc.RegistryContexts[j].Name)
		},
	)
	if useSvcAcc {
		err := sc.PopulateTokens()
		if err != nil {
			return SyncContext{}, err
		}
	}
	return sc, nil
}
func (sc *SyncContext) LogJSONSummary() {
	marshalled, err := json.MarshalIndent(sc.Logs, "", "  ")
	if err != nil {
		logrus.Infof("There was a problem generating the JSON summary: %v",
			err)
	} else {
		logrus.Info(string(marshalled))
	}
}
func ParseManifestFromFile(filePath string) (Manifest, error) {
	var mfest Manifest
	var empty Manifest
	b, err := ioutil.ReadFile(filePath)
	if err != nil {
		return empty, err
	}
	mfest, err = ParseManifestYAML(b)
	if err != nil {
		return empty, err
	}
	mfest.Filepath = filePath
	err = mfest.Finalize()
	if err != nil {
		return empty, err
	}
	return mfest, nil
}
func ParseThinManifestFromFile(filePath string) (Manifest, error) {
	var thinManifest ThinManifest
	var mfest Manifest
	var empty Manifest
	b, err := ioutil.ReadFile(filePath)
	if err != nil {
		return empty, err
	}
	thinManifest, err = ParseThinManifestYAML(b)
	if err != nil {
		return empty, err
	}
	subProject := filepath.Base(filepath.Dir(filePath))
	imagesPath := filepath.Join(filepath.Dir(filePath),
		"../../images",
		subProject,
		"images.yaml")
	images, err := ParseImagesFromFile(imagesPath)
	if err != nil {
		return empty, err
	}
	mfest.Filepath = filePath
	mfest.Images = images
	mfest.Registries = thinManifest.Registries
	err = mfest.Finalize()
	if err != nil {
		return empty, err
	}
	return mfest, nil
}
func ParseImagesFromFile(filePath string) (Images, error) {
	var images Images
	var empty Images
	b, err := ioutil.ReadFile(filePath)
	if err != nil {
		return empty, err
	}
	images, err = ParseImagesYAML(b)
	if err != nil {
		return empty, err
	}
	return images, nil
}
func (m *Manifest) Finalize() error {
	srcRegistry, err := GetSrcRegistry(m.Registries)
	if err != nil {
		return err
	}
	m.SrcRegistry = srcRegistry
	return nil
}
func ParseThinManifestsFromDir(
	dir string,
) ([]Manifest, error) {
	mfests := make([]Manifest, 0)
	if err := ValidateThinManifestDirectoryStructure(dir); err != nil {
		return mfests, err
	}
	var parseAsManifest filepath.WalkFunc = func(
		path string,
		info os.FileInfo,
		err error,
	) error {
		if err != nil {
			logrus.Errorf("failure accessing a path %q: %v\n", path, err)
		}
		if info.IsDir() {
			return nil
		}
		if filepath.Base(path) != "promoter-manifest.yaml" {
			return nil
		}
		shortened := strings.TrimPrefix(path, dir)
		shortenedList := strings.Split(shortened, "/")
		if len(shortenedList) != ThinManifestDepth {
			return fmt.Errorf("unexpected manifest path %q",
				path)
		}
		mfest, errParse := ParseThinManifestFromFile(path)
		if errParse != nil {
			logrus.Errorf("could not parse manifest file '%s'\n", path)
			return errParse
		}
		mfests = append(mfests, mfest)
		return nil
	}
	if err := filepath.Walk(filepath.Join(dir, "manifests"), parseAsManifest); err != nil {
		return mfests, err
	}
	if len(mfests) == 0 {
		return nil, fmt.Errorf("no manifests found in dir: %s", dir)
	}
	return mfests, nil
}
func ValidateThinManifestDirectoryStructure(
	dir string,
) error {
	if err := validateIsDirectory(filepath.Join(dir, "images")); err != nil {
		return err
	}
	manifestDir := filepath.Join(dir, "manifests")
	if err := validateIsDirectory(manifestDir); err != nil {
		return err
	}
	files, err := ioutil.ReadDir(manifestDir)
	if err != nil {
		return err
	}
	logrus.Infof("*looking at %q", dir)
	for _, file := range files {
		p, err := os.Stat(filepath.Join(manifestDir, file.Name()))
		if err != nil {
			return err
		}
		if !p.IsDir() {
			continue
		}
		manifestInfo, err := os.Stat(
			filepath.Join(manifestDir,
				file.Name(),
				"promoter-manifest.yaml"))
		if err != nil {
			logrus.Warningln(err)
			continue
		}
		if !manifestInfo.Mode().IsRegular() {
			logrus.Warnf("ignoring irregular file %q", manifestInfo)
			continue
		}
		imagesPath := filepath.Join(dir,
			"images",
			file.Name(),
			"images.yaml")
		imagesInfo, err := os.Stat(imagesPath)
		if err != nil {
			if os.IsNotExist(err) {
				return fmt.Errorf("corresponding file %q does not exist",
					imagesPath)
			}
			return err
		}
		if !imagesInfo.Mode().IsRegular() {
			return fmt.Errorf("corresponding file %q is not a regular file",
				imagesPath)
		}
	}
	return nil
}
func validateIsDirectory(dir string) error {
	p, err := os.Stat(filepath.Join(dir))
	if err != nil {
		return err
	}
	if !p.IsDir() {
		return fmt.Errorf("%q is not a directory", dir)
	}
	return nil
}
func ToPromotionEdges(mfests []Manifest) (map[PromotionEdge]interface{}, error) {
	edges := make(map[PromotionEdge]interface{})
	for _, mfest := range mfests {
		for _, image := range mfest.Images {
			for digest, tagArray := range image.Dmap {
				for _, destRC := range mfest.Registries {
					if destRC == *mfest.SrcRegistry {
						continue
					}
					if len(tagArray) > 0 {
						for _, tag := range tagArray {
							edge := mkPromotionEdge(
								*mfest.SrcRegistry,
								destRC,
								image.ImageName,
								digest,
								tag)
							edges[edge] = nil
						}
					} else {
						edge := mkPromotionEdge(
							*mfest.SrcRegistry,
							destRC,
							image.ImageName,
							digest,
							"",
						)
						edges[edge] = nil
					}
				}
			}
		}
	}
	return CheckOverlappingEdges(edges)
}
func mkPromotionEdge(
	srcRC, dstRC RegistryContext,
	srcImageName ImageName,
	digest Digest,
	tag Tag,
) PromotionEdge {
	edge := PromotionEdge{
		SrcRegistry: srcRC,
		SrcImageTag: ImageTag{
			ImageName: srcImageName,
			Tag:       tag,
		},
		Digest:      digest,
		DstRegistry: dstRC,
	}
	edge.DstImageTag = ImageTag{
		ImageName: srcImageName,
		Tag:       tag,
	}
	return edge
}
func (sc *SyncContext) GetPromotionCandidates(edges map[PromotionEdge]interface{}) (
	map[PromotionEdge]interface{},
	bool,
) {
	clean := true
	ignoreMap := make(map[ImageName]interface{})
	for _, ignoreMe := range sc.InvIgnore {
		ignoreMap[ignoreMe] = nil
	}
	toPromote := make(map[PromotionEdge]interface{})
	for edge := range edges {
		if img, ok := ignoreMap[edge.SrcImageTag.ImageName]; ok {
			logrus.Warnf(
				"edge %v: ignoring because src image could not be read: %s\n",
				edge,
				img,
			)
			continue
		}
		sp, dp := edge.VertexProps(&sc.Inv)
		if dp.PqinDigestMatch {
			logrus.Infof("edge %v: skipping because it was already promoted (case 1)\n", edge)
			continue
		}
		if edge.DstImageTag.Tag == "" && dp.DigestExists {
			if !sp.DigestExists {
				logrus.Errorf("edge %v: skipping %s/%s@%s because it was already promoted, but it is still _LOST_ (can't find it in src registry! please backfill it!)\n", edge, edge.SrcRegistry.Name, edge.SrcImageTag.ImageName, edge.Digest)
			}
			continue
		}
		if !sp.DigestExists {
			logrus.Errorf("edge %v: skipping %s/%s@%s because it is _LOST_ (can't find it in src registry!)\n", edge, edge.SrcRegistry.Name, edge.SrcImageTag.ImageName, edge.Digest)
			continue
		}
		if dp.PqinExists {
			if dp.DigestExists {
				if dp.PqinDigestMatch {
					logrus.Infof("edge %v: skipping because it was already promoted (case 2)\n", edge)
					continue
				} else {
					logrus.Errorf("edge %v: tag %s: ERROR: tag move detected from %s to %s", edge, edge.DstImageTag.Tag, edge.Digest, *sc.getDigestForTag(edge.DstImageTag.Tag))
					clean = false
					continue
				}
			} else {
				logrus.Warnf("edge %v: tag %s points to the wrong digest; moving\n", edge, dp.BadDigest)
			}
		} else {
			if dp.DigestExists {
				logrus.Infof("edge %v: digest %q already exists, but does not have the pqin we want (%s)\n", edge, edge.Digest, dp.OtherTags)
			} else {
				logrus.Infof("edge %v: regular promotion (neither digest nor pqin exists in dst)\n", edge)
			}
		}
		toPromote[edge] = nil
	}
	return toPromote, clean
}
func (sc *SyncContext) getDigestForTag(inputTag Tag) *Digest {
	for _, rii := range sc.Inv {
		for _, digestTags := range rii {
			for digest, tagSlice := range digestTags {
				for _, tag := range tagSlice {
					if tag == inputTag {
						return &digest
					}
				}
			}
		}
	}
	return nil
}