mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-04-23 20:57:10 +00:00
Merge pull request #788 from marquiz/devel/wildcard-rule-ng
apis/nfd: add matchName field in feature matcher terms
This commit is contained in:
commit
119e2a374b
25 changed files with 901 additions and 66 deletions
deployment
base/nfd-crds
components/worker-config
helm/node-feature-discovery
docs/usage
pkg/apis/nfd/v1alpha1
samples
nodefeaturerule-cpu.yamlnodefeaturerule-custom.yamlnodefeaturerule-kernel.yamlnodefeaturerule-local.yamlnodefeaturerule-memory.yamlnodefeaturerule-network.yamlnodefeaturerule-pci.yamlnodefeaturerule-storage.yamlnodefeaturerule-system.yamlnodefeaturerule-usb.yaml
source/custom
test/e2e
|
@ -94,6 +94,13 @@ spec:
|
|||
vendor: {op: In, value: ["8086"]}
|
||||
class: {op: In, value: ["02"]}
|
||||
|
||||
- name: "avx wildcard rule"
|
||||
labels:
|
||||
"my-avx-feature": "true"
|
||||
matchFeatures:
|
||||
- feature: cpu.cpuid
|
||||
matchName: {op: InRegexp, value: ["^AVX512"]}
|
||||
|
||||
# The following features demonstreate label templating capabilities
|
||||
- name: "my system template feature"
|
||||
labelsTemplate: |
|
||||
|
@ -143,3 +150,11 @@ spec:
|
|||
matchExpressions:
|
||||
my.kernel.feature: {op: IsTrue}
|
||||
my.dummy.var: {op: Gt, value: ["0"]}
|
||||
|
||||
- name: "kconfig template rule"
|
||||
labelsTemplate: |
|
||||
{{ range .kernel.config }}kconfig-{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: kernel.config
|
||||
matchName: {op: In, value: ["SWAP", "X86", "ARM"]}
|
||||
|
|
|
@ -190,6 +190,8 @@ spec:
|
|||
in the feature set.
|
||||
properties:
|
||||
feature:
|
||||
description: Feature is the name of the feature
|
||||
set to match against.
|
||||
type: string
|
||||
matchExpressions:
|
||||
additionalProperties:
|
||||
|
@ -229,13 +231,46 @@ spec:
|
|||
required:
|
||||
- op
|
||||
type: object
|
||||
description: MatchExpressionSet contains a set of
|
||||
MatchExpressions, each of which is evaluated against
|
||||
a set of input values.
|
||||
description: MatchExpressions is the set of per-element
|
||||
expressions evaluated. These match against the
|
||||
value of the specified elements.
|
||||
type: object
|
||||
matchName:
|
||||
description: MatchName in an expression that is
|
||||
matched against the name of each element in the
|
||||
feature set.
|
||||
properties:
|
||||
op:
|
||||
description: Op is the operator to be applied.
|
||||
enum:
|
||||
- In
|
||||
- NotIn
|
||||
- InRegexp
|
||||
- Exists
|
||||
- DoesNotExist
|
||||
- Gt
|
||||
- Lt
|
||||
- GtLt
|
||||
- IsTrue
|
||||
- IsFalse
|
||||
type: string
|
||||
value:
|
||||
description: Value is the list of values that
|
||||
the operand evaluates the input against. Value
|
||||
should be empty if the operator is Exists,
|
||||
DoesNotExist, IsTrue or IsFalse. Value should
|
||||
contain exactly one element if the operator
|
||||
is Gt or Lt and exactly two elements if the
|
||||
operator is GtLt. In other cases Value should
|
||||
contain at least one element.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- op
|
||||
type: object
|
||||
required:
|
||||
- feature
|
||||
- matchExpressions
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
|
@ -251,6 +286,8 @@ spec:
|
|||
are evaluated against each element in the feature set.
|
||||
properties:
|
||||
feature:
|
||||
description: Feature is the name of the feature set to
|
||||
match against.
|
||||
type: string
|
||||
matchExpressions:
|
||||
additionalProperties:
|
||||
|
@ -288,12 +325,44 @@ spec:
|
|||
required:
|
||||
- op
|
||||
type: object
|
||||
description: MatchExpressionSet contains a set of MatchExpressions,
|
||||
each of which is evaluated against a set of input values.
|
||||
description: MatchExpressions is the set of per-element
|
||||
expressions evaluated. These match against the value
|
||||
of the specified elements.
|
||||
type: object
|
||||
matchName:
|
||||
description: MatchName in an expression that is matched
|
||||
against the name of each element in the feature set.
|
||||
properties:
|
||||
op:
|
||||
description: Op is the operator to be applied.
|
||||
enum:
|
||||
- In
|
||||
- NotIn
|
||||
- InRegexp
|
||||
- Exists
|
||||
- DoesNotExist
|
||||
- Gt
|
||||
- Lt
|
||||
- GtLt
|
||||
- IsTrue
|
||||
- IsFalse
|
||||
type: string
|
||||
value:
|
||||
description: Value is the list of values that the
|
||||
operand evaluates the input against. Value should
|
||||
be empty if the operator is Exists, DoesNotExist,
|
||||
IsTrue or IsFalse. Value should contain exactly
|
||||
one element if the operator is Gt or Lt and exactly
|
||||
two elements if the operator is GtLt. In other cases
|
||||
Value should contain at least one element.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- op
|
||||
type: object
|
||||
required:
|
||||
- feature
|
||||
- matchExpressions
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
|
|
|
@ -174,6 +174,13 @@
|
|||
# vendor: {op: In, value: ["8086"]}
|
||||
# class: {op: In, value: ["02"]}
|
||||
#
|
||||
# - name: "avx wildcard rule"
|
||||
# labels:
|
||||
# "my-avx-feature": "true"
|
||||
# matchFeatures:
|
||||
# - feature: cpu.cpuid
|
||||
# matchName: {op: InRegexp, value: ["^AVX512"]}
|
||||
#
|
||||
# # The following features demonstreate label templating capabilities
|
||||
# - name: "my template rule"
|
||||
# labelsTemplate: |
|
||||
|
@ -224,3 +231,10 @@
|
|||
# vendor.io/my.kernel.feature: {op: IsTrue}
|
||||
# my.dummy.var: {op: Gt, value: ["0"]}
|
||||
#
|
||||
# - name: "kconfig template rule"
|
||||
# labelsTemplate: |
|
||||
# {{ range .kernel.config }}kconfig-{{ .Name }}={{ .Value }}
|
||||
# {{ end }}
|
||||
# matchFeatures:
|
||||
# - feature: kernel.config
|
||||
# matchName: {op: In, value: ["SWAP", "X86", "ARM"]}
|
||||
|
|
|
@ -190,6 +190,8 @@ spec:
|
|||
in the feature set.
|
||||
properties:
|
||||
feature:
|
||||
description: Feature is the name of the feature
|
||||
set to match against.
|
||||
type: string
|
||||
matchExpressions:
|
||||
additionalProperties:
|
||||
|
@ -229,13 +231,46 @@ spec:
|
|||
required:
|
||||
- op
|
||||
type: object
|
||||
description: MatchExpressionSet contains a set of
|
||||
MatchExpressions, each of which is evaluated against
|
||||
a set of input values.
|
||||
description: MatchExpressions is the set of per-element
|
||||
expressions evaluated. These match against the
|
||||
value of the specified elements.
|
||||
type: object
|
||||
matchName:
|
||||
description: MatchName in an expression that is
|
||||
matched against the name of each element in the
|
||||
feature set.
|
||||
properties:
|
||||
op:
|
||||
description: Op is the operator to be applied.
|
||||
enum:
|
||||
- In
|
||||
- NotIn
|
||||
- InRegexp
|
||||
- Exists
|
||||
- DoesNotExist
|
||||
- Gt
|
||||
- Lt
|
||||
- GtLt
|
||||
- IsTrue
|
||||
- IsFalse
|
||||
type: string
|
||||
value:
|
||||
description: Value is the list of values that
|
||||
the operand evaluates the input against. Value
|
||||
should be empty if the operator is Exists,
|
||||
DoesNotExist, IsTrue or IsFalse. Value should
|
||||
contain exactly one element if the operator
|
||||
is Gt or Lt and exactly two elements if the
|
||||
operator is GtLt. In other cases Value should
|
||||
contain at least one element.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- op
|
||||
type: object
|
||||
required:
|
||||
- feature
|
||||
- matchExpressions
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
|
@ -251,6 +286,8 @@ spec:
|
|||
are evaluated against each element in the feature set.
|
||||
properties:
|
||||
feature:
|
||||
description: Feature is the name of the feature set to
|
||||
match against.
|
||||
type: string
|
||||
matchExpressions:
|
||||
additionalProperties:
|
||||
|
@ -288,12 +325,44 @@ spec:
|
|||
required:
|
||||
- op
|
||||
type: object
|
||||
description: MatchExpressionSet contains a set of MatchExpressions,
|
||||
each of which is evaluated against a set of input values.
|
||||
description: MatchExpressions is the set of per-element
|
||||
expressions evaluated. These match against the value
|
||||
of the specified elements.
|
||||
type: object
|
||||
matchName:
|
||||
description: MatchName in an expression that is matched
|
||||
against the name of each element in the feature set.
|
||||
properties:
|
||||
op:
|
||||
description: Op is the operator to be applied.
|
||||
enum:
|
||||
- In
|
||||
- NotIn
|
||||
- InRegexp
|
||||
- Exists
|
||||
- DoesNotExist
|
||||
- Gt
|
||||
- Lt
|
||||
- GtLt
|
||||
- IsTrue
|
||||
- IsFalse
|
||||
type: string
|
||||
value:
|
||||
description: Value is the list of values that the
|
||||
operand evaluates the input against. Value should
|
||||
be empty if the operator is Exists, DoesNotExist,
|
||||
IsTrue or IsFalse. Value should contain exactly
|
||||
one element if the operator is Gt or Lt and exactly
|
||||
two elements if the operator is GtLt. In other cases
|
||||
Value should contain at least one element.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- op
|
||||
type: object
|
||||
required:
|
||||
- feature
|
||||
- matchExpressions
|
||||
type: object
|
||||
type: array
|
||||
name:
|
||||
|
|
|
@ -312,6 +312,13 @@ worker:
|
|||
# vendor: {op: In, value: ["8086"]}
|
||||
# class: {op: In, value: ["02"]}
|
||||
#
|
||||
# - name: "avx wildcard rule"
|
||||
# labels:
|
||||
# "my-avx-feature": "true"
|
||||
# matchFeatures:
|
||||
# - feature: cpu.cpuid
|
||||
# matchName: {op: InRegexp, value: ["^AVX512"]}
|
||||
#
|
||||
# # The following features demonstreate label templating capabilities
|
||||
# - name: "my template rule"
|
||||
# labelsTemplate: |
|
||||
|
@ -362,6 +369,13 @@ worker:
|
|||
# vendor.io/my.kernel.feature: {op: IsTrue}
|
||||
# my.dummy.var: {op: Gt, value: ["0"]}
|
||||
#
|
||||
# - name: "kconfig template rule"
|
||||
# labelsTemplate: |
|
||||
# {{ range .kernel.config }}kconfig-{{ .Name }}={{ .Value }}
|
||||
# {{ end }}
|
||||
# matchFeatures:
|
||||
# - feature: kernel.config
|
||||
# matchName: {op: In, value: ["SWAP", "X86", "ARM"]}
|
||||
### <NFD-WORKER-CONF-END-DO-NOT-REMOVE>
|
||||
|
||||
metricsPort: 8081
|
||||
|
|
|
@ -761,16 +761,46 @@ of them must match for the rule to trigger.
|
|||
value:
|
||||
- <value-1>
|
||||
- ...
|
||||
matchName:
|
||||
op: <op>
|
||||
value:
|
||||
- <value-1>
|
||||
- ...
|
||||
```
|
||||
|
||||
The `.matchFeatures[].feature` field specifies the feature against which to
|
||||
match.
|
||||
The `.matchFeatures[].feature` field specifies the feature which to evaluate.
|
||||
|
||||
The `.matchFeatures[].matchExpressions` field specifies a map of expressions
|
||||
which to evaluate against the elements of the feature.
|
||||
> **NOTE:**If both [`matchExpressions`](#matchexpressions) and
|
||||
> [`matchName`](#matchname) are specified, they both must match.
|
||||
|
||||
In each MatchExpression `op` specifies the operator to apply. Valid values are
|
||||
described below.
|
||||
##### matchExpressions
|
||||
|
||||
The `.matchFeatures[].matchExpressions` field is used to match against the
|
||||
value(s) of a feature. The `matchExpressions` field consists of a set of
|
||||
expressions, each of which is evaluated against all elements of the specified
|
||||
feature.
|
||||
|
||||
```yaml
|
||||
matchExpressions:
|
||||
<key>:
|
||||
op: <op>
|
||||
value:
|
||||
- <value-1>
|
||||
- ...
|
||||
```
|
||||
|
||||
In each MatchExpression the `key` specifies the name of of the feature element
|
||||
(*flag* and *attribute* features) or name of the attribute (*instance*
|
||||
features) which to look for. The behavior of MatchExpression depends on the
|
||||
[feature type](#feature-types):
|
||||
|
||||
- for *flag* and *attribute* features the MatchExpression operates on the
|
||||
feature element whose name matches the `<key>`
|
||||
- for *instance* features all MatchExpressions are evaluated against the
|
||||
attributes of each instance separately
|
||||
|
||||
The `op` field specifies the operator to apply. Valid values are described
|
||||
below.
|
||||
|
||||
| Operator | Number of values | Matches when |
|
||||
| --------------- | ---------------- | ----------- |
|
||||
|
@ -788,11 +818,57 @@ described below.
|
|||
The `value` field of MatchExpression is a list of string arguments to the
|
||||
operator.
|
||||
|
||||
The behavior of MatchExpression depends on the [feature type](#feature-types):
|
||||
for *flag* and *attribute* features the MatchExpression operates on the feature
|
||||
element whose name matches the `<key>`. However, for *instance* features all
|
||||
MatchExpressions are evaluated against the attributes of each instance
|
||||
separately.
|
||||
##### matchName
|
||||
|
||||
The `.matchFeatures[].matchName` field is used to match against the
|
||||
name(s) of a feature (whereas the [`matchExpressions`](#matchexpressions) field
|
||||
matches against the value(s). The `matchName` field consists of a single
|
||||
expression which is evaulated against the name of each element of the specified
|
||||
feature.
|
||||
|
||||
```yaml
|
||||
matchName:
|
||||
op: <op>
|
||||
value:
|
||||
- <value-1>
|
||||
- ...
|
||||
```
|
||||
|
||||
The behavior of `matchName` depends on the [feature type](#feature-types):
|
||||
|
||||
- for *flag* and *attribute* features the expression is evaluated against the
|
||||
name of each element
|
||||
- for *instance* features the expression is evaluated against the name of
|
||||
each attribute, for each element (instance) separately (matches if the
|
||||
attributes of any of the elements satisfy the expression)
|
||||
|
||||
The `op` field specifies the operator to apply. Same operators as for
|
||||
[`matchExpressions`](#matchexpressions) above are available.
|
||||
|
||||
| Operator | Number of values | Matches |
|
||||
| --------------- | ---------------- | ----------- |
|
||||
| `In` | 1 or greater | All name is equal to one of the values |
|
||||
| `NotIn` | 1 or greater | All name that is not equal to any of the values |
|
||||
| `InRegexp` | 1 or greater | All name that matches any of the values (treated as regexps) |
|
||||
| `Exists` | 0 | All elements |
|
||||
|
||||
Other operators are not practical with `matchName` (`DoesNotExist` never
|
||||
matches; `Gt`,`Lt` and `GtLt` are only usable if feature names are integers;
|
||||
`IsTrue` and `IsFalse` are only usable if the feature name is `true` or
|
||||
`false`).
|
||||
|
||||
The `value` field is a list of string arguments to the operator.
|
||||
|
||||
An example:
|
||||
|
||||
```yaml
|
||||
matchFeatures:
|
||||
- feature: cpu.cpuid
|
||||
matchName: {op: InRegexp, value: ["^AVX"]}
|
||||
```
|
||||
|
||||
The snippet above would match if any CPUID feature starting with AVX is present
|
||||
(e.g. AVX1 or AVX2 or AVX512F etc).
|
||||
|
||||
#### matchAny
|
||||
|
||||
|
@ -992,6 +1068,10 @@ feature:
|
|||
```
|
||||
|
||||
<!-- {% endraw %} -->
|
||||
> **NOTE:**If both `matchExpressions` and `matchName` for a feature matcher
|
||||
> term (see [`matchFeatures`](#matchfeatures)) is specified, the list of
|
||||
> matched features (for the template engine) is the union from both of these.
|
||||
<!-- note #2 -->
|
||||
> **NOTE:** In case of matchAny is specified, the template is executed
|
||||
> separately against each individual `matchFeatures` field and the final set of
|
||||
> labels will be superset of all these separate template expansions. E.g.
|
||||
|
|
|
@ -258,6 +258,88 @@ func (m *MatchExpression) MatchValues(name string, values map[string]string) (bo
|
|||
return matched, nil
|
||||
}
|
||||
|
||||
// MatchKeyNames evaluates the MatchExpression against names of a set of key features.
|
||||
func (m *MatchExpression) MatchKeyNames(keys map[string]Nil) (bool, []MatchedElement, error) {
|
||||
ret := []MatchedElement{}
|
||||
|
||||
for k := range keys {
|
||||
if match, err := m.Match(true, k); err != nil {
|
||||
return false, nil, err
|
||||
} else if match {
|
||||
ret = append(ret, MatchedElement{"Name": k})
|
||||
}
|
||||
}
|
||||
// Sort for reproducible output
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] })
|
||||
|
||||
if klogV3 := klog.V(3); klogV3.Enabled() {
|
||||
mk := make([]string, len(ret))
|
||||
for i, v := range ret {
|
||||
mk[i] = v["Name"]
|
||||
}
|
||||
mkMsg := strings.Join(mk, ", ")
|
||||
|
||||
if klogV4 := klog.V(4); klogV4.Enabled() {
|
||||
k := make([]string, 0, len(keys))
|
||||
for n := range keys {
|
||||
k = append(k, n)
|
||||
}
|
||||
sort.Strings(k)
|
||||
klogV3.InfoS("matched (key) names", "matchResult", mkMsg, "matchOp", m.Op, "matchValue", m.Value, "inputKeys", k)
|
||||
} else {
|
||||
klogV3.InfoS("matched (key) names", "matchResult", mkMsg, "matchOp", m.Op, "matchValue", m.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return len(ret) > 0, ret, nil
|
||||
}
|
||||
|
||||
// MatchValueNames evaluates the MatchExpression against names of a set of value features.
|
||||
func (m *MatchExpression) MatchValueNames(values map[string]string) (bool, []MatchedElement, error) {
|
||||
ret := []MatchedElement{}
|
||||
|
||||
for k, v := range values {
|
||||
if match, err := m.Match(true, k); err != nil {
|
||||
return false, nil, err
|
||||
} else if match {
|
||||
ret = append(ret, MatchedElement{"Name": k, "Value": v})
|
||||
}
|
||||
}
|
||||
// Sort for reproducible output
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i]["Name"] < ret[j]["Name"] })
|
||||
|
||||
if klogV3 := klog.V(3); klogV3.Enabled() {
|
||||
mk := make([]string, len(ret))
|
||||
for i, v := range ret {
|
||||
mk[i] = v["Name"]
|
||||
}
|
||||
mkMsg := strings.Join(mk, ", ")
|
||||
|
||||
if klogV4 := klog.V(4); klogV4.Enabled() {
|
||||
klogV3.InfoS("matched (value) names", "matchResult", mkMsg, "matchOp", m.Op, "matchValue", m.Value, "inputValues", values)
|
||||
} else {
|
||||
klogV3.InfoS("matched (value) names", "matchResult", mkMsg, "matchOp", m.Op, "matchValue", m.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return len(ret) > 0, ret, nil
|
||||
}
|
||||
|
||||
// MatchInstanceAttributeNames evaluates the MatchExpression against a set of
|
||||
// instance features, matching against the names of their attributes.
|
||||
func (m *MatchExpression) MatchInstanceAttributeNames(instances []InstanceFeature) ([]MatchedElement, error) {
|
||||
ret := []MatchedElement{}
|
||||
|
||||
for _, i := range instances {
|
||||
if match, _, err := m.MatchValueNames(i.Attributes); err != nil {
|
||||
return nil, err
|
||||
} else if match {
|
||||
ret = append(ret, i.Attributes)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// matchExpression is a helper type for unmarshalling MatchExpression
|
||||
type matchExpression MatchExpression
|
||||
|
||||
|
|
|
@ -180,16 +180,39 @@ func (m *FeatureMatcher) match(features *Features) (bool, matchedFeatures, error
|
|||
matches[dom] = make(domainMatchedFeatures)
|
||||
}
|
||||
|
||||
var isMatch bool
|
||||
var isMatch = true
|
||||
var matchedElems []MatchedElement
|
||||
var err error
|
||||
if f, ok := features.Flags[featureName]; ok {
|
||||
isMatch, matchedElems, err = term.MatchExpressions.MatchGetKeys(f.Elements)
|
||||
if term.MatchExpressions != nil {
|
||||
isMatch, matchedElems, err = term.MatchExpressions.MatchGetKeys(f.Elements)
|
||||
}
|
||||
var meTmp []MatchedElement
|
||||
if err == nil && isMatch && term.MatchName != nil {
|
||||
isMatch, meTmp, err = term.MatchName.MatchKeyNames(f.Elements)
|
||||
matchedElems = append(matchedElems, meTmp...)
|
||||
}
|
||||
} else if f, ok := features.Attributes[featureName]; ok {
|
||||
isMatch, matchedElems, err = term.MatchExpressions.MatchGetValues(f.Elements)
|
||||
if term.MatchExpressions != nil {
|
||||
isMatch, matchedElems, err = term.MatchExpressions.MatchGetValues(f.Elements)
|
||||
}
|
||||
var meTmp []MatchedElement
|
||||
if err == nil && isMatch && term.MatchName != nil {
|
||||
isMatch, meTmp, err = term.MatchName.MatchValueNames(f.Elements)
|
||||
matchedElems = append(matchedElems, meTmp...)
|
||||
}
|
||||
} else if f, ok := features.Instances[featureName]; ok {
|
||||
matchedElems, err = term.MatchExpressions.MatchGetInstances(f.Elements)
|
||||
isMatch = len(matchedElems) > 0
|
||||
if term.MatchExpressions != nil {
|
||||
matchedElems, err = term.MatchExpressions.MatchGetInstances(f.Elements)
|
||||
isMatch = len(matchedElems) > 0
|
||||
}
|
||||
var meTmp []MatchedElement
|
||||
if err == nil && isMatch && term.MatchName != nil {
|
||||
meTmp, err = term.MatchName.MatchInstanceAttributeNames(f.Elements)
|
||||
isMatch = len(meTmp) > 0
|
||||
matchedElems = append(matchedElems, meTmp...)
|
||||
|
||||
}
|
||||
} else {
|
||||
return false, nil, fmt.Errorf("feature %q not available", featureName)
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.kf-1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-1": newMatchExpression(MatchExists),
|
||||
},
|
||||
},
|
||||
|
@ -84,7 +84,7 @@ func TestRule(t *testing.T) {
|
|||
r1.MatchFeatures = FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.kf-1",
|
||||
MatchExpressions: MatchExpressionSet{},
|
||||
MatchExpressions: &MatchExpressionSet{},
|
||||
},
|
||||
}
|
||||
m, err = r1.Execute(f)
|
||||
|
@ -108,7 +108,7 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.vf-1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-1": newMatchExpression(MatchIn, "val-1"),
|
||||
},
|
||||
},
|
||||
|
@ -129,7 +129,7 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.if-1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"attr-1": newMatchExpression(MatchIn, "val-1"),
|
||||
},
|
||||
},
|
||||
|
@ -150,13 +150,13 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.vf-1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-1": newMatchExpression(MatchIn, "val-x"),
|
||||
},
|
||||
},
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.if-1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"attr-1": newMatchExpression(MatchIn, "val-1"),
|
||||
},
|
||||
},
|
||||
|
@ -166,7 +166,7 @@ func TestRule(t *testing.T) {
|
|||
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||
assert.Nil(t, m.Labels, "instances should not have matched")
|
||||
|
||||
r5.MatchFeatures[0].MatchExpressions["key-1"] = newMatchExpression(MatchIn, "val-1")
|
||||
(*r5.MatchFeatures[0].MatchExpressions)["key-1"] = newMatchExpression(MatchIn, "val-1")
|
||||
m, err = r5.Execute(f)
|
||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||
assert.Equal(t, r5.Labels, m.Labels, "instances should have matched")
|
||||
|
@ -177,7 +177,7 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.kf-1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-na": newMatchExpression(MatchExists),
|
||||
},
|
||||
},
|
||||
|
@ -193,13 +193,13 @@ func TestRule(t *testing.T) {
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain-1.kf-1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-1": newMatchExpression(MatchExists),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
r5.MatchFeatures[0].MatchExpressions["key-1"] = newMatchExpression(MatchIn, "val-1")
|
||||
(*r5.MatchFeatures[0].MatchExpressions)["key-1"] = newMatchExpression(MatchIn, "val-1")
|
||||
m, err = r5.Execute(f)
|
||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||
assert.Equal(t, r5.Labels, m.Labels, "instances should have matched")
|
||||
|
@ -222,6 +222,7 @@ func TestTemplating(t *testing.T) {
|
|||
"key-1": "val-1",
|
||||
"keu-2": "val-2",
|
||||
"key-3": "val-3",
|
||||
"key-4": "val-4",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -278,7 +279,7 @@ var-2=
|
|||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain_1.kf_1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-a": newMatchExpression(MatchExists),
|
||||
"key-c": newMatchExpression(MatchExists),
|
||||
"foo": newMatchExpression(MatchDoesNotExist),
|
||||
|
@ -286,20 +287,20 @@ var-2=
|
|||
},
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain_1.vf_1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-1": newMatchExpression(MatchIn, "val-1", "val-2"),
|
||||
"bar": newMatchExpression(MatchDoesNotExist),
|
||||
},
|
||||
},
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain_1.if_1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"attr-1": newMatchExpression(MatchLt, "100"),
|
||||
},
|
||||
},
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain_1.if_1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"attr-1": newMatchExpression(MatchExists),
|
||||
"attr-2": newMatchExpression(MatchExists),
|
||||
"attr-3": newMatchExpression(MatchExists),
|
||||
|
@ -356,7 +357,7 @@ var-2=
|
|||
// Use a simple empty matchexpression set to match anything.
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain_1.kf_1",
|
||||
MatchExpressions: MatchExpressionSet{
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-a": newMatchExpression(MatchExists),
|
||||
},
|
||||
},
|
||||
|
@ -397,4 +398,42 @@ var-2=
|
|||
_, err = r2.Execute(f)
|
||||
assert.Error(t, err)
|
||||
|
||||
//
|
||||
// Test matchName
|
||||
//
|
||||
r4 := Rule{
|
||||
LabelsTemplate: "{{range .domain_1.vf_1}}{{.Name}}={{.Value}}\n{{end}}",
|
||||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain_1.vf_1",
|
||||
MatchExpressions: &MatchExpressionSet{
|
||||
"key-5": newMatchExpression(MatchDoesNotExist),
|
||||
},
|
||||
MatchName: newMatchExpression(MatchIn, "key-1", "key-4"),
|
||||
},
|
||||
},
|
||||
}
|
||||
expectedLabels = map[string]string{
|
||||
"key-1": "val-1",
|
||||
"key-4": "val-4",
|
||||
"key-5": "",
|
||||
}
|
||||
|
||||
m, err = r4.Execute(f)
|
||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||
assert.Equal(t, expectedLabels, m.Labels, "instances should have matched")
|
||||
|
||||
r4 = Rule{
|
||||
Labels: map[string]string{"should-not-match": "true"},
|
||||
MatchFeatures: FeatureMatcher{
|
||||
FeatureMatcherTerm{
|
||||
Feature: "domain_1.vf_1",
|
||||
MatchName: newMatchExpression(MatchIn, "key-not-exists"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
m, err = r4.Execute(f)
|
||||
assert.Nilf(t, err, "unexpected error: %v", err)
|
||||
assert.Equal(t, map[string]string(nil), m.Labels, "instances should have matched")
|
||||
}
|
||||
|
|
|
@ -198,8 +198,16 @@ type FeatureMatcher []FeatureMatcherTerm
|
|||
// requirements (specified as MatchExpressions) are evaluated against each
|
||||
// element in the feature set.
|
||||
type FeatureMatcherTerm struct {
|
||||
Feature string `json:"feature"`
|
||||
MatchExpressions MatchExpressionSet `json:"matchExpressions"`
|
||||
// Feature is the name of the feature set to match against.
|
||||
Feature string `json:"feature"`
|
||||
// MatchExpressions is the set of per-element expressions evaluated. These
|
||||
// match against the value of the specified elements.
|
||||
// +optional
|
||||
MatchExpressions *MatchExpressionSet `json:"matchExpressions"`
|
||||
// MatchName in an expression that is matched against the name of each
|
||||
// element in the feature set.
|
||||
// +optional
|
||||
MatchName *MatchExpression `json:"matchName"`
|
||||
}
|
||||
|
||||
// MatchExpressionSet contains a set of MatchExpressions, each of which is
|
||||
|
@ -279,3 +287,7 @@ const (
|
|||
// output of preceding rules.
|
||||
RuleBackrefFeature = "matched"
|
||||
)
|
||||
|
||||
// MatchAllNames is a special key in MatchExpressionSet to use field names
|
||||
// (keys from the input) instead of values when matching.
|
||||
const MatchAllNames = "*"
|
||||
|
|
|
@ -58,19 +58,28 @@ func (in *FeatureMatcherTerm) DeepCopyInto(out *FeatureMatcherTerm) {
|
|||
*out = *in
|
||||
if in.MatchExpressions != nil {
|
||||
in, out := &in.MatchExpressions, &out.MatchExpressions
|
||||
*out = make(MatchExpressionSet, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *MatchExpression
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = new(MatchExpression)
|
||||
(*in).DeepCopyInto(*out)
|
||||
*out = new(MatchExpressionSet)
|
||||
if **in != nil {
|
||||
in, out := *in, *out
|
||||
*out = make(map[string]*MatchExpression, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal *MatchExpression
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = new(MatchExpression)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
if in.MatchName != nil {
|
||||
in, out := &in.MatchName, &out.MatchName
|
||||
*out = new(MatchExpression)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureMatcherTerm.
|
||||
|
|
112
samples/nodefeaturerule-cpu.yaml
Normal file
112
samples/nodefeaturerule-cpu.yaml
Normal file
|
@ -0,0 +1,112 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in cpu feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-cpu-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in cpu-cpuid labels"
|
||||
labelsTemplate: |
|
||||
{{ range .cpu.cpuid }}cpu-cpuid.{{ .Name }}=true
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: cpu.cpuid
|
||||
matchName:
|
||||
op: NotIn
|
||||
value:
|
||||
- "BMI1"
|
||||
- "BMI2"
|
||||
- "CLMUL"
|
||||
- "CMOV"
|
||||
- "CX16"
|
||||
- "ERMS"
|
||||
- "F16C"
|
||||
- "HTT"
|
||||
- "LZCNT"
|
||||
- "MMX"
|
||||
- "MMXEXT"
|
||||
- "NX"
|
||||
- "POPCNT"
|
||||
- "RDRAND"
|
||||
- "RDSEED"
|
||||
- "RDTSCP"
|
||||
- "SGX"
|
||||
- "SGXLC"
|
||||
- "SSE"
|
||||
- "SSE2"
|
||||
- "SSE3"
|
||||
- "SSE4"
|
||||
- "SSE42"
|
||||
- "SSSE3"
|
||||
|
||||
- name: "nfd built-in cpu-hardware_multithreading label"
|
||||
labelsTemplate: |
|
||||
{{ range .cpu.topology }}cpu-{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: cpu.topology
|
||||
matchName:
|
||||
op: In
|
||||
value:
|
||||
- "hardware_multithreading"
|
||||
|
||||
- name: "nfd built-in cpu-cstate and cpu-pstate labels"
|
||||
labelsTemplate: |
|
||||
{{ range .cpu.cstate }}cpu-cstate.{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
{{ range .cpu.pstate }}cpu-pstate.{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchAny:
|
||||
- matchFeatures:
|
||||
- feature: cpu.cstate
|
||||
matchName:
|
||||
op: Exists
|
||||
- matchFeatures:
|
||||
- feature: cpu.pstate
|
||||
matchName:
|
||||
op: Exists
|
||||
|
||||
- name: "nfd built-in cpu-model labels"
|
||||
labelsTemplate: |
|
||||
{{ range .cpu.model }}cpu-model.{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: cpu.model
|
||||
matchName:
|
||||
op: Exists
|
||||
|
||||
- name: "nfd built-in cpu-security labels"
|
||||
labelsTemplate: |
|
||||
{{ range .cpu.security }}cpu-security.{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: cpu.security
|
||||
matchName:
|
||||
op: NotIn
|
||||
value:
|
||||
- "tdx.total_keys"
|
||||
- "sgx.epc"
|
||||
- "sev.encrypted_state_ids"
|
||||
- "sev.asids"
|
||||
|
||||
- name: "nfd built-in cpu-sst labels"
|
||||
labelsTemplate: |
|
||||
{{ range .cpu.sst }}cpu-power.sst_{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: cpu.sst
|
||||
matchName:
|
||||
op: Exists
|
||||
|
||||
- name: "nfd built-in cpu-coprocessor labels"
|
||||
labelsTemplate: |
|
||||
{{ range .cpu.sst }}cpu-coprocessor.{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: cpu.coprocessor
|
||||
matchName:
|
||||
op: In
|
||||
value:
|
||||
- "nx_gzip"
|
29
samples/nodefeaturerule-custom.yaml
Normal file
29
samples/nodefeaturerule-custom.yaml
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in static custom feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-custom-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in static custom rdma.capable label"
|
||||
labels:
|
||||
"custom-rdma.capable": "true"
|
||||
matchFeatures:
|
||||
- feature: pci.device
|
||||
matchExpressions:
|
||||
vendor:
|
||||
op: In
|
||||
value: ["15b3"]
|
||||
|
||||
- name: "nfd built-in static custom rdma.available label"
|
||||
labels:
|
||||
"custom-rdma.available": "true"
|
||||
matchFeatures:
|
||||
- feature: kernel.loadedmodule
|
||||
matchExpressions:
|
||||
"ib_uverbs":
|
||||
op: Exists
|
||||
"rdma_ucm":
|
||||
op: Exists
|
38
samples/nodefeaturerule-kernel.yaml
Normal file
38
samples/nodefeaturerule-kernel.yaml
Normal file
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in kernel feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-kernel-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in kernel-version labels"
|
||||
labelsTemplate: |
|
||||
{{ range .kernel.version }}kernel-version.{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: kernel.version
|
||||
matchName:
|
||||
op: Exists
|
||||
|
||||
- name: "nfd built-in kernel-config labels"
|
||||
labelsTemplate: |
|
||||
{{ range .kernel.config }}kernel-config.{{ .Name }}=true
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: kernel.config
|
||||
matchExpressions:
|
||||
"NO_HZ": {op: In, value: ["y"]}
|
||||
"NO_HZ_IDLE": {op: In, value: ["y"]}
|
||||
"NO_HZ_FULL": {op: In, value: ["y"]}
|
||||
"PREEMPT": {op: In, value: ["y"]}
|
||||
|
||||
- name: "nfd built-in kernel-selinux labels"
|
||||
labels:
|
||||
"kernel-selinux.enabled": "true"
|
||||
matchFeatures:
|
||||
- feature: kernel.selinux
|
||||
matchExpressions:
|
||||
"enabled":
|
||||
op: IsTrue
|
17
samples/nodefeaturerule-local.yaml
Normal file
17
samples/nodefeaturerule-local.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in local feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-local-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in labels from the local feature source"
|
||||
labelsTemplate: |
|
||||
{{ range .local.label }}{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: local.label
|
||||
matchName:
|
||||
op: Exists
|
34
samples/nodefeaturerule-memory.yaml
Normal file
34
samples/nodefeaturerule-memory.yaml
Normal file
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in memory feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-memory-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in memory-numa labels"
|
||||
labels:
|
||||
"memory-numa": "true"
|
||||
matchFeatures:
|
||||
- feature: memory.numa
|
||||
matchExpressions:
|
||||
"is_numa":
|
||||
op: IsTrue
|
||||
|
||||
- name: "nfd built-in memory-nv.present label"
|
||||
labelsTemplate: "{{ if gt (len .memory.nv ) 0 }}memory-nv.present=true{{ end }}"
|
||||
matchFeatures:
|
||||
- feature: memory.nv
|
||||
matchName:
|
||||
op: Exists
|
||||
|
||||
- name: "nfd built-in memory-nv.dax label"
|
||||
labels:
|
||||
"memory.nv.dax": "true"
|
||||
matchFeatures:
|
||||
- feature: memory.nv
|
||||
matchExpressions:
|
||||
"devtype":
|
||||
op: In
|
||||
value: ["nd_dax"]
|
28
samples/nodefeaturerule-network.yaml
Normal file
28
samples/nodefeaturerule-network.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in networkfeature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-network-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in network-sriov.capable label"
|
||||
labels:
|
||||
"network-sriov.capable": "true"
|
||||
matchFeatures:
|
||||
- feature: network.device
|
||||
matchExpressions:
|
||||
"sriov_totalvfs":
|
||||
op: Gt
|
||||
value: ["0"]
|
||||
|
||||
- name: "nfd built-in network-sriov.configured label"
|
||||
labels:
|
||||
"network-sriov.configured": "true"
|
||||
matchFeatures:
|
||||
- feature: network.device
|
||||
matchExpressions:
|
||||
"network-sriov_numvfs":
|
||||
op: Gt
|
||||
value: ["0"]
|
32
samples/nodefeaturerule-pci.yaml
Normal file
32
samples/nodefeaturerule-pci.yaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in pci feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-pci-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in pci-<device>.present labels"
|
||||
labelsTemplate: |
|
||||
{{ range .pci.device }}pci-{{ .class }}_{{ .vendor }}.present=true
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: pci.device
|
||||
matchExpressions:
|
||||
"class":
|
||||
op: InRegexp
|
||||
value: ["^03", "^0b40", "^12"]
|
||||
|
||||
- name: "nfd built-in pci-<device>.sriov.capable labels"
|
||||
labelsTemplate: |
|
||||
{{ range .pci.device }}pci-{{ .class }}_{{ .vendor }}.sriov.capable=true
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: pci.device
|
||||
matchExpressions:
|
||||
"class":
|
||||
op: InRegexp
|
||||
value: ["^03", "^0b40", "^12"]
|
||||
"sriov_totalvfs":
|
||||
op: Exists
|
18
samples/nodefeaturerule-storage.yaml
Normal file
18
samples/nodefeaturerule-storage.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in storage feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-storage-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in storage-nonrotationaldisk label"
|
||||
labels:
|
||||
"storage-nonrotationaldisk": "true"
|
||||
matchFeatures:
|
||||
- feature: storage.block
|
||||
matchExpressions:
|
||||
"rotational":
|
||||
op: In
|
||||
value: ["0"]
|
23
samples/nodefeaturerule-system.yaml
Normal file
23
samples/nodefeaturerule-system.yaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in system feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-system-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in system-os_release labels"
|
||||
labelsTemplate: |
|
||||
{{ range .system.osrelease }}system-os_release.{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: system.osrelease
|
||||
matchName:
|
||||
op: In
|
||||
value:
|
||||
- "ID"
|
||||
- "VERSION_ID"
|
||||
- "VERSION_ID.major"
|
||||
- "VERSION_ID.minor"
|
||||
|
19
samples/nodefeaturerule-usb.yaml
Normal file
19
samples/nodefeaturerule-usb.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# This NodeFeatureRule replicates all built-in usb feature labels of NFD.
|
||||
#
|
||||
apiVersion: nfd.k8s-sigs.io/v1alpha1
|
||||
kind: NodeFeatureRule
|
||||
metadata:
|
||||
name: nfd-builtin-usb-features
|
||||
spec:
|
||||
rules:
|
||||
- name: "nfd built-in usb-<device>.present labels"
|
||||
labelsTemplate: |
|
||||
{{ range .usb.device }}usb-{{ .class }}_{{ .vendor }}_{{ .device }}.present=true
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: usb.device
|
||||
matchExpressions:
|
||||
"class":
|
||||
op: In
|
||||
value: ["0e", "ef", "fe", "ff"]
|
|
@ -31,7 +31,7 @@ func getStaticFeatureConfig() []CustomRule {
|
|||
MatchFeatures: nfdv1alpha1.FeatureMatcher{
|
||||
nfdv1alpha1.FeatureMatcherTerm{
|
||||
Feature: "pci.device",
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{
|
||||
"vendor": &nfdv1alpha1.MatchExpression{
|
||||
Op: nfdv1alpha1.MatchIn,
|
||||
Value: nfdv1alpha1.MatchValue{"15b3"}},
|
||||
|
@ -47,7 +47,7 @@ func getStaticFeatureConfig() []CustomRule {
|
|||
MatchFeatures: nfdv1alpha1.FeatureMatcher{
|
||||
nfdv1alpha1.FeatureMatcherTerm{
|
||||
Feature: "kernel.loadedmodule",
|
||||
MatchExpressions: nfdv1alpha1.MatchExpressionSet{
|
||||
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{
|
||||
"ib_uverbs": &nfdv1alpha1.MatchExpression{
|
||||
Op: nfdv1alpha1.MatchExists,
|
||||
},
|
||||
|
|
|
@ -17,6 +17,13 @@ spec:
|
|||
matchExpressions:
|
||||
"flag_1": {op: Exists}
|
||||
|
||||
- name: "e2e-flag-test-2"
|
||||
labels:
|
||||
e2e-flag-test-2: "true"
|
||||
matchFeatures:
|
||||
- feature: "fake.flag"
|
||||
matchName: {op: In, value: ["flag_2"]}
|
||||
|
||||
# Negative test not supposed to create a label
|
||||
- name: "e2e-flag-test-neg-1"
|
||||
labels:
|
||||
|
@ -26,6 +33,13 @@ spec:
|
|||
matchExpressions:
|
||||
"flag_1": {op: DoesNotExist}
|
||||
|
||||
- name: "e2e-flag-test-neg-2"
|
||||
labels:
|
||||
e2e-flag-test-neg-2: "true"
|
||||
matchFeatures:
|
||||
- feature: "fake.flag"
|
||||
matchName: {op: In, value: ["flag_x"]}
|
||||
|
||||
#
|
||||
# Simple test rules for attribute features
|
||||
#
|
||||
|
@ -40,6 +54,13 @@ spec:
|
|||
"attr_1": {op: IsTrue}
|
||||
"attr_2": {op: IsFalse}
|
||||
|
||||
- name: "e2e-attribute-test-2"
|
||||
labels:
|
||||
e2e-attribute-test-2: "true"
|
||||
matchFeatures:
|
||||
- feature: "fake.attribute"
|
||||
matchName: {op: In, value: ["attr_2", "attr_x"]}
|
||||
|
||||
# Negative test not supposed to create a label
|
||||
- name: "e2e-attribute-test-neg-1"
|
||||
labels:
|
||||
|
@ -50,8 +71,15 @@ spec:
|
|||
"attr_1": {op: IsTrue}
|
||||
"attr_2": {op: IsTrue}
|
||||
|
||||
- name: "e2e-attribute-test-neg-2"
|
||||
labels:
|
||||
e2e-attribute-test-neg-2: "true"
|
||||
matchFeatures:
|
||||
- feature: "fake.attribute"
|
||||
matchName: {op: In, value: ["attr_x"]}
|
||||
|
||||
#
|
||||
# Simple test rules for instnace features
|
||||
# Simple test rules for instance features
|
||||
#
|
||||
- name: "e2e-instance-test-1"
|
||||
labels:
|
||||
|
@ -65,6 +93,13 @@ spec:
|
|||
"attr_1": {op: In, value: ["true"]}
|
||||
"attr_3": {op: Gt, value: ["10"]}
|
||||
|
||||
- name: "e2e-instance-test-2"
|
||||
labels:
|
||||
e2e-instance-test-2: "true"
|
||||
matchFeatures:
|
||||
- feature: "fake.instance"
|
||||
matchName: {op: In, value: ["attr_1", "attr_x"]}
|
||||
|
||||
# Negative test not supposed to create a label
|
||||
- name: "e2e-instance-test-neg-1"
|
||||
labels:
|
||||
|
@ -74,3 +109,10 @@ spec:
|
|||
matchExpressions:
|
||||
"attr_1": {op: In, value: ["true"]}
|
||||
"attr_3": {op: Lt, value: ["10"]}
|
||||
|
||||
- name: "e2e-instance-test-neg-2"
|
||||
labels:
|
||||
e2e-instance-test-neg-2: "true"
|
||||
matchFeatures:
|
||||
- feature: "fake.instance"
|
||||
matchName: {op: In, value: ["attr_x"]}
|
||||
|
|
|
@ -40,3 +40,12 @@ spec:
|
|||
matchExpressions:
|
||||
"attr_1": {op: In, value: ["true"]}
|
||||
|
||||
- name: "e2e-template-test-2"
|
||||
labelsTemplate: |
|
||||
{{ range .fake.attribute }}e2e-template-test-2-{{ .Name }}={{ .Value }}
|
||||
{{ end }}
|
||||
matchFeatures:
|
||||
- feature: "fake.attribute"
|
||||
matchExpressions:
|
||||
"attr_2": {op: IsFalse}
|
||||
matchName: {op: In, value: ["attr_3"]}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -653,7 +654,7 @@ var _ = NFDDescribe(Label("nfd-master"), func() {
|
|||
|
||||
// Test NodeFeatureRule
|
||||
//
|
||||
Context("and nfd-worker and NodeFeatureRules objects deployed", func() {
|
||||
Context("and nfd-worker and NodeFeatureRules objects deployed", Label("nodefeaturerule"), func() {
|
||||
testTolerations := []corev1.Toleration{
|
||||
{
|
||||
Key: "feature.node.kubernetes.io/fake-special-node",
|
||||
|
@ -714,8 +715,11 @@ core:
|
|||
expectedLabels := map[string]k8sLabels{
|
||||
"*": {
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-flag-test-1": "true",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-flag-test-2": "true",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-attribute-test-1": "true",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-attribute-test-2": "true",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-instance-test-1": "true",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-instance-test-2": "true",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -729,13 +733,17 @@ core:
|
|||
Expect(testutils.CreateNodeFeatureRulesFromFile(ctx, nfdClient, "nodefeaturerule-2.yaml")).NotTo(HaveOccurred())
|
||||
|
||||
// Add features from NodeFeatureRule #2
|
||||
expectedLabels["*"][nfdv1alpha1.FeatureLabelNs+"/e2e-matchany-test-1"] = "true"
|
||||
expectedLabels["*"][nfdv1alpha1.FeatureLabelNs+"/e2e-template-test-1-instance_1"] = "found"
|
||||
expectedLabels["*"][nfdv1alpha1.FeatureLabelNs+"/e2e-template-test-1-instance_2"] = "found"
|
||||
expectedLabels["*"][nfdv1alpha1.FeatureLabelNs+"/dynamic-label"] = "true"
|
||||
maps.Copy(expectedLabels["*"], k8sLabels{
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-matchany-test-1": "true",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-template-test-1-instance_1": "found",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-template-test-1-instance_2": "found",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-template-test-2-attr_2": "false",
|
||||
nfdv1alpha1.FeatureLabelNs + "/e2e-template-test-2-attr_3": "10",
|
||||
nfdv1alpha1.FeatureLabelNs + "/dynamic-label": "true",
|
||||
})
|
||||
expectedAnnotations := map[string]k8sAnnotations{
|
||||
"*": {
|
||||
"nfd.node.kubernetes.io/feature-labels": "dynamic-label,e2e-attribute-test-1,e2e-flag-test-1,e2e-instance-test-1,e2e-matchany-test-1,e2e-template-test-1-instance_1,e2e-template-test-1-instance_2"},
|
||||
"nfd.node.kubernetes.io/feature-labels": "dynamic-label,e2e-attribute-test-1,e2e-attribute-test-2,e2e-flag-test-1,e2e-flag-test-2,e2e-instance-test-1,e2e-instance-test-2,e2e-matchany-test-1,e2e-template-test-1-instance_1,e2e-template-test-1-instance_2,e2e-template-test-2-attr_2,e2e-template-test-2-attr_3"},
|
||||
}
|
||||
|
||||
By("Verifying node labels from NodeFeatureRules #1 and #2")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue