mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-16 21:38:23 +00:00
source/custom: implement matchAny directive
Implement a new 'matchAny' directive in the new rule format, building on top of the previously implemented 'matchFeatures' matcher. MatchAny applies a logical OR over multiple matchFeatures directives. That is, it allows specifying multiple alternative matchers (at least one of which must match) in a single label rule. The configuration format for the new matchers is matchAny: - matchFeatures: - feature: <domain>.<feature> matchExpressions: <attribute>: op: <operator> value: - <list-of-values> - matchFeatures: ... A configuration example. In order to require a cpu feature, kernel module and one of two specific PCI devices (taking use of the shortform notation): - name: multi-device-test labels: multi-device-feature: "true" matchFeatures: - feature: kernel.loadedmodule matchExpressions: [driver-module] - feature: cpu.cpuid matchExpressions: [AVX512F] matchAny: - matchFeatures: - feature; pci.device matchExpressions: vendor: "8086" device: "1234" - matchFeatures: - feature: pci.device matchExpressions: vendor: "8086" device: "abcd"
This commit is contained in:
parent
e206f0b86b
commit
6cbed379df
4 changed files with 101 additions and 0 deletions
|
@ -160,3 +160,27 @@
|
||||||
# - feature: local.label
|
# - feature: local.label
|
||||||
# matchExpressions:
|
# matchExpressions:
|
||||||
# custom-feature-knob: {op: Gt, value: ["100"]}
|
# custom-feature-knob: {op: Gt, value: ["100"]}
|
||||||
|
#
|
||||||
|
# # The following feature demonstrates the capabilities of the matchAny
|
||||||
|
# - name: "my.ng.feature.2"
|
||||||
|
# labels:
|
||||||
|
# my-ng-feature-2: "my-value"
|
||||||
|
# # matchAny implements a logical IF over all elements (sub-matchers) in
|
||||||
|
# # the list (i.e. at least one feature matcher must match)
|
||||||
|
# matchAny:
|
||||||
|
# - matchFeatures:
|
||||||
|
# - feature: kernel.loadedmodule
|
||||||
|
# matchExpressions:
|
||||||
|
# driver-module-X: {op: Exists}
|
||||||
|
# - feature: pci.device
|
||||||
|
# matchExpressions:
|
||||||
|
# vendor: {op: In, value: ["8086"]}
|
||||||
|
# class: {op: In, value: ["0200"]}
|
||||||
|
# - matchFeatures:
|
||||||
|
# - feature: kernel.loadedmodule
|
||||||
|
# matchExpressions:
|
||||||
|
# driver-module-Y: {op: Exists}
|
||||||
|
# - feature: usb.device
|
||||||
|
# matchExpressions:
|
||||||
|
# vendor: {op: In, value: ["8086"]}
|
||||||
|
# class: {op: In, value: ["02"]}
|
||||||
|
|
|
@ -246,6 +246,30 @@ worker:
|
||||||
# - feature: local.label
|
# - feature: local.label
|
||||||
# matchExpressions:
|
# matchExpressions:
|
||||||
# custom-feature-knob: {op: Gt, value: ["100"]}
|
# custom-feature-knob: {op: Gt, value: ["100"]}
|
||||||
|
#
|
||||||
|
# # The following feature demonstrates the capabilities of the matchAny
|
||||||
|
# - name: "my.ng.feature.2"
|
||||||
|
# labels:
|
||||||
|
# my-ng-feature-2: "my-value"
|
||||||
|
# # matchAny implements a logical IF over all elements (sub-matchers) in
|
||||||
|
# # the list (i.e. at least one feature matcher must match)
|
||||||
|
# matchAny:
|
||||||
|
# - matchFeatures:
|
||||||
|
# - feature: kernel.loadedmodule
|
||||||
|
# matchExpressions:
|
||||||
|
# driver-module-X: {op: Exists}
|
||||||
|
# - feature: pci.device
|
||||||
|
# matchExpressions:
|
||||||
|
# vendor: {op: In, value: ["8086"]}
|
||||||
|
# class: {op: In, value: ["0200"]}
|
||||||
|
# - matchFeatures:
|
||||||
|
# - feature: kernel.loadedmodule
|
||||||
|
# matchExpressions:
|
||||||
|
# driver-module-Y: {op: Exists}
|
||||||
|
# - feature: usb.device
|
||||||
|
# matchExpressions:
|
||||||
|
# vendor: {op: In, value: ["8086"]}
|
||||||
|
# class: {op: In, value: ["02"]}
|
||||||
### <NFD-WORKER-CONF-END-DO-NOT-REMOVE>
|
### <NFD-WORKER-CONF-END-DO-NOT-REMOVE>
|
||||||
|
|
||||||
podSecurityContext: {}
|
podSecurityContext: {}
|
||||||
|
|
|
@ -54,6 +54,11 @@ type Rule struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Labels map[string]string `json:"labels"`
|
Labels map[string]string `json:"labels"`
|
||||||
MatchFeatures FeatureMatcher `json:"matchFeatures"`
|
MatchFeatures FeatureMatcher `json:"matchFeatures"`
|
||||||
|
MatchAny []MatchAnyElem `json:"matchAny"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MatchAnyElem struct {
|
||||||
|
MatchFeatures FeatureMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
type FeatureMatcher []FeatureMatcherTerm
|
type FeatureMatcher []FeatureMatcherTerm
|
||||||
|
@ -185,6 +190,22 @@ func (r *LegacyRule) execute(features map[string]*feature.DomainFeatures) (map[s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Rule) execute(features map[string]*feature.DomainFeatures) (map[string]string, error) {
|
func (r *Rule) execute(features map[string]*feature.DomainFeatures) (map[string]string, error) {
|
||||||
|
if len(r.MatchAny) > 0 {
|
||||||
|
// Logical OR over the matchAny matchers
|
||||||
|
matched := false
|
||||||
|
for _, matcher := range r.MatchAny {
|
||||||
|
if m, err := matcher.match(features); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if m {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(r.MatchFeatures) > 0 {
|
if len(r.MatchFeatures) > 0 {
|
||||||
if m, err := r.MatchFeatures.match(features); err != nil {
|
if m, err := r.MatchFeatures.match(features); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -201,6 +222,10 @@ func (r *Rule) execute(features map[string]*feature.DomainFeatures) (map[string]
|
||||||
return labels, nil
|
return labels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *MatchAnyElem) match(features map[string]*feature.DomainFeatures) (bool, error) {
|
||||||
|
return e.MatchFeatures.match(features)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *FeatureMatcher) match(features map[string]*feature.DomainFeatures) (bool, error) {
|
func (m *FeatureMatcher) match(features map[string]*feature.DomainFeatures) (bool, error) {
|
||||||
// Logical AND over the terms
|
// Logical AND over the terms
|
||||||
for _, term := range *m {
|
for _, term := range *m {
|
||||||
|
|
|
@ -150,4 +150,32 @@ func TestRule(t *testing.T) {
|
||||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||||
assert.Equal(t, r5.Labels, m, "instances should have matched")
|
assert.Equal(t, r5.Labels, m, "instances should have matched")
|
||||||
|
|
||||||
|
// Test MatchAny
|
||||||
|
r5.MatchAny = []MatchAnyElem{
|
||||||
|
MatchAnyElem{
|
||||||
|
MatchFeatures: FeatureMatcher{
|
||||||
|
FeatureMatcherTerm{
|
||||||
|
Feature: "domain-1.kf-1",
|
||||||
|
MatchExpressions: expression.MatchExpressionSet{"key-na": expression.MustCreateMatchExpression(expression.MatchExists)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
m, err = r5.execute(f)
|
||||||
|
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||||
|
assert.Nil(t, m, "instances should not have matched")
|
||||||
|
|
||||||
|
r5.MatchAny = append(r5.MatchAny,
|
||||||
|
MatchAnyElem{
|
||||||
|
MatchFeatures: FeatureMatcher{
|
||||||
|
FeatureMatcherTerm{
|
||||||
|
Feature: "domain-1.kf-1",
|
||||||
|
MatchExpressions: expression.MatchExpressionSet{"key-1": expression.MustCreateMatchExpression(expression.MatchExists)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r5.MatchFeatures[0].MatchExpressions["key-1"] = expression.MustCreateMatchExpression(expression.MatchIn, "val-1")
|
||||||
|
m, err = r5.execute(f)
|
||||||
|
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||||
|
assert.Equal(t, r5.Labels, m, "instances should have matched")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue