1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2024-12-15 17:50:49 +00:00

source/custom: implement 'GtLt' operator

A new operator for checking that an input (integer) is between two
values.
This commit is contained in:
Markus Lehtonen 2021-08-26 15:01:52 +03:00
parent 8b4314bbbb
commit 689703be48
2 changed files with 56 additions and 2 deletions

View file

@ -46,8 +46,8 @@ type MatchExpression struct {
// Value is the list of values that the operand evaluates the input // Value is the list of values that the operand evaluates the input
// against. Value should be empty if the operator is Exists, DoesNotExist, // against. Value should be empty if the operator is Exists, DoesNotExist,
// IsTrue or IsFalse. Value should contain exactly one element if the // IsTrue or IsFalse. Value should contain exactly one element if the
// operator is Gt or Lt. In other cases Value should contain at least one // operator is Gt or Lt and exactly two elements if the operator is GtLt.
// element. // In other cases Value should contain at least one element.
Value MatchValue `json:",omitempty"` Value MatchValue `json:",omitempty"`
// valueRe caches compiled regexps for "InRegexp" operator // valueRe caches compiled regexps for "InRegexp" operator
@ -89,6 +89,11 @@ const (
// Both the input and value must be integer numbers, otherwise an error is // Both the input and value must be integer numbers, otherwise an error is
// returned. // returned.
MatchLt MatchOp = "Lt" MatchLt MatchOp = "Lt"
// MatchGtLt returns true if the input is between two values, i.e. greater
// than the first value and less than the second value of the expression
// (number of values in the expression must be exactly two). Both the input
// and values must be integer numbers, otherwise an error is returned.
MatchGtLt MatchOp = "GtLt"
// MatchIsTrue returns true if the input holds the value "true". The // MatchIsTrue returns true if the input holds the value "true". The
// expression must not have any values. // expression must not have any values.
MatchIsTrue MatchOp = "IsTrue" MatchIsTrue MatchOp = "IsTrue"
@ -106,6 +111,7 @@ var matchOps = map[MatchOp]struct{}{
MatchDoesNotExist: struct{}{}, MatchDoesNotExist: struct{}{},
MatchGt: struct{}{}, MatchGt: struct{}{},
MatchLt: struct{}{}, MatchLt: struct{}{},
MatchGtLt: struct{}{},
MatchIsTrue: struct{}{}, MatchIsTrue: struct{}{},
MatchIsFalse: struct{}{}, MatchIsFalse: struct{}{},
} }
@ -154,6 +160,20 @@ func (m *MatchExpression) Validate() error {
if _, err := strconv.Atoi(m.Value[0]); err != nil { if _, err := strconv.Atoi(m.Value[0]); err != nil {
return fmt.Errorf("Value must be an integer for Op %q (have %v)", m.Op, m.Value[0]) return fmt.Errorf("Value must be an integer for Op %q (have %v)", m.Op, m.Value[0])
} }
case MatchGtLt:
if len(m.Value) != 2 {
return fmt.Errorf("Value must contain exactly two elements for Op %q (have %v)", m.Op, m.Value)
}
var err error
v := make([]int, 2)
for i := 0; i < 2; i++ {
if v[i], err = strconv.Atoi(m.Value[i]); err != nil {
return fmt.Errorf("Value must contain integers for Op %q (have %v)", m.Op, m.Value)
}
}
if v[0] >= v[1] {
return fmt.Errorf("Value[0] must be less than Value[1] for Op %q (have %v)", m.Op, m.Value)
}
case MatchInRegexp: case MatchInRegexp:
if len(m.Value) == 0 { if len(m.Value) == 0 {
return fmt.Errorf("Value must be non-empty for Op %q", m.Op) return fmt.Errorf("Value must be non-empty for Op %q", m.Op)
@ -223,6 +243,19 @@ func (m *MatchExpression) Match(valid bool, value interface{}) (bool, error) {
if (l < r && m.Op == MatchLt) || (l > r && m.Op == MatchGt) { if (l < r && m.Op == MatchLt) || (l > r && m.Op == MatchGt) {
return true, nil return true, nil
} }
case MatchGtLt:
v, err := strconv.Atoi(value)
if err != nil {
return false, fmt.Errorf("not a number %q", value)
}
lr := make([]int, 2)
for i := 0; i < 2; i++ {
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)
}
}
return v > lr[0] && v < lr[1], nil
case MatchIsTrue: case MatchIsTrue:
return value == "true", nil return value == "true", nil
case MatchIsFalse: case MatchIsFalse:

View file

@ -73,6 +73,13 @@ func TestCreateMatchExpression(t *testing.T) {
{op: e.MatchLt, values: V{"1", "2", "3"}, err: assert.NotNilf}, {op: e.MatchLt, values: V{"1", "2", "3"}, err: assert.NotNilf},
{op: e.MatchLt, values: V{"a"}, err: assert.NotNilf}, {op: e.MatchLt, values: V{"a"}, err: assert.NotNilf},
{op: e.MatchGtLt, err: assert.NotNilf},
{op: e.MatchGtLt, values: V{"1"}, err: assert.NotNilf},
{op: e.MatchGtLt, values: V{"1", "2"}, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"2", "1"}, err: assert.NotNilf},
{op: e.MatchGtLt, values: V{"1", "2", "3"}, err: assert.NotNilf},
{op: e.MatchGtLt, values: V{"a", "2"}, err: assert.NotNilf},
{op: e.MatchIsTrue, err: assert.Nilf}, {op: e.MatchIsTrue, err: assert.Nilf},
{op: e.MatchIsTrue, values: V{"1"}, err: assert.NotNilf}, {op: e.MatchIsTrue, values: V{"1"}, err: assert.NotNilf},
@ -134,6 +141,12 @@ func TestMatch(t *testing.T) {
{op: e.MatchLt, values: V{"2"}, input: "1", valid: true, result: assert.Truef, err: assert.Nilf}, {op: e.MatchLt, values: V{"2"}, input: "1", valid: true, result: assert.Truef, err: assert.Nilf},
{op: e.MatchLt, values: V{"2"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf}, {op: e.MatchLt, values: V{"2"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchGtLt, values: V{"1", "10"}, input: "1", valid: false, result: assert.Falsef, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"1", "10"}, input: "1", valid: true, result: assert.Falsef, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"1", "10"}, input: "10", valid: true, result: assert.Falsef, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"1", "10"}, input: "2", valid: true, result: assert.Truef, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"1", "10"}, input: "1.0", valid: true, result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchIsTrue, input: true, valid: false, result: assert.Falsef, err: assert.Nilf}, {op: e.MatchIsTrue, input: true, valid: false, result: assert.Falsef, err: assert.Nilf},
{op: e.MatchIsTrue, input: true, valid: true, result: assert.Truef, err: assert.Nilf}, {op: e.MatchIsTrue, input: true, valid: true, result: assert.Truef, err: assert.Nilf},
{op: e.MatchIsTrue, input: false, valid: true, result: assert.Falsef, err: assert.Nilf}, {op: e.MatchIsTrue, input: false, valid: true, result: assert.Falsef, err: assert.Nilf},
@ -155,6 +168,7 @@ func TestMatch(t *testing.T) {
{op: e.MatchGt, values: V{"3.0"}, input: 1, valid: true}, {op: e.MatchGt, values: V{"3.0"}, input: 1, valid: true},
{op: e.MatchLt, values: V{"0x2"}, input: 1, valid: true}, {op: e.MatchLt, values: V{"0x2"}, input: 1, valid: true},
{op: e.MatchGtLt, values: V{"1", "str"}, input: 1, valid: true},
{op: "non-existent-op", values: V{"1"}, input: 1, valid: true}, {op: "non-existent-op", values: V{"1"}, input: 1, valid: true},
} }
@ -196,6 +210,7 @@ func TestMatchKeys(t *testing.T) {
{op: e.MatchInRegexp, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf}, {op: e.MatchInRegexp, values: V{"foo"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchGt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf}, {op: e.MatchGt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchLt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf}, {op: e.MatchLt, values: V{"1"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchGtLt, values: V{"1", "10"}, name: "foo", result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.NotNilf}, {op: e.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchIsFalse, name: "foo", result: assert.Falsef, err: assert.NotNilf}, {op: e.MatchIsFalse, name: "foo", result: assert.Falsef, err: assert.NotNilf},
} }
@ -252,6 +267,12 @@ func TestMatchValues(t *testing.T) {
{op: e.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf}, {op: e.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf},
{op: e.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf}, {op: e.MatchLt, values: V{"2"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1"}, result: assert.Falsef, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "11"}, result: assert.Falsef, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "-11"}, result: assert.Falsef, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "1", "foo": "1"}, result: assert.Truef, err: assert.Nilf},
{op: e.MatchGtLt, values: V{"-10", "10"}, name: "foo", input: I{"bar": "str", "foo": "str"}, result: assert.Falsef, err: assert.NotNilf},
{op: e.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.Nilf}, {op: e.MatchIsTrue, name: "foo", result: assert.Falsef, err: assert.Nilf},
{op: e.MatchIsTrue, name: "foo", input: I{"foo": "1"}, result: assert.Falsef, err: assert.Nilf}, {op: e.MatchIsTrue, name: "foo", input: I{"foo": "1"}, result: assert.Falsef, err: assert.Nilf},
{op: e.MatchIsTrue, name: "foo", input: I{"foo": "true"}, result: assert.Truef, err: assert.Nilf}, {op: e.MatchIsTrue, name: "foo", input: I{"foo": "true"}, result: assert.Truef, err: assert.Nilf},