func MKImageVulnCheck(
	syncContext *SyncContext,
	newPullEdges map[PromotionEdge]interface{},
	severityThreshold int,
	fakeVulnProducer ImageVulnProducer,
) *ImageVulnCheck {
	return &ImageVulnCheck{
		*syncContext,
		newPullEdges,
		severityThreshold,
		fakeVulnProducer,
	}
}
MKImageVulnCheck returns an instance of ImageVulnCheck which
checks against images that have known vulnerabilities.
package inventory
import (
	"context"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"path"
	"sort"
	"strings"
	"sync"
	containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
	"github.com/sirupsen/logrus"
	"google.golang.org/api/iterator"
	grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
	gogit "gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"sigs.k8s.io/promo-tools/v3/legacy/stream"
)
func MBToBytes(value int) int {
	const mbToBytesShift = 20
	return value << mbToBytesShift
}
func BytesToMB(value int) int {
	const bytesToMBShift = 20
	return value >> bytesToMBShift
}
func getGitShaFromEnv(envVar string) (plumbing.Hash, error) {
	potenitalSHA := os.Getenv(envVar)
	const gitShaLength = 40
	if len(potenitalSHA) != gitShaLength {
		return plumbing.Hash{},
			fmt.Errorf("length of SHA is %d characters, should be %d",
				len(potenitalSHA), gitShaLength)
	}
	_, err := hex.DecodeString(potenitalSHA)
	if err != nil {
		return plumbing.Hash{}, fmt.Errorf("not a valid SHA: %v", err)
	}
	return plumbing.NewHash(potenitalSHA), nil
}
func MKRealImageRemovalCheck(
	gitRepoPath string,
	edges map[PromotionEdge]interface{},
) (*ImageRemovalCheck, error) {
	masterSHA, err := getGitShaFromEnv("PULL_BASE_SHA")
	if err != nil {
		return nil, fmt.Errorf("the PULL_BASE_SHA environment variable "+
			"is invalid: %v", err)
	}
	pullRequestSHA, err := getGitShaFromEnv("PULL_PULL_SHA")
	if err != nil {
		return nil, fmt.Errorf("the PULL_PULL_SHA environment variable "+
			"is invalid: %v", err)
	}
	return &ImageRemovalCheck{
			gitRepoPath,
			masterSHA,
			pullRequestSHA,
			edges,
		},
		nil
}
func (check *ImageRemovalCheck) Run() error {
	r, err := gogit.PlainOpen(check.GitRepoPath)
	if err != nil {
		return fmt.Errorf("could not open the Git repo: %v", err)
	}
	w, err := r.Worktree()
	if err != nil {
		return fmt.Errorf("could not create Git worktree: %v", err)
	}
	err = w.Checkout(&gogit.CheckoutOptions{
		Hash:  check.MasterSHA,
		Force: true,
	})
	if err != nil {
		return fmt.Errorf("could not checkout the master branch of the Git"+
			" repo: %v", err)
	}
	mfests, err := ParseThinManifestsFromDir(check.GitRepoPath)
	if err != nil {
		return fmt.Errorf("could not parse manifests from the directory: %v",
			err)
	}
	masterEdges, err := ToPromotionEdges(mfests)
	if err != nil {
		return fmt.Errorf("could not generate promotion edges from promoter"+
			" manifests: %v", err)
	}
	err = w.Checkout(&gogit.CheckoutOptions{
		Hash:  check.PullRequestSHA,
		Force: true,
	})
	if err != nil {
		return fmt.Errorf("could not checkout the pull request branch of the"+
			" Git repo %v: %v",
			check.GitRepoPath, err)
	}
	return check.Compare(masterEdges, check.PullEdges)
}
func (check *ImageRemovalCheck) Compare(
	edgesMaster map[PromotionEdge]interface{},
	edgesPullRequest map[PromotionEdge]interface{},
) error {
	destinationImages := make(map[PromotionEdge]interface{})
	for edge := range edgesPullRequest {
		destinationImages[PromotionEdge{
			DstImageTag: edge.DstImageTag,
			Digest:      edge.Digest,
		}] = nil
	}
	removedImages := make([]string, 0)
	for edge := range edgesMaster {
		_, found := destinationImages[PromotionEdge{
			DstImageTag: edge.DstImageTag,
			Digest:      edge.Digest,
		}]
		if !found {
			removedImages = append(removedImages,
				string(edge.DstImageTag.ImageName))
		}
	}
	if len(removedImages) > 0 {
		return fmt.Errorf("the following images were removed in this pull "+
			"request: %v", strings.Join(removedImages, ", "))
	}
	return nil
}
func (err ImageSizeError) Error() string {
	errStr := ""
	if len(err.OversizedImages) > 0 {
		errStr += fmt.Sprintf("The following images were over the max file "+
			"size of %dMiB:\n%v\n", err.MaxImageSize,
			err.joinImageSizesToString(err.OversizedImages))
	}
	if len(err.InvalidImages) > 0 {
		errStr += fmt.Sprintf("The following images had an invalid file size "+
			"of 0 bytes or less:\n%v\n",
			err.joinImageSizesToString(err.InvalidImages))
	}
	return errStr
}
func (err ImageSizeError) joinImageSizesToString(
	imageSizes map[string]int,
) string {
	imageSizesStr := ""
	imageNames := make([]string, 0)
	for k := range imageSizes {
		imageNames = append(imageNames, k)
	}
	sort.Strings(imageNames)
	for i, imageName := range imageNames {
		imageSizesStr += imageName + " (" +
			fmt.Sprint(BytesToMB(imageSizes[imageName])) + " MiB)"
		if i < len(imageNames)-1 {
			imageSizesStr += "\n"
		}
	}
	return imageSizesStr
}
func MKRealImageSizeCheck(
	maxImageSize int,
	edges map[PromotionEdge]interface{},
	digestImageSize DigestImageSize,
) *ImageSizeCheck {
	return &ImageSizeCheck{
		maxImageSize,
		digestImageSize,
		edges,
	}
}
func (check *ImageSizeCheck) Run() error {
	maxImageSizeByte := MBToBytes(check.MaxImageSize)
	oversizedImages := make(map[string]int)
	invalidImages := make(map[string]int)
	for edge := range check.PullEdges {
		imageSize := check.DigestImageSize[edge.Digest]
		imageName := string(edge.DstImageTag.ImageName)
		if imageSize > maxImageSizeByte {
			oversizedImages[imageName] = imageSize
		}
		if imageSize <= 0 {
			invalidImages[imageName] = imageSize
		}
	}
	if len(oversizedImages) > 0 || len(invalidImages) > 0 {
		return ImageSizeError{
			check.MaxImageSize,
			oversizedImages,
			invalidImages,
		}
	}
	return nil
}