From 5f1acc6b456c1ba467ad594c09558a31bd86c9b4 Mon Sep 17 00:00:00 2001 From: Pranav Gaikwad Date: Mon, 25 Sep 2023 14:04:19 -0400 Subject: [PATCH] :sparkles: match targets with version ranges correctly Signed-off-by: Pranav Gaikwad --- engine/labels/labels.go | 50 +++++++++++++++++++++- engine/labels/labels_test.go | 83 ++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/engine/labels/labels.go b/engine/labels/labels.go index cdd8dbaa..f5f451cc 100644 --- a/engine/labels/labels.go +++ b/engine/labels/labels.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "regexp" + "strconv" "strings" "github.com/PaesslerAG/gval" @@ -237,9 +238,56 @@ func tokenize(expr string) []string { func contains(elem string, items []string) bool { for _, item := range items { - if item == elem { + if labelValueMatches(item, elem) { return true } } return false } + +// labelValueMatches returns true when candidate matches with matchWith +// label value is divided into two parts - name and version +// version is absolute version or a range denoted by + or - +// returns true when names of values are equal and the version of +// candidate falls within the version range of matchWith +func labelValueMatches(matchWith string, candidate string) bool { + versionRegex := regexp.MustCompile(`(\d+)([\+-])?$`) + mMatch := versionRegex.FindStringSubmatch(matchWith) + cMatch := versionRegex.FindStringSubmatch(candidate) + if len(mMatch) != 3 { + return matchWith == candidate + } + mName, mVersion, mVersionRangeSymbol := + versionRegex.ReplaceAllString(matchWith, ""), mMatch[1], mMatch[2] + if len(cMatch) != 3 { + // when no version on candidate, match for any version + return mName == candidate + } + cName, cVersion := + versionRegex.ReplaceAllString(candidate, ""), cMatch[1] + if mName != cName { + return false + } + if mVersion == "" { + return mVersion == cVersion + } + if cVersion == "" { + return true + } + cVersionInt, err := strconv.ParseInt(cVersion, 10, 32) + if err != nil { + return mVersion == cVersion + } + mVersionInt, err := strconv.ParseInt(mVersion, 10, 32) + if err != nil { + return mVersion == cVersion + } + switch mVersionRangeSymbol { + case "+": + return cVersionInt >= mVersionInt + case "-": + return cVersionInt <= mVersionInt + default: + return cVersionInt == mVersionInt + } +} diff --git a/engine/labels/labels_test.go b/engine/labels/labels_test.go index 6bf526d4..0140d7d2 100644 --- a/engine/labels/labels_test.go +++ b/engine/labels/labels_test.go @@ -336,3 +336,86 @@ func Test_ruleSelector_Matches(t *testing.T) { }) } } + +func Test_labelValueMatches(t *testing.T) { + tests := []struct { + name string + candidate string + matchWith string + want bool + }{ + { + name: "no version range test", + candidate: "eap", + matchWith: "eap", + want: true, + }, + { + name: "name mismatch test", + candidate: "eap", + matchWith: "javaee", + want: false, + }, + { + name: "absolute version test", + candidate: "eap6", + matchWith: "eap6", + want: true, + }, + { + name: "version range test for '+'", + candidate: "eap6", + matchWith: "eap5+", + want: true, + }, + { + name: "version range test for '+'", + candidate: "eap5", + matchWith: "eap5+", + want: true, + }, + { + name: "version range test for '-'", + candidate: "eap7", + matchWith: "eap8-", + want: true, + }, + { + name: "version range negative test for '-'", + candidate: "eap9", + matchWith: "eap8-", + want: false, + }, + { + name: "version range negative test for '+'", + candidate: "eap7", + matchWith: "eap8+", + want: false, + }, + { + name: "complex value version range test", + candidate: "Golang Version", + matchWith: "Golang Version11+", + want: true, + }, + { + name: "match any version test", + candidate: "eap", + matchWith: "eap6+", + want: true, + }, + { + name: "match any version test negative", + candidate: "eap6", + matchWith: "eap", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := labelValueMatches(tt.matchWith, tt.candidate); got != tt.want { + t.Errorf("versionRangeMatches() = %v, want %v", got, tt.want) + } + }) + } +}