1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-31 04:04:51 +00:00
This commit is contained in:
Marcin Franczyk 2025-03-28 09:09:13 +00:00 committed by GitHub
commit ba4a5b1728
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 490 additions and 61 deletions

View file

@ -296,16 +296,44 @@ type MatchExpression struct {
// In other cases Value should contain at least one element.
// +optional
Value MatchValue `json:"value,omitempty"`
// Type defines the value type for specific operators.
// The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
// +optional
Type ValueType `json:"type,omitempty"`
}
// MatchOp is the match operator that is applied on values when evaluating a
// MatchExpression.
// +kubebuilder:validation:Enum="In";"NotIn";"InRegexp";"Exists";"DoesNotExist";"Gt";"Lt";"GtLt";"IsTrue";"IsFalse"
// +kubebuilder:validation:Enum="In";"NotIn";"InRegexp";"Exists";"DoesNotExist";"Gt";"Ge";"Lt";"Le";"GtLt";"GeLe";"IsTrue";"IsFalse"
type MatchOp string
// MatchValue is the list of values associated with a MatchExpression.
type MatchValue []string
// VersionComparisonResult represents the output of versions comparison.
type VersionComparisonResult int
const (
// CmpGt returns 1 when version1 > version2
CmpGt VersionComparisonResult = 1
// CmpLt returns -1 when version1 < version2
CmpLt VersionComparisonResult = -1
// CmpEq returns 0 when version1 == version2
CmpEq VersionComparisonResult = 0
)
// ValueType represents the type of value in the expression.
type ValueType string
const (
// TypeVersion represents a version with the following supported formats:
// %d.%d.%d (e.g., 1.2.3),
// %d.%d (e.g., 1.2),
// %d (e.g., 1)
TypeVersion ValueType = "version"
)
const (
// MatchAny returns always true.
MatchAny MatchOp = ""

View file

@ -18,6 +18,8 @@ package v1alpha1
import (
"fmt"
"strconv"
"strings"
)
// String represents the match expression as a string type.
@ -27,3 +29,59 @@ func (m MatchExpression) String() string {
}
return fmt.Sprintf("{op: %q, value: %q}", m.Op, m.Value)
}
// ExtractVersion extracts version from string with the following formats:
// %d.%d.%d (e.g., 1.2.3)
// %d.%d (e.g., 1.2)
// %d (e.g., 1)
func ExtractVersion(s string) (string, error) {
var major, minor, patch int
_, err := fmt.Sscanf(s, "%d.%d.%d", &major, &minor, &patch)
if err == nil {
return fmt.Sprintf("%d.%d.%d", major, minor, patch), nil
}
_, err = fmt.Sscanf(s, "%d.%d", &major, &minor)
if err == nil {
return fmt.Sprintf("%d.%d", major, minor), nil
}
_, err = fmt.Sscanf(s, "%d", &major)
if err == nil {
return fmt.Sprintf("%d", major), nil
}
return "", fmt.Errorf("unable to extract semantic version from value: %s", s)
}
// CompareVersions compare two versions with the following formats:
// %d.%d.%d (e.g., 1.2.3)
// %d.%d (e.g., 1.2)
// %d (e.g., 1)
// Returns:
// -1 if v1 < v2
// 0 if v1 == v2
// 1 if v1 > v2
func CompareVersions(v1, v2 string) VersionComparisonResult {
p1 := strings.Split(v1, ".")
p2 := strings.Split(v2, ".")
maxLen := max(len(p1), len(p2))
for i := 0; i < maxLen; i++ {
var num1, num2 int
if i < len(p1) {
num1, _ = strconv.Atoi(p1[i])
}
if i < len(p2) {
num2, _ = strconv.Atoi(p2[i])
}
if num1 < num2 {
return CmpLt
} else if num1 > num2 {
return CmpGt
}
}
return CmpEq
}

View file

@ -0,0 +1,70 @@
package v1alpha1
import (
"testing"
"github.com/stretchr/testify/assert"
)
type ValueAssertionFunc func(assert.TestingT, interface{}, ...interface{}) bool
func TestExtractVersion(t *testing.T) {
tcs := []struct {
name string
input string
result string
err ValueAssertionFunc
}{
{name: "1", input: "1", result: "1", err: assert.Nil},
{name: "2", input: "1.2", result: "1.2", err: assert.Nil},
{name: "3", input: "1.2.3", result: "1.2.3", err: assert.Nil},
{name: "4", input: "1.2.3-flavor", result: "1.2.3", err: assert.Nil},
{name: "5", input: "1.2.3-flavor.2.3.4", result: "1.2.3", err: assert.Nil},
{name: "6", input: "flavor-1.2.3", result: "", err: assert.NotNil},
{name: "7", input: "A.2.3", result: "", err: assert.NotNil},
{name: "8", input: "str", result: "", err: assert.NotNil},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
result, err := ExtractVersion(tc.input)
assert.Equal(t, result, tc.result)
tc.err(t, err)
})
}
}
func TestCompareVersions(t *testing.T) {
tcs := []struct {
name string
v1 string
v2 string
result VersionComparisonResult
}{
{name: "1", v1: "1", v2: "2", result: CmpLt},
{name: "2", v1: "2", v2: "2", result: CmpEq},
{name: "3", v1: "3", v2: "2", result: CmpGt},
{name: "4", v1: "1.9", v2: "2.0", result: CmpLt},
{name: "5", v1: "2.0", v2: "2.0", result: CmpEq},
{name: "6", v1: "2.1", v2: "2.0", result: CmpGt},
{name: "7", v1: "1.9.9", v2: "2.0.0", result: CmpLt},
{name: "8", v1: "2.0.0", v2: "2.0.0", result: CmpEq},
{name: "9", v1: "2.0.1", v2: "2.0.0", result: CmpGt},
{name: "10", v1: "1", v2: "2.0", result: CmpLt},
{name: "11", v1: "2", v2: "2.0", result: CmpEq},
{name: "12", v1: "3", v2: "2.0", result: CmpGt},
{name: "13", v1: "1", v2: "2.0.0", result: CmpLt},
{name: "14", v1: "2", v2: "2.0.0", result: CmpEq},
{name: "15", v1: "3", v2: "2.0.0", result: CmpGt},
{name: "16", v1: "1.9.9", v2: "2", result: CmpLt},
{name: "17", v1: "2.0.0", v2: "2", result: CmpEq},
{name: "18", v1: "2.0.1", v2: "2", result: CmpGt},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
result := CompareVersions(tc.v1, tc.v2)
assert.Equal(t, result, tc.result)
})
}
}

View file

@ -204,11 +204,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -240,11 +248,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -295,11 +311,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -331,11 +355,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -511,11 +543,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -547,11 +587,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -602,11 +650,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -638,11 +694,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input

View file

@ -204,11 +204,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -240,11 +248,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -295,11 +311,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -331,11 +355,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -511,11 +543,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -547,11 +587,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -602,11 +650,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input
@ -638,11 +694,19 @@ spec:
- Exists
- DoesNotExist
- Gt
- Ge
- Lt
- Le
- GtLt
- GeLe
- IsTrue
- IsFalse
type: string
type:
description: |-
Type defines the value type for specific operators.
The currently supported type is 'version' for Gt,Ge,Lt,Le,GtLt,GeLe operators.
type: string
value:
description: |-
Value is the list of values that the operand evaluates the input

View file

@ -786,6 +786,7 @@ feature.
value:
- <value-1>
- ...
type: <type>
```
In each MatchExpression the `key` specifies the name of of the feature element
@ -820,6 +821,13 @@ below.
The `value` field of MatchExpression is a list of string arguments to the
operator.
Type optional `type` field specifies the type of the `value` field.
Valid types for specific operators are described below.
| Type | Description | Supported Operators |
| --------- | ----------- | ------------------- |
| `version` | Input is recognized as a version in the following formats `%d.%d.%d`, `%d.%d`, `%d` (e.g., "1.2.3", "1.2", "1") |`Gt`,`Ge`,`Lt`,`Le`,`GtLt`,`GeLe` |
##### matchName
The `.matchFeatures[].matchName` field is used to match against the

View file

@ -122,40 +122,94 @@ func evaluateMatchExpression(m *nfdv1alpha1.MatchExpression, valid bool, value i
if len(m.Value) != 1 {
return false, fmt.Errorf("invalid expression, 'value' field must contain exactly one element for Op %q (have %v)", m.Op, m.Value)
}
l, err := strconv.Atoi(value)
if err != nil {
return false, fmt.Errorf("not a number %q", value)
}
r, err := strconv.Atoi(m.Value[0])
if err != nil {
return false, fmt.Errorf("not a number %q in %v", m.Value[0], m)
if m.Type != "" && m.Type != nfdv1alpha1.TypeVersion {
return false, fmt.Errorf("invalid expression, 'type' field only accepts %q value for Op %q (have %q)", nfdv1alpha1.TypeVersion, m.Op, m.Type)
}
if (l < r && m.Op == nfdv1alpha1.MatchLt) || (l <= r && m.Op == nfdv1alpha1.MatchLe) ||
(l > r && m.Op == nfdv1alpha1.MatchGt) || (l >= r && m.Op == nfdv1alpha1.MatchGe) {
return true, nil
switch m.Type {
case nfdv1alpha1.TypeVersion:
l, err := nfdv1alpha1.ExtractVersion(value)
if err != nil {
return false, fmt.Errorf("not a version %q", value)
}
r, err := nfdv1alpha1.ExtractVersion(m.Value[0])
if err != nil {
return false, fmt.Errorf("not a version %q in %v", m.Value[0], m)
}
cmp := nfdv1alpha1.CompareVersions(l, r)
if (m.Op == nfdv1alpha1.MatchLt && cmp == nfdv1alpha1.CmpLt) ||
(m.Op == nfdv1alpha1.MatchGt && cmp == nfdv1alpha1.CmpGt) ||
(m.Op == nfdv1alpha1.MatchLe && (cmp == nfdv1alpha1.CmpLt || cmp == nfdv1alpha1.CmpEq)) ||
(m.Op == nfdv1alpha1.MatchGe && (cmp == nfdv1alpha1.CmpGt || cmp == nfdv1alpha1.CmpEq)) {
return true, nil
}
return false, nil
default:
l, err := strconv.Atoi(value)
if err != nil {
return false, fmt.Errorf("not a number %q", value)
}
r, err := strconv.Atoi(m.Value[0])
if err != nil {
return false, fmt.Errorf("not a number %q in %v", m.Value[0], m)
}
if (l < r && m.Op == nfdv1alpha1.MatchLt) || (l <= r && m.Op == nfdv1alpha1.MatchLe) ||
(l > r && m.Op == nfdv1alpha1.MatchGt) || (l >= r && m.Op == nfdv1alpha1.MatchGe) {
return true, nil
}
}
case nfdv1alpha1.MatchGtLt, nfdv1alpha1.MatchGeLe:
if len(m.Value) != 2 {
return false, fmt.Errorf("invalid expression, value' field must contain exactly two elements for Op %q (have %v)", m.Op, m.Value)
return false, fmt.Errorf("invalid expression, 'value' field must contain exactly two elements for Op %q (have %v)", m.Op, m.Value)
}
v, err := strconv.Atoi(value)
if err != nil {
return false, fmt.Errorf("not a number %q", value)
}
lr := make([]int, 2)
for i := range 2 {
lr[i], err = strconv.Atoi(m.Value[i])
switch m.Type {
case nfdv1alpha1.TypeVersion:
v, err := nfdv1alpha1.ExtractVersion(value)
if err != nil {
return false, fmt.Errorf("not a number %q in %v", m.Value[i], m)
return false, fmt.Errorf("not a version %q", value)
}
lr := make([]string, 2)
for i := range 2 {
lr[i], err = nfdv1alpha1.ExtractVersion(m.Value[i])
if err != nil {
return false, fmt.Errorf("not a version %q in %v", m.Value[i], m)
}
}
lRange := nfdv1alpha1.CompareVersions(v, lr[0])
rRange := nfdv1alpha1.CompareVersions(v, lr[1])
if (m.Op == nfdv1alpha1.MatchGtLt && lRange == nfdv1alpha1.CmpGt && rRange == nfdv1alpha1.CmpLt) ||
(m.Op == nfdv1alpha1.MatchGeLe &&
(lRange == nfdv1alpha1.CmpGt || lRange == nfdv1alpha1.CmpEq) &&
(rRange == nfdv1alpha1.CmpLt || rRange == nfdv1alpha1.CmpEq)) {
return true, nil
}
return false, nil
default:
v, err := strconv.Atoi(value)
if err != nil {
return false, fmt.Errorf("not a number %q", value)
}
lr := make([]int, 2)
for i := range 2 {
lr[i], err = strconv.Atoi(m.Value[i])
if err != nil {
return false, fmt.Errorf("not a number %q in %v", m.Value[i], m)
}
}
if lr[0] >= lr[1] {
return false, fmt.Errorf("invalid expression, value[0] must be less than Value[1] for Op %q (have %v)", m.Op, m.Value)
}
return (v > lr[0] && v < lr[1] && m.Op == nfdv1alpha1.MatchGtLt) ||
(v >= lr[0] && v <= lr[1] && m.Op == nfdv1alpha1.MatchGeLe), nil
}
if lr[0] >= lr[1] {
return false, fmt.Errorf("invalid expression, value[0] must be less than Value[1] for Op %q (have %v)", m.Op, m.Value)
}
return (v > lr[0] && v < lr[1] && m.Op == nfdv1alpha1.MatchGtLt) ||
(v >= lr[0] && v <= lr[1] && m.Op == nfdv1alpha1.MatchGeLe), nil
case nfdv1alpha1.MatchIsTrue:
if len(m.Value) != 0 {
return false, fmt.Errorf("invalid expression, 'value' field must be empty for Op %q (have %v)", m.Op, m.Value)

View file

@ -197,13 +197,14 @@ func TestEvaluateMatchExpressionValues(t *testing.T) {
type I = map[string]string
type TC struct {
name string
op nfdv1alpha1.MatchOp
values V
key string
input I
result BoolAssertionFunc
err ValueAssertionFunc
name string
op nfdv1alpha1.MatchOp
values V
valueType nfdv1alpha1.ValueType
key string
input I
result BoolAssertionFunc
err ValueAssertionFunc
}
tcs := []TC{
@ -231,45 +232,127 @@ func TestEvaluateMatchExpressionValues(t *testing.T) {
{name: "16", op: nfdv1alpha1.MatchGt, values: V{"2"}, key: "foo", input: I{"bar": "3", "foo": "2"}, result: assert.False, err: assert.Nil},
{name: "17", op: nfdv1alpha1.MatchGt, values: V{"2"}, key: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.True, err: assert.Nil},
{name: "18", op: nfdv1alpha1.MatchGt, values: V{"2"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "19", op: nfdv1alpha1.MatchGt, values: V{"2.0"}, key: "foo", input: I{"bar": "str", "foo": "3"}, result: assert.False, err: assert.NotNil},
{name: "20", op: nfdv1alpha1.MatchGt, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "3"}, result: assert.False, err: assert.Nil},
{name: "21", op: nfdv1alpha1.MatchGt, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2"}, result: assert.False, err: assert.Nil},
{name: "22", op: nfdv1alpha1.MatchGt, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.True, err: assert.Nil},
{name: "23", op: nfdv1alpha1.MatchGt, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.0"}, result: assert.False, err: assert.Nil},
{name: "24", op: nfdv1alpha1.MatchGt, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.1"}, result: assert.True, err: assert.Nil},
{name: "25", op: nfdv1alpha1.MatchGt, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.0.1"}, result: assert.False, err: assert.Nil},
{name: "26", op: nfdv1alpha1.MatchGt, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.0.2"}, result: assert.True, err: assert.Nil},
{name: "27", op: nfdv1alpha1.MatchGt, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "19", op: nfdv1alpha1.MatchGe, values: V{"2"}, key: "foo", input: I{"bar": "3"}, result: assert.False, err: assert.Nil},
{name: "20", op: nfdv1alpha1.MatchGe, values: V{"2"}, key: "foo", input: I{"bar": "3", "foo": "2"}, result: assert.True, err: assert.Nil},
{name: "21", op: nfdv1alpha1.MatchGe, values: V{"2"}, key: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.True, err: assert.Nil},
{name: "22", op: nfdv1alpha1.MatchGe, values: V{"2"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "28", op: nfdv1alpha1.MatchGe, values: V{"2"}, key: "foo", input: I{"bar": "3"}, result: assert.False, err: assert.Nil},
{name: "29", op: nfdv1alpha1.MatchGe, values: V{"2"}, key: "foo", input: I{"bar": "3", "foo": "2"}, result: assert.True, err: assert.Nil},
{name: "30", op: nfdv1alpha1.MatchGe, values: V{"2"}, key: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.True, err: assert.Nil},
{name: "31", op: nfdv1alpha1.MatchGe, values: V{"2"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "32", op: nfdv1alpha1.MatchGe, values: V{"2.0"}, key: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.False, err: assert.NotNil},
{name: "33", op: nfdv1alpha1.MatchGe, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "3"}, result: assert.False, err: assert.Nil},
{name: "34", op: nfdv1alpha1.MatchGe, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2"}, result: assert.True, err: assert.Nil},
{name: "35", op: nfdv1alpha1.MatchGe, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "3"}, result: assert.True, err: assert.Nil},
{name: "36", op: nfdv1alpha1.MatchGe, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "1"}, result: assert.False, err: assert.Nil},
{name: "37", op: nfdv1alpha1.MatchGe, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "3"}, result: assert.False, err: assert.Nil},
{name: "38", op: nfdv1alpha1.MatchGe, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.0"}, result: assert.True, err: assert.Nil},
{name: "39", op: nfdv1alpha1.MatchGe, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.1"}, result: assert.True, err: assert.Nil},
{name: "40", op: nfdv1alpha1.MatchGe, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "1.9"}, result: assert.False, err: assert.Nil},
{name: "41", op: nfdv1alpha1.MatchGe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "3"}, result: assert.False, err: assert.Nil},
{name: "42", op: nfdv1alpha1.MatchGe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.0.1"}, result: assert.True, err: assert.Nil},
{name: "43", op: nfdv1alpha1.MatchGe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.0.2"}, result: assert.True, err: assert.Nil},
{name: "44", op: nfdv1alpha1.MatchGe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "3", "foo": "2.0.0"}, result: assert.False, err: assert.Nil},
{name: "45", op: nfdv1alpha1.MatchGe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "23", op: nfdv1alpha1.MatchLt, values: V{"2"}, key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "24", op: nfdv1alpha1.MatchLt, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.False, err: assert.Nil},
{name: "25", op: nfdv1alpha1.MatchLt, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "26", op: nfdv1alpha1.MatchLt, values: V{"2"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "46", op: nfdv1alpha1.MatchLt, values: V{"2"}, key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "47", op: nfdv1alpha1.MatchLt, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.False, err: assert.Nil},
{name: "48", op: nfdv1alpha1.MatchLt, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "49", op: nfdv1alpha1.MatchLt, values: V{"2"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "50", op: nfdv1alpha1.MatchLt, values: V{"2.0"}, key: "foo", input: I{"bar": "str", "foo": "1"}, result: assert.False, err: assert.NotNil},
{name: "51", op: nfdv1alpha1.MatchLt, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "52", op: nfdv1alpha1.MatchLt, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.False, err: assert.Nil},
{name: "53", op: nfdv1alpha1.MatchLt, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "54", op: nfdv1alpha1.MatchLt, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "55", op: nfdv1alpha1.MatchLt, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2.0"}, result: assert.False, err: assert.Nil},
{name: "56", op: nfdv1alpha1.MatchLt, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1.9"}, result: assert.True, err: assert.Nil},
{name: "57", op: nfdv1alpha1.MatchLt, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "58", op: nfdv1alpha1.MatchLt, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2.0.1"}, result: assert.False, err: assert.Nil},
{name: "59", op: nfdv1alpha1.MatchLt, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2.0.0"}, result: assert.True, err: assert.Nil},
{name: "49", op: nfdv1alpha1.MatchLt, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "27", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "28", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.True, err: assert.Nil},
{name: "29", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "30", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "60", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "61", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.True, err: assert.Nil},
{name: "62", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "63", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "64", op: nfdv1alpha1.MatchLe, values: V{"2"}, key: "foo", input: I{"bar": "1", "foo": "1.0"}, result: assert.False, err: assert.NotNil},
{name: "65", op: nfdv1alpha1.MatchLe, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "66", op: nfdv1alpha1.MatchLe, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.True, err: assert.Nil},
{name: "67", op: nfdv1alpha1.MatchLe, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "68", op: nfdv1alpha1.MatchLe, values: V{"2"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "3"}, result: assert.False, err: assert.Nil},
{name: "69", op: nfdv1alpha1.MatchLe, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "1.0"}, result: assert.False, err: assert.Nil},
{name: "70", op: nfdv1alpha1.MatchLe, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2.0"}, result: assert.True, err: assert.Nil},
{name: "71", op: nfdv1alpha1.MatchLe, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1.0"}, result: assert.True, err: assert.Nil},
{name: "72", op: nfdv1alpha1.MatchLe, values: V{"2.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2.1"}, result: assert.False, err: assert.Nil},
{name: "73", op: nfdv1alpha1.MatchLe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1.0"}, result: assert.False, err: assert.Nil},
{name: "74", op: nfdv1alpha1.MatchLe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2.0.1"}, result: assert.True, err: assert.Nil},
{name: "75", op: nfdv1alpha1.MatchLe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2.0.0"}, result: assert.True, err: assert.Nil},
{name: "76", op: nfdv1alpha1.MatchLe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2.0.2"}, result: assert.False, err: assert.Nil},
{name: "77", op: nfdv1alpha1.MatchLe, values: V{"2.0.1"}, valueType: "version", key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "31", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "32", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "11"}, result: assert.False, err: assert.Nil},
{name: "33", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.False, err: assert.Nil},
{name: "34", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "35", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "78", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "79", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "11"}, result: assert.False, err: assert.Nil},
{name: "80", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.False, err: assert.Nil},
{name: "81", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "82", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "83", op: nfdv1alpha1.MatchGtLt, values: V{"-10", "10"}, key: "foo", input: I{"bar": "str", "foo": "5.0"}, result: assert.False, err: assert.NotNil},
{name: "84", op: nfdv1alpha1.MatchGtLt, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "85", op: nfdv1alpha1.MatchGtLt, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "11"}, result: assert.False, err: assert.Nil},
{name: "86", op: nfdv1alpha1.MatchGtLt, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.False, err: assert.Nil},
{name: "87", op: nfdv1alpha1.MatchGtLt, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "2"}, result: assert.True, err: assert.Nil},
{name: "88", op: nfdv1alpha1.MatchGtLt, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "0"}, result: assert.False, err: assert.Nil},
{name: "89", op: nfdv1alpha1.MatchGtLt, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1.1"}, result: assert.False, err: assert.Nil},
{name: "90", op: nfdv1alpha1.MatchGtLt, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "10.1"}, result: assert.False, err: assert.Nil},
{name: "91", op: nfdv1alpha1.MatchGtLt, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1.0"}, result: assert.False, err: assert.Nil},
{name: "92", op: nfdv1alpha1.MatchGtLt, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1.1"}, result: assert.True, err: assert.Nil},
{name: "93", op: nfdv1alpha1.MatchGtLt, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "0.9"}, result: assert.False, err: assert.Nil},
{name: "94", op: nfdv1alpha1.MatchGtLt, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1.1"}, result: assert.False, err: assert.Nil},
{name: "95", op: nfdv1alpha1.MatchGtLt, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "10.0.1"}, result: assert.False, err: assert.Nil},
{name: "96", op: nfdv1alpha1.MatchGtLt, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1.0.1"}, result: assert.False, err: assert.Nil},
{name: "97", op: nfdv1alpha1.MatchGtLt, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1.0.2"}, result: assert.True, err: assert.Nil},
{name: "98", op: nfdv1alpha1.MatchGtLt, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "0.0.9"}, result: assert.False, err: assert.Nil},
{name: "99", op: nfdv1alpha1.MatchGtLt, values: V{"1.0.1", "10.0.1"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "36", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "37", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "-10", "foo": "10"}, result: assert.True, err: assert.Nil},
{name: "38", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.False, err: assert.Nil},
{name: "39", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "40", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "100", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "101", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "-10", "foo": "10"}, result: assert.True, err: assert.Nil},
{name: "102", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.False, err: assert.Nil},
{name: "103", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "104", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "105", op: nfdv1alpha1.MatchGeLe, values: V{"-10", "10"}, key: "foo", input: I{"bar": "1", "foo": "1.0"}, result: assert.False, err: assert.NotNil},
{name: "106", op: nfdv1alpha1.MatchGeLe, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "107", op: nfdv1alpha1.MatchGeLe, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "-10", "foo": "10"}, result: assert.True, err: assert.Nil},
{name: "108", op: nfdv1alpha1.MatchGeLe, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.False, err: assert.Nil},
{name: "109", op: nfdv1alpha1.MatchGeLe, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.True, err: assert.Nil},
{name: "110", op: nfdv1alpha1.MatchGeLe, values: V{"1", "10"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "0"}, result: assert.False, err: assert.Nil},
{name: "111", op: nfdv1alpha1.MatchGeLe, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "112", op: nfdv1alpha1.MatchGeLe, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "-10", "foo": "10.0"}, result: assert.True, err: assert.Nil},
{name: "113", op: nfdv1alpha1.MatchGeLe, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "10.1"}, result: assert.False, err: assert.Nil},
{name: "114", op: nfdv1alpha1.MatchGeLe, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1.1"}, result: assert.True, err: assert.Nil},
{name: "115", op: nfdv1alpha1.MatchGeLe, values: V{"1.0", "10.0"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "0.9"}, result: assert.False, err: assert.Nil},
{name: "116", op: nfdv1alpha1.MatchGeLe, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1"}, result: assert.False, err: assert.Nil},
{name: "117", op: nfdv1alpha1.MatchGeLe, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "-10", "foo": "10.0.1"}, result: assert.True, err: assert.Nil},
{name: "118", op: nfdv1alpha1.MatchGeLe, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "10.0.2"}, result: assert.False, err: assert.Nil},
{name: "119", op: nfdv1alpha1.MatchGeLe, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "1.0.2"}, result: assert.True, err: assert.Nil},
{name: "120", op: nfdv1alpha1.MatchGeLe, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "1", "foo": "0.0.9"}, result: assert.False, err: assert.Nil},
{name: "121", op: nfdv1alpha1.MatchGeLe, values: V{"1.0.1", "10.0.1"}, valueType: "version", key: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.False, err: assert.NotNil},
{name: "41", op: nfdv1alpha1.MatchIsTrue, key: "foo", result: assert.False, err: assert.Nil},
{name: "42", op: nfdv1alpha1.MatchIsTrue, key: "foo", input: I{"foo": "1"}, result: assert.False, err: assert.Nil},
{name: "43", op: nfdv1alpha1.MatchIsTrue, key: "foo", input: I{"foo": "true"}, result: assert.True, err: assert.Nil},
{name: "122", op: nfdv1alpha1.MatchIsTrue, key: "foo", result: assert.False, err: assert.Nil},
{name: "123", op: nfdv1alpha1.MatchIsTrue, key: "foo", input: I{"foo": "1"}, result: assert.False, err: assert.Nil},
{name: "124", op: nfdv1alpha1.MatchIsTrue, key: "foo", input: I{"foo": "true"}, result: assert.True, err: assert.Nil},
{name: "44", op: nfdv1alpha1.MatchIsFalse, key: "foo", input: I{"foo": "true"}, result: assert.False, err: assert.Nil},
{name: "45", op: nfdv1alpha1.MatchIsFalse, key: "foo", input: I{"foo": "false"}, result: assert.True, err: assert.Nil},
{name: "125", op: nfdv1alpha1.MatchIsFalse, key: "foo", input: I{"foo": "true"}, result: assert.False, err: assert.Nil},
{name: "126", op: nfdv1alpha1.MatchIsFalse, key: "foo", input: I{"foo": "false"}, result: assert.True, err: assert.Nil},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
me := &nfdv1alpha1.MatchExpression{Op: tc.op, Value: tc.values}
me := &nfdv1alpha1.MatchExpression{Op: tc.op, Value: tc.values, Type: tc.valueType}
res, err := evaluateMatchExpressionValues(me, tc.key, tc.input)
tc.result(t, res)
tc.err(t, err)