Skip to content

Commit 7ae2516

Browse files
authored
Merge pull request #1479 from marquiz/devel/api-internal
source/custom: add internal rule api
2 parents 884edc6 + b28d5c1 commit 7ae2516

File tree

11 files changed

+934
-293
lines changed

11 files changed

+934
-293
lines changed

pkg/apis/nfd/v1alpha1/expression.go

Lines changed: 1 addition & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ limitations under the License.
1717
package v1alpha1
1818

1919
import (
20-
"encoding/json"
2120
"fmt"
2221
"regexp"
2322
"sort"
2423
"strconv"
25-
"strings"
24+
strings "strings"
2625

2726
"golang.org/x/exp/maps"
2827
"k8s.io/klog/v2"
@@ -42,63 +41,6 @@ var matchOps = map[MatchOp]struct{}{
4241
MatchIsFalse: {},
4342
}
4443

45-
// newMatchExpression returns a new MatchExpression instance.
46-
func newMatchExpression(op MatchOp, values ...string) *MatchExpression {
47-
return &MatchExpression{
48-
Op: op,
49-
Value: values,
50-
}
51-
}
52-
53-
// Validate validates the expression.
54-
func (m *MatchExpression) Validate() error {
55-
if _, ok := matchOps[m.Op]; !ok {
56-
return fmt.Errorf("invalid Op %q", m.Op)
57-
}
58-
switch m.Op {
59-
case MatchExists, MatchDoesNotExist, MatchIsTrue, MatchIsFalse, MatchAny:
60-
if len(m.Value) != 0 {
61-
return fmt.Errorf("value must be empty for Op %q (have %v)", m.Op, m.Value)
62-
}
63-
case MatchGt, MatchLt:
64-
if len(m.Value) != 1 {
65-
return fmt.Errorf("value must contain exactly one element for Op %q (have %v)", m.Op, m.Value)
66-
}
67-
if _, err := strconv.Atoi(m.Value[0]); err != nil {
68-
return fmt.Errorf("value must be an integer for Op %q (have %v)", m.Op, m.Value[0])
69-
}
70-
case MatchGtLt:
71-
if len(m.Value) != 2 {
72-
return fmt.Errorf("value must contain exactly two elements for Op %q (have %v)", m.Op, m.Value)
73-
}
74-
var err error
75-
v := make([]int, 2)
76-
for i := 0; i < 2; i++ {
77-
if v[i], err = strconv.Atoi(m.Value[i]); err != nil {
78-
return fmt.Errorf("value must contain integers for Op %q (have %v)", m.Op, m.Value)
79-
}
80-
}
81-
if v[0] >= v[1] {
82-
return fmt.Errorf("value[0] must be less than Value[1] for Op %q (have %v)", m.Op, m.Value)
83-
}
84-
case MatchInRegexp:
85-
if len(m.Value) == 0 {
86-
return fmt.Errorf("value must be non-empty for Op %q", m.Op)
87-
}
88-
for _, v := range m.Value {
89-
_, err := regexp.Compile(v)
90-
if err != nil {
91-
return fmt.Errorf("value must only contain valid regexps for Op %q (have %v)", m.Op, m.Value)
92-
}
93-
}
94-
default:
95-
if len(m.Value) == 0 {
96-
return fmt.Errorf("value must be non-empty for Op %q", m.Op)
97-
}
98-
}
99-
return nil
100-
}
101-
10244
// Match evaluates the MatchExpression against a single input value.
10345
func (m *MatchExpression) Match(valid bool, value interface{}) (bool, error) {
10446
if _, ok := matchOps[m.Op]; !ok {
@@ -340,48 +282,6 @@ func (m *MatchExpression) MatchInstanceAttributeNames(instances []InstanceFeatur
340282
return ret, nil
341283
}
342284

343-
// matchExpression is a helper type for unmarshalling MatchExpression
344-
type matchExpression MatchExpression
345-
346-
// UnmarshalJSON implements the Unmarshaler interface of "encoding/json"
347-
func (m *MatchExpression) UnmarshalJSON(data []byte) error {
348-
raw := new(interface{})
349-
350-
err := json.Unmarshal(data, raw)
351-
if err != nil {
352-
return err
353-
}
354-
355-
switch v := (*raw).(type) {
356-
case string:
357-
*m = *newMatchExpression(MatchIn, v)
358-
case bool:
359-
*m = *newMatchExpression(MatchIn, strconv.FormatBool(v))
360-
case float64:
361-
*m = *newMatchExpression(MatchIn, strconv.FormatFloat(v, 'f', -1, 64))
362-
case []interface{}:
363-
values := make([]string, len(v))
364-
for i, value := range v {
365-
str, ok := value.(string)
366-
if !ok {
367-
return fmt.Errorf("invalid value %v in %v", value, v)
368-
}
369-
values[i] = str
370-
}
371-
*m = *newMatchExpression(MatchIn, values...)
372-
case map[string]interface{}:
373-
helper := &matchExpression{}
374-
if err := json.Unmarshal(data, &helper); err != nil {
375-
return err
376-
}
377-
*m = *newMatchExpression(helper.Op, helper.Value...)
378-
default:
379-
return fmt.Errorf("invalid rule '%v' (%T)", v, v)
380-
}
381-
382-
return m.Validate()
383-
}
384-
385285
// MatchKeys evaluates the MatchExpressionSet against a set of keys.
386286
func (m *MatchExpressionSet) MatchKeys(keys map[string]Nil) (bool, error) {
387287
matched, _, err := m.MatchGetKeys(keys)
@@ -464,83 +364,3 @@ func (m *MatchExpressionSet) MatchGetInstances(instances []InstanceFeature) ([]M
464364
}
465365
return ret, nil
466366
}
467-
468-
// UnmarshalJSON implements the Unmarshaler interface of "encoding/json".
469-
func (m *MatchExpressionSet) UnmarshalJSON(data []byte) error {
470-
*m = MatchExpressionSet{}
471-
472-
names := make([]string, 0)
473-
if err := json.Unmarshal(data, &names); err == nil {
474-
// Simplified slice form
475-
for _, name := range names {
476-
split := strings.SplitN(name, "=", 2)
477-
if len(split) == 1 {
478-
(*m)[split[0]] = newMatchExpression(MatchExists)
479-
} else {
480-
(*m)[split[0]] = newMatchExpression(MatchIn, split[1])
481-
}
482-
}
483-
} else {
484-
// Unmarshal the full map form
485-
expressions := make(map[string]*MatchExpression)
486-
if err := json.Unmarshal(data, &expressions); err != nil {
487-
return err
488-
}
489-
for k, v := range expressions {
490-
if v != nil {
491-
(*m)[k] = v
492-
} else {
493-
(*m)[k] = newMatchExpression(MatchExists)
494-
}
495-
}
496-
}
497-
498-
return nil
499-
}
500-
501-
// UnmarshalJSON implements the Unmarshaler interface of "encoding/json".
502-
func (m *MatchOp) UnmarshalJSON(data []byte) error {
503-
var raw string
504-
505-
if err := json.Unmarshal(data, &raw); err != nil {
506-
return err
507-
}
508-
509-
if _, ok := matchOps[MatchOp(raw)]; !ok {
510-
return fmt.Errorf("invalid Op %q", raw)
511-
}
512-
*m = MatchOp(raw)
513-
return nil
514-
}
515-
516-
// UnmarshalJSON implements the Unmarshaler interface of "encoding/json".
517-
func (m *MatchValue) UnmarshalJSON(data []byte) error {
518-
var raw interface{}
519-
520-
if err := json.Unmarshal(data, &raw); err != nil {
521-
return err
522-
}
523-
524-
switch v := raw.(type) {
525-
case string:
526-
*m = []string{v}
527-
case bool:
528-
*m = []string{strconv.FormatBool(v)}
529-
case float64:
530-
*m = []string{strconv.FormatFloat(v, 'f', -1, 64)}
531-
case []interface{}:
532-
values := make([]string, len(v))
533-
for i, value := range v {
534-
str, ok := value.(string)
535-
if !ok {
536-
return fmt.Errorf("invalid value %v in %v", value, v)
537-
}
538-
values[i] = str
539-
}
540-
*m = values
541-
default:
542-
return fmt.Errorf("invalid values '%v' (%T)", v, v)
543-
}
544-
545-
return nil
546-
}

pkg/apis/nfd/v1alpha1/expression_test.go

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -29,73 +29,6 @@ type BoolAssertionFunc func(assert.TestingT, bool, ...interface{}) bool
2929

3030
type ValueAssertionFunc func(assert.TestingT, interface{}, ...interface{}) bool
3131

32-
func TestMatchExpressionValidate(t *testing.T) {
33-
type V = api.MatchValue
34-
type TC struct {
35-
name string
36-
op api.MatchOp
37-
values V
38-
err ValueAssertionFunc
39-
}
40-
41-
tcs := []TC{
42-
{name: "1", op: api.MatchAny, err: assert.Nil}, // #0
43-
{name: "2", op: api.MatchAny, values: V{"1"}, err: assert.NotNil},
44-
45-
{name: "3", op: api.MatchIn, err: assert.NotNil},
46-
{name: "4", op: api.MatchIn, values: V{"1"}, err: assert.Nil},
47-
{name: "5", op: api.MatchIn, values: V{"1", "2", "3", "4"}, err: assert.Nil},
48-
49-
{name: "6", op: api.MatchNotIn, err: assert.NotNil},
50-
{name: "7", op: api.MatchNotIn, values: V{"1"}, err: assert.Nil},
51-
{name: "8", op: api.MatchNotIn, values: V{"1", "2"}, err: assert.Nil},
52-
53-
{name: "9", op: api.MatchInRegexp, err: assert.NotNil},
54-
{name: "10", op: api.MatchInRegexp, values: V{"1"}, err: assert.Nil},
55-
{name: "11", op: api.MatchInRegexp, values: V{"()", "2", "3"}, err: assert.Nil},
56-
{name: "12", op: api.MatchInRegexp, values: V{"("}, err: assert.NotNil},
57-
58-
{name: "13", op: api.MatchExists, err: assert.Nil},
59-
{name: "14", op: api.MatchExists, values: V{"1"}, err: assert.NotNil},
60-
61-
{name: "15", op: api.MatchDoesNotExist, err: assert.Nil},
62-
{name: "16", op: api.MatchDoesNotExist, values: V{"1"}, err: assert.NotNil},
63-
64-
{name: "17", op: api.MatchGt, err: assert.NotNil},
65-
{name: "18", op: api.MatchGt, values: V{"1"}, err: assert.Nil},
66-
{name: "19", op: api.MatchGt, values: V{"-10"}, err: assert.Nil},
67-
{name: "20", op: api.MatchGt, values: V{"1", "2"}, err: assert.NotNil},
68-
{name: "21", op: api.MatchGt, values: V{""}, err: assert.NotNil},
69-
70-
{name: "22", op: api.MatchLt, err: assert.NotNil},
71-
{name: "23", op: api.MatchLt, values: V{"1"}, err: assert.Nil},
72-
{name: "24", op: api.MatchLt, values: V{"-1"}, err: assert.Nil},
73-
{name: "25", op: api.MatchLt, values: V{"1", "2", "3"}, err: assert.NotNil},
74-
{name: "26", op: api.MatchLt, values: V{"a"}, err: assert.NotNil},
75-
76-
{name: "27", op: api.MatchGtLt, err: assert.NotNil},
77-
{name: "28", op: api.MatchGtLt, values: V{"1"}, err: assert.NotNil},
78-
{name: "29", op: api.MatchGtLt, values: V{"1", "2"}, err: assert.Nil},
79-
{name: "30", op: api.MatchGtLt, values: V{"2", "1"}, err: assert.NotNil},
80-
{name: "31", op: api.MatchGtLt, values: V{"1", "2", "3"}, err: assert.NotNil},
81-
{name: "32", op: api.MatchGtLt, values: V{"a", "2"}, err: assert.NotNil},
82-
83-
{name: "33", op: api.MatchIsTrue, err: assert.Nil},
84-
{name: "34", op: api.MatchIsTrue, values: V{"1"}, err: assert.NotNil},
85-
86-
{name: "35", op: api.MatchIsFalse, err: assert.Nil},
87-
{name: "36", op: api.MatchIsFalse, values: V{"1", "2"}, err: assert.NotNil},
88-
}
89-
90-
for _, tc := range tcs {
91-
t.Run(tc.name, func(t *testing.T) {
92-
me := api.MatchExpression{Op: tc.op, Value: tc.values}
93-
err := me.Validate()
94-
tc.err(t, err)
95-
})
96-
}
97-
}
98-
9932
func TestMatch(t *testing.T) {
10033
type V = api.MatchValue
10134
type TC struct {

pkg/apis/nfd/v1alpha1/rule_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ import (
2222
"github.com/stretchr/testify/assert"
2323
)
2424

25+
// newMatchExpression returns a new MatchExpression instance.
26+
func newMatchExpression(op MatchOp, values ...string) *MatchExpression {
27+
return &MatchExpression{
28+
Op: op,
29+
Value: values,
30+
}
31+
}
32+
2533
func TestRule(t *testing.T) {
2634
f := &Features{}
2735
r1 := Rule{Labels: map[string]string{"label-1": "", "label-2": "true"}}

0 commit comments

Comments
 (0)