Skip to content

Commit

Permalink
✨ match targets with version ranges correctly (#341)
Browse files Browse the repository at this point in the history
Signed-off-by: Pranav Gaikwad <[email protected]>
  • Loading branch information
pranavgaikwad authored Sep 26, 2023
1 parent dca2d15 commit 1d1205d
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 3 deletions.
54 changes: 51 additions & 3 deletions engine/labels/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/PaesslerAG/gval"
"github.com/hashicorp/go-version"
)

const (
Expand Down Expand Up @@ -204,7 +205,7 @@ func getBooleanExpression(expr string, compareLabels map[string][]string) string
}
if labelVals, ok := compareLabels[exprLabelKey]; !ok {
replaceMap[toReplace] = "false"
} else if exprLabelVal != "" && !contains(exprLabelVal, labelVals) {
} else if exprLabelVal != "" && !matchesAny(exprLabelVal, labelVals) {
replaceMap[toReplace] = "false"
} else {
replaceMap[toReplace] = "true"
Expand Down Expand Up @@ -235,11 +236,58 @@ func tokenize(expr string) []string {
return tokens
}

func contains(elem string, items []string) bool {
func matchesAny(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(?:[\d\.]*\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
}
cSemver, err := version.NewSemver(cVersion)
if err != nil {
return cVersion == mVersion
}
mSemver, err := version.NewSemver(mVersion)
if err != nil {
return cVersion == mVersion
}
switch mVersionRangeSymbol {
case "+":
return cSemver.GreaterThanOrEqual(mSemver)
case "-":
return mSemver.GreaterThanOrEqual(cSemver)
default:
return cSemver.Equal(mSemver)
}
}
101 changes: 101 additions & 0 deletions engine/labels/labels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,104 @@ 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,
},
{
name: "float value absolute match",
candidate: "hibernate5.1",
matchWith: "hibernate5.1",
want: true,
},
{
name: "float value range symbol '+' match",
candidate: "hibernate5.2",
matchWith: "hibernate5.1+",
want: true,
},
{
name: "float value range symbol '+' negative match",
candidate: "hibernate5.0.12",
matchWith: "hibernate5.1+",
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)
}
})
}
}

0 comments on commit 1d1205d

Please sign in to comment.