Skip to content

Commit

Permalink
source/custom: implement matchAny directive
Browse files Browse the repository at this point in the history
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"
  • Loading branch information
marquiz committed Nov 11, 2021
1 parent dce4e3d commit e545848
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
24 changes: 24 additions & 0 deletions deployment/components/worker-config/nfd-worker.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,27 @@
# - feature: local.label
# matchExpressions:
# 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"]}
24 changes: 24 additions & 0 deletions deployment/helm/node-feature-discovery/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,30 @@ worker:
# - feature: local.label
# matchExpressions:
# 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>

podSecurityContext: {}
Expand Down
25 changes: 25 additions & 0 deletions source/custom/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ type Rule struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`
MatchFeatures FeatureMatcher `json:"matchFeatures"`
MatchAny []MatchAnyElem `json:"matchAny"`
}

type MatchAnyElem struct {
MatchFeatures FeatureMatcher
}

type FeatureMatcher []FeatureMatcherTerm
Expand Down Expand Up @@ -187,6 +192,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) {
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 m, err := r.MatchFeatures.match(features); err != nil {
return nil, err
Expand All @@ -203,6 +224,10 @@ func (r *Rule) execute(features map[string]*feature.DomainFeatures) (map[string]
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) {
// Logical AND over the terms
for _, term := range *m {
Expand Down
28 changes: 28 additions & 0 deletions source/custom/custom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,32 @@ func TestRule(t *testing.T) {
assert.Nilf(t, err, "unexpected error: %v", err)
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")
}

0 comments on commit e545848

Please sign in to comment.