From 3a001d779b5db61747ffe1b51b0de042934b7dd3 Mon Sep 17 00:00:00 2001 From: nicholasSUSE Date: Thu, 13 Feb 2025 15:46:43 -0300 Subject: [PATCH 1/3] changing main implementation to list present tags with skopeo from the prime registry --- pkg/regsync/generateconfig.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/pkg/regsync/generateconfig.go b/pkg/regsync/generateconfig.go index 27a9047b..0559bbef 100644 --- a/pkg/regsync/generateconfig.go +++ b/pkg/regsync/generateconfig.go @@ -11,6 +11,7 @@ import ( "sort" "strings" + "github.com/rancher/charts-build-scripts/pkg/git" "golang.org/x/exp/slices" "gopkg.in/yaml.v2" ) @@ -38,25 +39,34 @@ func GenerateConfigFile() error { return err } - // Check for new image tags from the charts dependencies (value.yaml files) - imageTags, err := checkNewImageTags() + git, err := git.OpenGitRepo(".") if err != nil { return err } - cosignedImages, err := checkCosignedImages(imageTags) + if clean, _ := git.StatusProcelain(); !clean { + if err := git.AddAndCommit("regsync: images and tags present on the current release"); err != nil { + return err + } + } + + newPrimeImgTags, err := checkPrimeImageTags(imageTagMap) if err != nil { return err } - // remove cosigned images from the imageTagMap - removeCosignedImages(imageTagMap, cosignedImages) + syncImgTags := removePrimeImageTags(imageTagMap, newPrimeImgTags) // Update the regsync config file excluding the cosigned images - if err := createRegSyncConfigFile(imageTagMap); err != nil { + if err := createRegSyncConfigFile(syncImgTags); err != nil { return err } + if clean, _ := git.StatusProcelain(); !clean { + if err := git.AddAndCommit("regsync: images to be synced"); err != nil { + return err + } + } return nil } @@ -162,6 +172,8 @@ sync:`) fmt.Fprintln(file) fmt.Fprintln(file, " type: repository") fmt.Fprintln(file, " tags:") + fmt.Fprintln(file, " deny:") + fmt.Fprintln(file, ` - "*"`) fmt.Fprintln(file, " allow:") // We collect all tags and then sort them so there is consistency From 60b83b08db510bf515ffaade0cf7ccc37420d801 Mon Sep 17 00:00:00 2001 From: nicholasSUSE Date: Thu, 13 Feb 2025 15:50:42 -0300 Subject: [PATCH 2/3] prime registry interaction implementation with skopeo package --- pkg/regsync/prime.go | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 pkg/regsync/prime.go diff --git a/pkg/regsync/prime.go b/pkg/regsync/prime.go new file mode 100644 index 00000000..8efc702b --- /dev/null +++ b/pkg/regsync/prime.go @@ -0,0 +1,88 @@ +package regsync + +import ( + "encoding/json" + "fmt" + "os/exec" + "strings" +) + +type skopeo struct { + Repository string `json:"Repository"` + Tags []string `json:"Tags"` +} + +// checkPrimeImageTags checks the prime image tags on the registry. +func checkPrimeImageTags(imageTags map[string][]string) (map[string][]string, error) { + var primeImageTags map[string][]string = make(map[string][]string) + + fmt.Println("checking prime image tags") + for image := range imageTags { + if image == "" { + continue + } + fmt.Printf("image: %s\n", image) + tags, err := skopeoListTags(image) + if err != nil { + return nil, err + } + fmt.Printf("Tags: %s\n", strings.Join(tags, ", ")) + primeImageTags[image] = tags + } + + return primeImageTags, nil +} + +// skopeoListTags uses skopeo package to list tags of a given image on a specific registry. +// at the time this was implemented there was no go package to list tags of an image on a registry. +// skopeo is written in go, for speed we are not rewriting skopeo fucntionalities in go. +func skopeoListTags(image string) ([]string, error) { + suseRegistry := "docker://registry.suse.com/" + + cmd := exec.Command("skopeo", "list-tags", suseRegistry+image) + output, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error executing command: %v", err) + } + + var tags skopeo + if err := json.Unmarshal(output, &tags); err != nil { + return nil, fmt.Errorf("error parsing JSON output: %v", err) + } + + return tags.Tags, nil +} + +// removePrimeImageTags will only allow the tags that are not present in the prime image tags. +func removePrimeImageTags(imageTagMap, newPrimeImgTags map[string][]string) map[string][]string { + var syncImgTags map[string][]string = make(map[string][]string) + + for image, tags := range imageTagMap { + if image == "" { + continue + } + syncImgTags[image] = []string{} + for _, tag := range tags { + if tag == "" { + continue + } + primeTags := newPrimeImgTags[image] + if exist := primeTagFinder(tag, primeTags); !exist { + syncImgTags[image] = append(syncImgTags[image], tag) + fmt.Println(syncImgTags) + } + } + } + + return syncImgTags +} + +// primeTagFinder checks if the tag is present in the prime tags. +func primeTagFinder(tag string, primeTags []string) bool { + for _, primeTag := range primeTags { + if tag == primeTag { + return true + } + } + return false +} From d5586e3dd67cd0a1850eee93f69eaffd46e253bd Mon Sep 17 00:00:00 2001 From: nicholasSUSE Date: Thu, 13 Feb 2025 15:51:56 -0300 Subject: [PATCH 3/3] removing old cosign check implementation and unit-tests, keep only for skopeo registry interaction --- pkg/regsync/cosign.go | 312 --------------------- pkg/regsync/cosign_test.go | 536 ------------------------------------- pkg/regsync/prime_test.go | 179 +++++++++++++ 3 files changed, 179 insertions(+), 848 deletions(-) delete mode 100644 pkg/regsync/cosign.go delete mode 100644 pkg/regsync/cosign_test.go create mode 100644 pkg/regsync/prime_test.go diff --git a/pkg/regsync/cosign.go b/pkg/regsync/cosign.go deleted file mode 100644 index f12ba0c2..00000000 --- a/pkg/regsync/cosign.go +++ /dev/null @@ -1,312 +0,0 @@ -package regsync - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - "os/exec" - "strings" - - "github.com/rancher/charts-build-scripts/pkg/path" - slsactl "github.com/rancherlabs/slsactl/pkg/verify" - - "github.com/sourcegraph/go-diff/diff" -) - -type insertedDiffs struct { - sourceImage string - insertions []insertion -} - -type insertion struct { - tag string - line int32 -} - -// checkNewImageTags will get the git diff for regsync.yaml and parse the new available image tags. -func checkNewImageTags() (map[string][]string, error) { - rawDiff, err := getRawDiff(path.RegsyncYamlFile) - if err != nil { - return nil, err - } - - insertedDiffsToParse, err := parseGitDiff(rawDiff) - if err != nil { - return nil, err - } - - parsedRegsync, err := mapFileLineByValue(path.RegsyncYamlFile) - if err != nil { - return nil, err - } - - return parseSourceImage(insertedDiffsToParse, parsedRegsync) -} - -// getRawDiff returns the raw git diff output for a specific file -func getRawDiff(filePath string) (string, error) { - // Run the git diff command for the specific file - cmd := exec.Command("git", "diff", filePath) - - var stdout bytes.Buffer - var stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - - // Execute the command - err := cmd.Run() - if err != nil { - return "", fmt.Errorf("failed to execute git diff: %v, %s", err, stderr.String()) - } - - return stdout.String(), nil -} - -func parseGitDiff(rawDiff string) ([]insertedDiffs, error) { - // Parse the raw diff into structured format - file, err := diff.ParseFileDiff([]byte(rawDiff)) - if err != nil { - return nil, err - } - - idfs := make([]insertedDiffs, len(file.Hunks)) - - // Iterate through file diffs - for i, hunk := range file.Hunks { - currentLine := hunk.NewStartLine // Starting line in the new file - idfs[i] = insertedDiffs{ - insertions: make([]insertion, 0), - } - - // Split the hunk body into lines - lines := bytes.Split(hunk.Body, []byte("\n")) - for _, line := range lines { - if len(line) > 0 && line[0] == '+' { - if bytes.Contains(line, []byte("+++")) || - bytes.Contains(line, []byte("source")) || - bytes.Contains(line, []byte("target")) || - bytes.Contains(line, []byte("type")) || - bytes.Contains(line, []byte("tags")) || - bytes.Contains(line, []byte("allow")) || - bytes.Contains(line, []byte("sync")) || - bytes.Contains(line, []byte("default")) || - bytes.Contains(line, []byte("media")) { - currentLine++ - continue - } - // Output the added line and its calculated line number - idfs[i].insertions = append(idfs[i].insertions, insertion{ - tag: parseTag(string(line[1:])), - line: currentLine, - }) - currentLine++ - continue - } - // Skip any line that was not inserted - currentLine++ - } - } - return idfs, nil -} - -// parseTag will parse the tag from the raw line value. -func parseTag(tag string) string { - parsedTag := strings.TrimSpace(tag) - parsedTag = strings.TrimPrefix(parsedTag, "- ") - return parsedTag -} - -// mapFileLineByValue reads a file and maps line numbers to their content. -func mapFileLineByValue(filePath string) (map[int]string, error) { - // Open the file - file, err := os.Open(filePath) - if err != nil { - return nil, fmt.Errorf("failed to open file: %v", err) - } - defer file.Close() - - // Map to store line numbers and their content - lineToContent := make(map[int]string) - - // Read the file line by line - scanner := bufio.NewScanner(file) - lineNumber := 1 - for scanner.Scan() { - lineToContent[lineNumber] = scanner.Text() - lineNumber++ - } - - // Check for errors during scanning - if err := scanner.Err(); err != nil { - return nil, fmt.Errorf("failed to read file: %v", err) - } - - return lineToContent, nil -} - -// parseSourceImage will parse the source image and its tags from the regsync.yaml. -func parseSourceImage(insertedDiffs []insertedDiffs, parsedRegsync map[int]string) (map[string][]string, error) { - - var currentSourceImage string - var imageTags map[string][]string = make(map[string][]string) - - for _, idf := range insertedDiffs { - for i, insertion := range idf.insertions { - // iterate only the very first time to get the source image - if i == 0 { - sourceImage, err := findSourceImage(parsedRegsync, int(insertion.line)) - if err != nil { - return imageTags, err - } - parsedSource := strings.TrimPrefix(sourceImage, "- source: docker.io/") - currentSourceImage = parsedSource - imageTags[parsedSource] = []string{} - } - imageTags[currentSourceImage] = append(imageTags[currentSourceImage], insertion.tag) - } - } - - return imageTags, nil -} - -// findSourceImage will find the source image from the regsync.yaml above the line number passed. -func findSourceImage(parsedRegsync map[int]string, maxLine int) (string, error) { - for i := maxLine; i > 0; i-- { - lineTxt := parsedRegsync[i] - if strings.Contains(lineTxt, "source") { - return parsedRegsync[i], nil - } - } - return "", fmt.Errorf("source image not found around line %d", maxLine) -} - -// checkCosignedImages checks if the images are cosigned or not. -func checkCosignedImages(imageTags map[string][]string) (map[string][]string, error) { - // prepare the signed-images.txt file for later manual inspection - if err := prepareSignedImagesFile(); err != nil { - return nil, err - } - - baseImageRepository := "registry.suse.com/" - cosignedImages := make(map[string][]string) - - for image, tags := range imageTags { - for _, tag := range tags { - imageTag := baseImageRepository + image + ":" + tag - fmt.Printf("checking: %s\n", imageTag) - // Close the stdout and stderr so there are no leaks - oldStdout, oldStderr, r, w := closeStdOut() - // Verify the image, if it's cosigned - if err := slsactl.Verify(imageTag); err != nil { - reopenStdOut(oldStdout, oldStderr, r, w) - continue - } - reopenStdOut(oldStdout, oldStderr, r, w) - - // add it to the cosignedImages map - cosignedImages[image] = append(cosignedImages[image], tag) - - // log and write the cosigned images to a file - fmt.Printf("cosigned - %s\n", imageTag) - if err := writeCosignedImages(imageTag); err != nil { - return nil, err - } - } - } - - return cosignedImages, nil -} - -// prepareSignedImagesFile checks if signed-images.txt exists. -// If it exists, it erases all its content. If it does not exist, it creates it. -func prepareSignedImagesFile() error { - // Check if the file exists - _, err := os.Stat(path.SignedImagesFile) - if err != nil { - if os.IsNotExist(err) { - // Create the file if it does not exist - file, err := os.Create(path.SignedImagesFile) - if err != nil { - return err - } - - return file.Close() - } - // some other error occurred while checking the file - return err - } - - return os.Truncate(path.SignedImagesFile, 0) -} - -// writeCosignedImages writes the imageTag to a text file called signed-images.txt. -func writeCosignedImages(imageTag string) error { - // Open the file in append mode, create it if it doesn't exist - file, err := os.OpenFile(path.SignedImagesFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return err - } - defer file.Close() - - // Write the imageTag to the file - if _, err := file.WriteString(imageTag + "\n"); err != nil { - return err - } - - return nil -} - -// closeStdOut closes the stdout and stderr and returns the old ones. -func closeStdOut() (*os.File, *os.File, *os.File, *os.File) { - // Save the current stdout and stderr - oldStdout := os.Stdout - oldStderr := os.Stderr - // Create a pipe to discard the output - r, w, _ := os.Pipe() - os.Stdout = w - os.Stderr = w - return oldStdout, oldStderr, r, w -} - -// reopenStdOut reopens the stdout and stderr and reads the discarded output. -func reopenStdOut(oldStdout, oldStderr, r, w *os.File) { - w.Close() - os.Stdout = oldStdout - os.Stderr = oldStderr - io.ReadAll(r) // Read the discarded output to prevent pipe errors -} - -// removeCosignedImages removes the cosigned images from the imageTagMap. -func removeCosignedImages(imageTagMap, cosignedImages map[string][]string) { - for cosignedImage, cosignedTags := range cosignedImages { - if _, exist := imageTagMap[cosignedImage]; exist { - currentTags := imageTagMap[cosignedImage] - notCosignedTags := excludeCosignedTags(currentTags, cosignedTags) - imageTagMap[cosignedImage] = notCosignedTags - } - } -} - -// excludeCosignedTags removes the cosigned tags from the tags slice. -func excludeCosignedTags(tags, cosignedTags []string) []string { - var isCosigned bool = false - var notCosignedTags []string - for _, tag := range tags { - for _, cTag := range cosignedTags { - if tag == cTag { - isCosigned = true - break - } - } - - if !isCosigned { - notCosignedTags = append(notCosignedTags, tag) - } else { - isCosigned = false - continue - } - } - return notCosignedTags -} diff --git a/pkg/regsync/cosign_test.go b/pkg/regsync/cosign_test.go deleted file mode 100644 index 037daf41..00000000 --- a/pkg/regsync/cosign_test.go +++ /dev/null @@ -1,536 +0,0 @@ -package regsync - -import ( - "errors" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var ( - diffExample1 = `diff --git a/regsync.yaml b/regsync.yaml -index c584669a3..ab15c1bc5 100644 ---- a/regsync.yaml -+++ b/regsync.yaml -@@ -76,6 +76,26 @@ sync: - - v5.0.1 - - v5.0.2 - - v6.0.0 -+- source: docker.io/rancher/cis-operator -+ target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/cis-operator' -+ type: repository -+ tags: -+ allow: -+ - v1.1.0 -+ - v1.2.0 -+ - v1.3.0 - - source: docker.io/rancher/eks-operator - target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/eks-operator' - type: repository -` - - diffExample2 = `diff --git a/regsync.yaml b/regsync.yaml -index c584669a3..ee5f53416 100644 ---- a/regsync.yaml -+++ b/regsync.yaml -@@ -76,6 +76,28 @@ sync: - - v5.0.1 - - v5.0.2 - - v6.0.0 -+- source: docker.io/rancher/cis-operator -+ target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/cis-operator' -+ type: repository -+ tags: -+ allow: -+ - v1.1.0 -+ - v1.2.0 -+ - v1.3.0 -+ - v1.3.1 -+ - v1.3.3 - - source: docker.io/rancher/eks-operator - target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/eks-operator' - type: repository -@@ -1426,6 +1448,7 @@ sync: - - v0.4.0 - - v0.5.0 - - v0.5.1 -+ - v0.5.2 - - source: docker.io/rancher/shell - target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/shell' - type: repository -` - - badDiffExample = `@@ -76,6 +76,28 @@ sync: - - v5.0.1 - - v5.0.2 - - v6.0.0 -+- source: docker.io/rancher/cis-operator -+ target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/cis-operator' -+ type: repository -+ tags: -+ allow: -+ - v1.1.0 -+ - v1.2.0 -+ - v1.3.0 -+ - v1.3.1 -+ - v1.3.3 - - source: docker.io/rancher/eks-operator - target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/eks-operator' - type: repository -@@ -1426,6 +1448,7 @@ sync: - - v0.4.0 - - v0.5.0 - - v0.5.1 -+ - v0.5.2 - - source: docker.io/rancher/shell - target: '{{ env "REGISTRY_ENDPOINT" }}/rancher/shell' - type: repository -` -) - -func assertError(t *testing.T, err, expectedErr error) { - if expectedErr != nil { - assert.EqualError(t, err, expectedErr.Error()) - } else { - assert.NoError(t, err) - } -} - -func Test_parseGitDiff(t *testing.T) { - type input struct { - diff string - } - type expected struct { - result []insertedDiffs - err error - } - type test struct { - name string - input input - expected expected - } - - tests := []test{ - { - name: "#1", - input: input{ - diff: diffExample1, - }, - expected: expected{ - result: []insertedDiffs{ - insertedDiffs{ - insertions: []insertion{ - insertion{ - tag: "v1.1.0", - line: 84, - }, - insertion{ - tag: "v1.2.0", - line: 85, - }, - insertion{ - tag: "v1.3.0", - line: 86, - }, - }, - }, - }, - err: nil, - }, - }, - { - name: "#2", - input: input{ - diff: diffExample2, - }, - expected: expected{ - result: []insertedDiffs{ - insertedDiffs{ - insertions: []insertion{ - insertion{ - tag: "v1.1.0", - line: 84, - }, - insertion{ - tag: "v1.2.0", - line: 85, - }, - insertion{ - tag: "v1.3.0", - line: 86, - }, - insertion{ - tag: "v1.3.1", - line: 87, - }, - insertion{ - tag: "v1.3.3", - line: 88, - }, - }, - }, - insertedDiffs{ - insertions: []insertion{ - insertion{ - tag: "v0.5.2", - line: 1451, - }, - }, - }, - }, - err: nil, - }, - }, - { - name: "#3", - input: input{ - diff: badDiffExample, - }, - expected: expected{ - result: nil, - err: errors.New("line 25, char 581: expected file header while reading extended headers, got EOF"), - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - insertionsToParse, err := parseGitDiff(tt.input.diff) - require.Equal(t, tt.expected.result, insertionsToParse) - assertError(t, err, tt.expected.err) - }) - } - -} - -func Test_parseTag(t *testing.T) { - type input struct { - line string - } - type expected struct { - result string - } - type test struct { - name string - input input - expected expected - } - - tests := []test{ - { - name: "#1", - input: input{ - line: "- v1.0.0", - }, - expected: expected{ - result: "v1.0.0", - }, - }, - { - name: "#2", - input: input{ - line: "- v1.0.1", - }, - expected: expected{ - result: "v1.0.1", - }, - }, - { - name: "#3", - input: input{ - line: " - v1.1.0", - }, - expected: expected{ - result: "v1.1.0", - }, - }, - { - name: "#4", - input: input{ - line: " - 1.1.0", - }, - expected: expected{ - result: "1.1.0", - }, - }, - { - name: "#5", - input: input{ - line: " - 1.1.0-rc1.0.0", - }, - expected: expected{ - result: "1.1.0-rc1.0.0", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := parseTag(tt.input.line) - require.Equal(t, tt.expected.result, result) - }) - } -} - -func Test_parseSourceImage(t *testing.T) { - type input struct { - insertedDiffs []insertedDiffs - parsedRegsync map[int]string - } - type expected struct { - result map[string][]string - err error - } - type test struct { - name string - input input - expected expected - } - - tests := []test{ - { - name: "#1", - input: input{ - insertedDiffs: []insertedDiffs{ - { - insertions: []insertion{ - { - tag: "1.0.0", - line: 100, - }, - { - tag: "1.0.1", - line: 101, - }, - { - tag: "1.1.0", - line: 102, - }, - }, - }, - }, - parsedRegsync: map[int]string{ - 95: "- source: docker.io/rancher/cis-operator", - 96: " target: '{{ env REGISTRY_ENDPOINT }}/rancher/cis-operator'", - 97: " type: repository", - 98: " tags:", - 99: " allow:", - 100: " - 1.0.0", - 101: " - 1.0.1", - 102: " - 1.1.0", - }, - }, - expected: expected{ - result: map[string][]string{ - "rancher/cis-operator": []string{"1.0.0", "1.0.1", "1.1.0"}, - }, - err: nil, - }, - }, - { - name: "#2", - input: input{ - insertedDiffs: []insertedDiffs{ - { - insertions: []insertion{ - { - tag: "1.0.0", - line: 100, - }, - { - tag: "1.0.1", - line: 101, - }, - { - tag: "1.1.0", - line: 102, - }, - }, - }, - { - insertions: []insertion{ - { - tag: "0.0.1", - line: 100000000, - }, - }, - }, - }, - parsedRegsync: map[int]string{ - 95: "- source: docker.io/rancher/cis-operator", - 96: " target: '{{ env REGISTRY_ENDPOINT }}/rancher/cis-operator'", - 97: " type: repository", - 98: " tags:", - 99: " allow:", - 100: " - 1.0.0", - 101: " - 1.0.1", - 102: " - 1.1.0", - - 99999995: "- source: docker.io/rancher/security-scan", - 99999996: " target: '{{ env REGISTRY_ENDPOINT }}/rancher/sec-scan'", - 99999997: " type: repository", - 99999998: " tags:", - 99999999: " allow:", - 100000000: " - 0.0.1", - }, - }, - expected: expected{ - result: map[string][]string{ - "rancher/cis-operator": []string{"1.0.0", "1.0.1", "1.1.0"}, - "rancher/security-scan": []string{"0.0.1"}, - }, - err: nil, - }, - }, - { - name: "#3", - input: input{ - insertedDiffs: []insertedDiffs{ - { - insertions: []insertion{ - { - tag: "1.0.0", - line: 4, - }, - }, - }, - }, - parsedRegsync: map[int]string{ - 1: " type: repository", - 2: " tags:", - 3: " allow:", - 4: " - 1.0.0", - }, - }, - expected: expected{ - result: map[string][]string{}, - err: fmt.Errorf("source image not found around line %d", 4), - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := parseSourceImage(tt.input.insertedDiffs, tt.input.parsedRegsync) - require.Equal(t, tt.expected.result, result) - assertError(t, err, tt.expected.err) - }) - } -} - -func Test_removeCosignedImages(t *testing.T) { - type input struct { - imageTagMap map[string][]string - cosignedImages map[string][]string - } - type expected struct { - result map[string][]string - } - type test struct { - name string - input input - expected expected - } - - tests := []test{ - { - name: "#1", - input: input{ - imageTagMap: map[string][]string{ - "rancher/cis-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/security-scan": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, - }, - cosignedImages: map[string][]string{ - "rancher/cis-operator": {"v1.0.1", "v1.1.0"}, - "rancher/security-scan": {"v1.1.0"}, - }, - }, - expected: expected{ - result: map[string][]string{ - "rancher/cis-operator": {"v1.0.0"}, - "rancher/security-scan": {"v1.0.0", "v1.0.1"}, - "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, - }, - }, - }, - { - name: "#2", - input: input{ - imageTagMap: map[string][]string{ - "rancher/cis-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/security-scan": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, - }, - cosignedImages: map[string][]string{ - "rancher/cis-operator": {"v1.1.0"}, - "rancher/security-scan": {"v1.1.0"}, - }, - }, - expected: expected{ - result: map[string][]string{ - "rancher/cis-operator": {"v1.0.0", "v1.0.1"}, - "rancher/security-scan": {"v1.0.0", "v1.0.1"}, - "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, - }, - }, - }, - { - name: "#3", - input: input{ - imageTagMap: map[string][]string{ - "rancher/cis-operator": {"v1.0.0"}, - "rancher/security-scan": {"v1.0.0"}, - "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, - }, - cosignedImages: map[string][]string{ - "rancher/security-scan": {}, - }, - }, - expected: expected{ - result: map[string][]string{ - "rancher/cis-operator": {"v1.0.0"}, - "rancher/security-scan": {"v1.0.0"}, - "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, - }, - }, - }, - { - name: "#4", - input: input{ - imageTagMap: map[string][]string{ - "rancher/cis-operator": {"v1.0.0"}, - "rancher/security-scan": {"v1.0.0"}, - "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, - }, - cosignedImages: map[string][]string{ - "rancher/security-scan": {"2.0.0"}, - }, - }, - expected: expected{ - result: map[string][]string{ - "rancher/cis-operator": {"v1.0.0"}, - "rancher/security-scan": {"v1.0.0"}, - "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, - "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - removeCosignedImages(tt.input.imageTagMap, tt.input.cosignedImages) - require.Equal(t, tt.expected.result, tt.input.imageTagMap) - }) - } -} diff --git a/pkg/regsync/prime_test.go b/pkg/regsync/prime_test.go new file mode 100644 index 00000000..522ece30 --- /dev/null +++ b/pkg/regsync/prime_test.go @@ -0,0 +1,179 @@ +package regsync + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_removePrimeImageTags(t *testing.T) { + type input struct { + imageTagMap map[string][]string + primeImageTags map[string][]string + } + type expected struct { + result map[string][]string + } + type test struct { + name string + input input + expected expected + } + + tests := []test{ + { + name: "#1", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/security-scan": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + primeImageTags: map[string][]string{ + "rancher/cis-operator": {"v1.0.1", "v1.1.0"}, + "rancher/security-scan": {"v1.1.0"}, + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0", "v1.0.1"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + }, + }, + { + name: "#2", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/security-scan": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + primeImageTags: map[string][]string{ + "rancher/cis-operator": {"v1.1.0"}, + "rancher/security-scan": {"v1.1.0"}, + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": {"v1.0.0", "v1.0.1"}, + "rancher/security-scan": {"v1.0.0", "v1.0.1"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + }, + }, + { + name: "#3", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + primeImageTags: map[string][]string{ + "rancher/security-scan": {}, + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + }, + }, + { + name: "#4", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + primeImageTags: map[string][]string{ + "rancher/security-scan": {"2.0.0"}, + }, + }, + expected: expected{ + result: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + "rancher/security-scan": {"v1.0.0"}, + "rancher/eks-operator": {"v1.0.0", "v1.0.1", "v1.1.0"}, + "rancher/shell": {"v1.0.0", "v1.0.1", "v1.1.0"}, + }, + }, + }, + // edge cases + { + name: "#5", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + }, + primeImageTags: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + }, + }, + expected: expected{ + result: map[string][]string{"rancher/cis-operator": []string{}}, + }, + }, + { + name: "#6", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {}, + }, + primeImageTags: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + }, + }, + expected: expected{ + result: map[string][]string{"rancher/cis-operator": []string{}}, + }, + }, + { + name: "#7", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {"v1.0.0"}, + }, + primeImageTags: map[string][]string{ + "rancher/cis-operator": {}, + }, + }, + expected: expected{ + result: map[string][]string{"rancher/cis-operator": []string{"v1.0.0"}}, + }, + }, + { + name: "#8", + input: input{ + imageTagMap: map[string][]string{ + "rancher/cis-operator": {}, + }, + primeImageTags: map[string][]string{ + "rancher/cis-operator": {}, + }, + }, + expected: expected{ + result: map[string][]string{"rancher/cis-operator": []string{}}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + notPrimeImageTags := removePrimeImageTags(tt.input.imageTagMap, tt.input.primeImageTags) + require.Equal(t, tt.expected.result, notPrimeImageTags) + }) + } +}