diff --git a/api/nfd/v1alpha1/types.go b/api/nfd/v1alpha1/types.go index 79c0c2891..ee319999c 100644 --- a/api/nfd/v1alpha1/types.go +++ b/api/nfd/v1alpha1/types.go @@ -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 = "" @@ -360,6 +388,9 @@ const ( // MatchIsFalse returns true if the input holds the value "false". The // expression must not have any values. MatchIsFalse MatchOp = "IsFalse" + // MatchVersionRange returns true if the input is version that falls into the + // specified version range. Both the input and value must be semantic versions. + MatchVersionRange MatchOp = "VersionRange" ) const ( diff --git a/api/nfd/v1alpha1/utils.go b/api/nfd/v1alpha1/utils.go index 49c34a426..1fe1ca041 100644 --- a/api/nfd/v1alpha1/utils.go +++ b/api/nfd/v1alpha1/utils.go @@ -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 +} diff --git a/deployment/base/nfd-crds/nfd-api-crds.yaml b/deployment/base/nfd-crds/nfd-api-crds.yaml index 223d2e661..69e306c3b 100644 --- a/deployment/base/nfd-crds/nfd-api-crds.yaml +++ b/deployment/base/nfd-crds/nfd-api-crds.yaml @@ -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 diff --git a/deployment/helm/node-feature-discovery/crds/nfd-api-crds.yaml b/deployment/helm/node-feature-discovery/crds/nfd-api-crds.yaml index 223d2e661..69e306c3b 100644 --- a/deployment/helm/node-feature-discovery/crds/nfd-api-crds.yaml +++ b/deployment/helm/node-feature-discovery/crds/nfd-api-crds.yaml @@ -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 diff --git a/pkg/apis/nfd/nodefeaturerule/expression.go b/pkg/apis/nfd/nodefeaturerule/expression.go index 3bdbfa1bf..0e0ef86ce 100644 --- a/pkg/apis/nfd/nodefeaturerule/expression.go +++ b/pkg/apis/nfd/nodefeaturerule/expression.go @@ -53,6 +53,7 @@ var matchOps = map[nfdv1alpha1.MatchOp]struct{}{ nfdv1alpha1.MatchGeLe: {}, nfdv1alpha1.MatchIsTrue: {}, nfdv1alpha1.MatchIsFalse: {}, + nfdv1alpha1.MatchVersionRange: {}, } // evaluateMatchExpression evaluates the MatchExpression against a single input value. @@ -122,40 +123,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) diff --git a/pkg/apis/nfd/nodefeaturerule/expression_test.go b/pkg/apis/nfd/nodefeaturerule/expression_test.go index 179861d70..c9073d918 100644 --- a/pkg/apis/nfd/nodefeaturerule/expression_test.go +++ b/pkg/apis/nfd/nodefeaturerule/expression_test.go @@ -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)