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

Merge pull request #1468 from marquiz/devel/nfr-template-fix

apis/nfd: fix multiple matcher terms targeting the same feature
This commit is contained in:
Kubernetes Prow Robot 2023-11-24 13:20:49 +01:00 committed by GitHub
commit 7154458524
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 101 deletions

View file

@ -288,16 +288,15 @@ func (m *MatchExpressionSet) MatchKeys(keys map[string]Nil) (bool, error) {
return matched, err
}
// MatchedKey holds one matched key.
type MatchedKey struct {
Name string
}
// MatchedElement holds one matched Instance.
// +k8s:deepcopy-gen=false
type MatchedElement map[string]string
// MatchGetKeys evaluates the MatchExpressionSet against a set of keys and
// returns all matched keys or nil if no match was found. Note that an empty
// MatchExpressionSet returns a match with an empty slice of matched features.
func (m *MatchExpressionSet) MatchGetKeys(keys map[string]Nil) (bool, []MatchedKey, error) {
ret := make([]MatchedKey, 0, len(*m))
func (m *MatchExpressionSet) MatchGetKeys(keys map[string]Nil) (bool, []MatchedElement, error) {
ret := make([]MatchedElement, 0, len(*m))
for n, e := range *m {
match, err := e.MatchKeys(n, keys)
@ -307,10 +306,8 @@ func (m *MatchExpressionSet) MatchGetKeys(keys map[string]Nil) (bool, []MatchedK
if !match {
return false, nil, nil
}
ret = append(ret, MatchedKey{Name: n})
ret = append(ret, MatchedElement{"Name": n})
}
// Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i].Name < ret[j].Name })
return true, ret, nil
}
@ -320,17 +317,11 @@ func (m *MatchExpressionSet) MatchValues(values map[string]string) (bool, error)
return matched, err
}
// MatchedValue holds one matched key-value pair.
type MatchedValue struct {
Name string
Value string
}
// MatchGetValues evaluates the MatchExpressionSet against a set of key-value
// pairs and returns all matched key-value pairs. Note that an empty
// MatchExpressionSet returns a match with an empty slice of matched features.
func (m *MatchExpressionSet) MatchGetValues(values map[string]string) (bool, []MatchedValue, error) {
ret := make([]MatchedValue, 0, len(*m))
func (m *MatchExpressionSet) MatchGetValues(values map[string]string) (bool, []MatchedElement, error) {
ret := make([]MatchedElement, 0, len(*m))
for n, e := range *m {
match, err := e.MatchValues(n, values)
@ -340,10 +331,8 @@ func (m *MatchExpressionSet) MatchGetValues(values map[string]string) (bool, []M
if !match {
return false, nil, nil
}
ret = append(ret, MatchedValue{Name: n, Value: values[n]})
ret = append(ret, MatchedElement{"Name": n, "Value": values[n]})
}
// Sort for reproducible output
sort.Slice(ret, func(i, j int) bool { return ret[i].Name < ret[j].Name })
return true, ret, nil
}
@ -355,15 +344,12 @@ func (m *MatchExpressionSet) MatchInstances(instances []InstanceFeature) (bool,
return len(v) > 0, err
}
// MatchedInstance holds one matched Instance.
type MatchedInstance map[string]string
// MatchGetInstances evaluates the MatchExpressionSet against a set of instance
// features, each of which is an individual set of key-value pairs
// (attributes). A slice containing all matching instances is returned. An
// empty (non-nil) slice is returned if no matching instances were found.
func (m *MatchExpressionSet) MatchGetInstances(instances []InstanceFeature) ([]MatchedInstance, error) {
ret := []MatchedInstance{}
func (m *MatchExpressionSet) MatchGetInstances(instances []InstanceFeature) ([]MatchedElement, error) {
ret := []MatchedElement{}
for _, i := range instances {
if match, err := m.MatchValues(i.Attributes); err != nil {

View file

@ -290,8 +290,8 @@ func TestMatchValues(t *testing.T) {
func TestMESMatchKeys(t *testing.T) {
type I = map[string]api.Nil
type MK = api.MatchedKey
type O = []MK
type ME = api.MatchedElement
type O = []ME
type TC struct {
mes string
input I
@ -312,7 +312,7 @@ foo: { op: DoesNotExist }
bar: { op: Exists }
`,
input: I{"bar": {}, "baz": {}, "buzz": {}},
output: O{MK{Name: "bar"}, MK{Name: "foo"}},
output: O{ME{"Name": "bar"}, ME{"Name": "foo"}},
result: assert.Truef, err: assert.Nilf},
{mes: `
@ -351,8 +351,8 @@ bar: { op: Exists }
func TestMESMatchValues(t *testing.T) {
type I = map[string]string
type MV = api.MatchedValue
type O = []MV
type ME = api.MatchedElement
type O = []ME
type TC struct {
mes string
input I
@ -382,7 +382,7 @@ bar: { op: In, value: ["val", "wal"] }
baz: { op: Gt, value: ["10"] }
`,
input: I{"foo": "1", "bar": "val", "baz": "123", "buzz": "light"},
output: O{MV{Name: "bar", Value: "val"}, MV{Name: "baz", Value: "123"}, MV{Name: "foo", Value: "1"}},
output: O{ME{"Name": "bar", "Value": "val"}, ME{"Name": "baz", "Value": "123"}, ME{"Name": "foo", "Value": "1"}},
result: assert.Truef, err: assert.Nilf},
{mes: `
@ -413,8 +413,8 @@ baz: { op: Gt, value: ["10"] }
func TestMESMatchInstances(t *testing.T) {
type I = api.InstanceFeature
type MI = api.MatchedInstance
type O = []MI
type ME = api.MatchedElement
type O = []ME
type A = map[string]string
type TC struct {
mes string

View file

@ -155,7 +155,7 @@ func (r *Rule) executeVarsTemplate(in matchedFeatures, out map[string]string) er
type matchedFeatures map[string]domainMatchedFeatures
type domainMatchedFeatures map[string]interface{}
type domainMatchedFeatures map[string][]MatchedElement
func (e *MatchAnyElem) match(features *Features) (bool, matchedFeatures, error) {
return e.MatchFeatures.match(features)
@ -175,30 +175,26 @@ func (m *FeatureMatcher) match(features *Features) (bool, matchedFeatures, error
nameSplit = []string{featureName, ""}
}
if _, ok := matches[nameSplit[0]]; !ok {
matches[nameSplit[0]] = make(domainMatchedFeatures)
dom := nameSplit[0]
nam := nameSplit[1]
if _, ok := matches[dom]; !ok {
matches[dom] = make(domainMatchedFeatures)
}
var isMatch bool
var matchedElems []MatchedElement
var err error
if f, ok := features.Flags[featureName]; ok {
m, v, e := term.MatchExpressions.MatchGetKeys(f.Elements)
isMatch = m
err = e
matches[nameSplit[0]][nameSplit[1]] = v
isMatch, matchedElems, err = term.MatchExpressions.MatchGetKeys(f.Elements)
} else if f, ok := features.Attributes[featureName]; ok {
m, v, e := term.MatchExpressions.MatchGetValues(f.Elements)
isMatch = m
err = e
matches[nameSplit[0]][nameSplit[1]] = v
isMatch, matchedElems, err = term.MatchExpressions.MatchGetValues(f.Elements)
} else if f, ok := features.Instances[featureName]; ok {
v, e := term.MatchExpressions.MatchGetInstances(f.Elements)
isMatch = len(v) > 0
err = e
matches[nameSplit[0]][nameSplit[1]] = v
matchedElems, err = term.MatchExpressions.MatchGetInstances(f.Elements)
isMatch = len(matchedElems) > 0
} else {
return false, nil, fmt.Errorf("feature %q not available", featureName)
}
matches[dom][nam] = append(matches[dom][nam], matchedElems...)
if err != nil {
return false, nil, err

View file

@ -246,6 +246,13 @@ func TestTemplating(t *testing.T) {
"attr-2": "val-200",
},
},
{
Attributes: map[string]string{
"attr-1": "1000",
"attr-2": "val-2000",
"attr-3": "3000",
},
},
},
},
},
@ -290,6 +297,14 @@ var-2=
"attr-1": MustCreateMatchExpression(MatchLt, "100"),
},
},
FeatureMatcherTerm{
Feature: "domain_1.if_1",
MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchExists),
"attr-2": MustCreateMatchExpression(MatchExists),
"attr-3": MustCreateMatchExpression(MatchExists),
},
},
},
}
@ -311,6 +326,7 @@ var-2=
// From if_1 template
"if-1_val-2": "present",
"if-10_val-20": "present",
"if-1000_val-2000": "present",
}
expectedVars := map[string]string{
"var-1": "var-val-1",

View file

@ -276,57 +276,6 @@ func (in MatchValue) DeepCopy() MatchValue {
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in MatchedInstance) DeepCopyInto(out *MatchedInstance) {
{
in := &in
*out = make(MatchedInstance, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchedInstance.
func (in MatchedInstance) DeepCopy() MatchedInstance {
if in == nil {
return nil
}
out := new(MatchedInstance)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MatchedKey) DeepCopyInto(out *MatchedKey) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchedKey.
func (in *MatchedKey) DeepCopy() *MatchedKey {
if in == nil {
return nil
}
out := new(MatchedKey)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MatchedValue) DeepCopyInto(out *MatchedValue) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchedValue.
func (in *MatchedValue) DeepCopy() *MatchedValue {
if in == nil {
return nil
}
out := new(MatchedValue)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Nil) DeepCopyInto(out *Nil) {
*out = *in