mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
Option to stop implicitly adding default prefix to names
Add new autoDefaultNs (default is "true") config option to nfd-master. Setting the config option to false stops NFD from automatically adding the "feature.node.kubernetes.io/" prefix to labels, annotations and extended resources. Taints are not affected as for them no prefix is automatically added. The user-visible part of enabling the option change is that NodeFeatureRules, local feature files, hooks and configuration of the "custom" may need to be altereda (if the auto-prefixing is relied on). For now, the config option defaults to "true", meaning no change in default behavior. However, the intent is to change the default to "false" in a future release, deprecating the option and eventually removing it (forcing it to "false"). The goal of stopping doing "auto-prefixing" is to simplify the operation (of nfd and users). Make the naming more straightforward and easier to understand and debug (kind of WYSIWYG), eliminating peculiar corner cases: 1. Make validation simpler and unambiguous 2. Remove "overloading" of names, i.e. the mapping two values to the same actual name. E.g. previously something like labels: feature.node.kubernetes.io/foo: bar foo: baz Could actually result in node label: feature.node.kubernetes.io/foo: baz 3. Make the processing/usagee of the "rule.matched" and "local.labels" feature in NodeFeatureRules unambiguous and more understadable. E.g. previously you could have node label "feature.node.kubernetes.io/local-foo: bar" but in the NodeFeatureRule you'd need to use the unprefixed name "local-foo" or the fully prefixed name, depending on what was specified in the feature file (or hook) on the node(s). NOTE: setting autoDefaultNs to false is a breaking change for users who rely on automatic prefixing with the default feature.node.kubernetes.io/ namespace. NodeFeatureRules, feature files, hooks and custom rules (configuration of the "custom" source of nfd-worker) will need to be altered. Unprefixed labels, annoations and extended resources will be denied by nfd-master.
This commit is contained in:
parent
4e7f8b10be
commit
1d012a28cd
10 changed files with 245 additions and 118 deletions
|
@ -1,4 +1,5 @@
|
||||||
# noPublish: false
|
# noPublish: false
|
||||||
|
# autoDefaultNs: true
|
||||||
# extraLabelNs: ["added.ns.io","added.kubernets.io"]
|
# extraLabelNs: ["added.ns.io","added.kubernets.io"]
|
||||||
# denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
# denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
||||||
# resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
|
# resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
# # The following feature demonstrates the capabilities of the matchFeatures
|
# # The following feature demonstrates the capabilities of the matchFeatures
|
||||||
# - name: "my custom rule"
|
# - name: "my custom rule"
|
||||||
# labels:
|
# labels:
|
||||||
# my-ng-feature: "true"
|
# "vendor.io/my-ng-feature": "true"
|
||||||
# # matchFeatures implements a logical AND over all matcher terms in the
|
# # matchFeatures implements a logical AND over all matcher terms in the
|
||||||
# # list (i.e. all of the terms, or per-feature matchers, must match)
|
# # list (i.e. all of the terms, or per-feature matchers, must match)
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
|
@ -153,7 +153,7 @@
|
||||||
# # The following feature demonstrates the capabilities of the matchAny
|
# # The following feature demonstrates the capabilities of the matchAny
|
||||||
# - name: "my matchAny rule"
|
# - name: "my matchAny rule"
|
||||||
# labels:
|
# labels:
|
||||||
# my-ng-feature-2: "my-value"
|
# "vendor.io/my-ng-feature-2": "my-value"
|
||||||
# # matchAny implements a logical IF over all elements (sub-matchers) in
|
# # matchAny implements a logical IF over all elements (sub-matchers) in
|
||||||
# # the list (i.e. at least one feature matcher must match)
|
# # the list (i.e. at least one feature matcher must match)
|
||||||
# matchAny:
|
# matchAny:
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
# # The following features demonstreate label templating capabilities
|
# # The following features demonstreate label templating capabilities
|
||||||
# - name: "my template rule"
|
# - name: "my template rule"
|
||||||
# labelsTemplate: |
|
# labelsTemplate: |
|
||||||
# {{ range .system.osrelease }}my-system-feature.{{ .Name }}={{ .Value }}
|
# {{ range .system.osrelease }}vendor.io/my-system-feature.{{ .Name }}={{ .Value }}
|
||||||
# {{ end }}
|
# {{ end }}
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
# - feature: system.osrelease
|
# - feature: system.osrelease
|
||||||
|
@ -187,7 +187,7 @@
|
||||||
#
|
#
|
||||||
# - name: "my template rule 2"
|
# - name: "my template rule 2"
|
||||||
# labelsTemplate: |
|
# labelsTemplate: |
|
||||||
# {{ range .pci.device }}my-pci-device.{{ .class }}-{{ .device }}=with-cpuid
|
# {{ range .pci.device }}vendor.io/my-pci-device.{{ .class }}-{{ .device }}=with-cpuid
|
||||||
# {{ end }}
|
# {{ end }}
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
# - feature: pci.device
|
# - feature: pci.device
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
# # previous labels and vars
|
# # previous labels and vars
|
||||||
# - name: "my dummy kernel rule"
|
# - name: "my dummy kernel rule"
|
||||||
# labels:
|
# labels:
|
||||||
# "my.kernel.feature": "true"
|
# "vendor.io/my.kernel.feature": "true"
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
# - feature: kernel.version
|
# - feature: kernel.version
|
||||||
# matchExpressions:
|
# matchExpressions:
|
||||||
|
@ -217,10 +217,10 @@
|
||||||
#
|
#
|
||||||
# - name: "my rule using backrefs"
|
# - name: "my rule using backrefs"
|
||||||
# labels:
|
# labels:
|
||||||
# "my.backref.feature": "true"
|
# "vendor.io/my.backref.feature": "true"
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
# - feature: rule.matched
|
# - feature: rule.matched
|
||||||
# matchExpressions:
|
# matchExpressions:
|
||||||
# my.kernel.feature: {op: IsTrue}
|
# vendor.io/my.kernel.feature: {op: IsTrue}
|
||||||
# my.dummy.var: {op: Gt, value: ["0"]}
|
# my.dummy.var: {op: Gt, value: ["0"]}
|
||||||
#
|
#
|
||||||
|
|
|
@ -16,6 +16,7 @@ master:
|
||||||
enable: true
|
enable: true
|
||||||
config: ### <NFD-MASTER-CONF-START-DO-NOT-REMOVE>
|
config: ### <NFD-MASTER-CONF-START-DO-NOT-REMOVE>
|
||||||
# noPublish: false
|
# noPublish: false
|
||||||
|
# autoDefaultNs: true
|
||||||
# extraLabelNs: ["added.ns.io","added.kubernets.io"]
|
# extraLabelNs: ["added.ns.io","added.kubernets.io"]
|
||||||
# denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
# denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
||||||
# resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
|
# resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
|
||||||
|
@ -219,7 +220,7 @@ worker:
|
||||||
# # The following feature demonstrates the capabilities of the matchFeatures
|
# # The following feature demonstrates the capabilities of the matchFeatures
|
||||||
# - name: "my custom rule"
|
# - name: "my custom rule"
|
||||||
# labels:
|
# labels:
|
||||||
# my-ng-feature: "true"
|
# "vendor.io/my-ng-feature": "true"
|
||||||
# # matchFeatures implements a logical AND over all matcher terms in the
|
# # matchFeatures implements a logical AND over all matcher terms in the
|
||||||
# # list (i.e. all of the terms, or per-feature matchers, must match)
|
# # list (i.e. all of the terms, or per-feature matchers, must match)
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
|
@ -290,7 +291,7 @@ worker:
|
||||||
# # The following feature demonstrates the capabilities of the matchAny
|
# # The following feature demonstrates the capabilities of the matchAny
|
||||||
# - name: "my matchAny rule"
|
# - name: "my matchAny rule"
|
||||||
# labels:
|
# labels:
|
||||||
# my-ng-feature-2: "my-value"
|
# "vendor.io/my-ng-feature-2": "my-value"
|
||||||
# # matchAny implements a logical IF over all elements (sub-matchers) in
|
# # matchAny implements a logical IF over all elements (sub-matchers) in
|
||||||
# # the list (i.e. at least one feature matcher must match)
|
# # the list (i.e. at least one feature matcher must match)
|
||||||
# matchAny:
|
# matchAny:
|
||||||
|
@ -314,7 +315,7 @@ worker:
|
||||||
# # The following features demonstreate label templating capabilities
|
# # The following features demonstreate label templating capabilities
|
||||||
# - name: "my template rule"
|
# - name: "my template rule"
|
||||||
# labelsTemplate: |
|
# labelsTemplate: |
|
||||||
# {{ range .system.osrelease }}my-system-feature.{{ .Name }}={{ .Value }}
|
# {{ range .system.osrelease }}vendor.io/my-system-feature.{{ .Name }}={{ .Value }}
|
||||||
# {{ end }}
|
# {{ end }}
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
# - feature: system.osrelease
|
# - feature: system.osrelease
|
||||||
|
@ -324,7 +325,7 @@ worker:
|
||||||
#
|
#
|
||||||
# - name: "my template rule 2"
|
# - name: "my template rule 2"
|
||||||
# labelsTemplate: |
|
# labelsTemplate: |
|
||||||
# {{ range .pci.device }}my-pci-device.{{ .class }}-{{ .device }}=with-cpuid
|
# {{ range .pci.device }}vendor.io/my-pci-device.{{ .class }}-{{ .device }}=with-cpuid
|
||||||
# {{ end }}
|
# {{ end }}
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
# - feature: pci.device
|
# - feature: pci.device
|
||||||
|
@ -339,7 +340,7 @@ worker:
|
||||||
# # previous labels and vars
|
# # previous labels and vars
|
||||||
# - name: "my dummy kernel rule"
|
# - name: "my dummy kernel rule"
|
||||||
# labels:
|
# labels:
|
||||||
# "my.kernel.feature": "true"
|
# "vendor.io/my.kernel.feature": "true"
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
# - feature: kernel.version
|
# - feature: kernel.version
|
||||||
# matchExpressions:
|
# matchExpressions:
|
||||||
|
@ -354,11 +355,11 @@ worker:
|
||||||
#
|
#
|
||||||
# - name: "my rule using backrefs"
|
# - name: "my rule using backrefs"
|
||||||
# labels:
|
# labels:
|
||||||
# "my.backref.feature": "true"
|
# "vendor.io/my.backref.feature": "true"
|
||||||
# matchFeatures:
|
# matchFeatures:
|
||||||
# - feature: rule.matched
|
# - feature: rule.matched
|
||||||
# matchExpressions:
|
# matchExpressions:
|
||||||
# my.kernel.feature: {op: IsTrue}
|
# vendor.io/my.kernel.feature: {op: IsTrue}
|
||||||
# my.dummy.var: {op: Gt, value: ["0"]}
|
# my.dummy.var: {op: Gt, value: ["0"]}
|
||||||
#
|
#
|
||||||
### <NFD-WORKER-CONF-END-DO-NOT-REMOVE>
|
### <NFD-WORKER-CONF-END-DO-NOT-REMOVE>
|
||||||
|
|
|
@ -70,6 +70,38 @@ Example:
|
||||||
denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## autoDefaultNs
|
||||||
|
|
||||||
|
The `autoDefaultNs` option controls the automatic prefixing of names. When set
|
||||||
|
to true (the default in {{ site.version }}) nfd-master automatically adds the
|
||||||
|
default `feature.node.kubernetes.io/` prefix to unprefixed labels, annotations
|
||||||
|
and extended resources - this is also the default behavior in NFD v0.13 and
|
||||||
|
earlier. When the option is set to `false`, no prefix will be prepended to
|
||||||
|
unprefixed names, effectively causing them to be filtered out (as NFD does not
|
||||||
|
currently allow unprefixed names of labels, annotations or extended resources).
|
||||||
|
The default will be changed to `false` in a future release.
|
||||||
|
|
||||||
|
For example, with the `autoDefaultNs` set to `true`, a NodeFeatureRule with
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
foo: bar
|
||||||
|
```
|
||||||
|
|
||||||
|
Will turn into `feature.node.kubernetes.io/foo=bar` node label. With
|
||||||
|
`autoDefaultNs` set to `false`, no prefix is added and the label will be
|
||||||
|
filtered out.
|
||||||
|
|
||||||
|
Note that taint keys are not affected by this option.
|
||||||
|
|
||||||
|
Default: `true`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
autoDefaultNs: false
|
||||||
|
```
|
||||||
|
|
||||||
## resourceLabels
|
## resourceLabels
|
||||||
|
|
||||||
**DEPRECATED**: [NodeFeatureRule](../usage/custom-resources.md#nodefeaturerule)
|
**DEPRECATED**: [NodeFeatureRule](../usage/custom-resources.md#nodefeaturerule)
|
||||||
|
|
|
@ -81,7 +81,7 @@ spec:
|
||||||
vendor: "acme"
|
vendor: "acme"
|
||||||
# Labels to be created
|
# Labels to be created
|
||||||
labels:
|
labels:
|
||||||
vendor-feature.enabled: "true"
|
vendor.io/feature.enabled: "true"
|
||||||
```
|
```
|
||||||
|
|
||||||
The object targets node named `node-1`. It lists two "flag type" features under
|
The object targets node named `node-1`. It lists two "flag type" features under
|
||||||
|
@ -92,8 +92,7 @@ labels but they will be used as input when the
|
||||||
[`NodeFeatureRule`](#nodefeaturerule-custom-resource) objects are evaluated.
|
[`NodeFeatureRule`](#nodefeaturerule-custom-resource) objects are evaluated.
|
||||||
|
|
||||||
In addition, the example requests directly the
|
In addition, the example requests directly the
|
||||||
`feature.node.kubenernetes.io/vendor-feature.enabled=true` node label to be
|
`vendor.io/feature.enabled=true` node label to be created.
|
||||||
created.
|
|
||||||
|
|
||||||
The `nfd.node.kubernetes.io/node-name=<node-name>` must be in place for each
|
The `nfd.node.kubernetes.io/node-name=<node-name>` must be in place for each
|
||||||
NodeFeature object as NFD uses it to determine the node which it is targeting.
|
NodeFeature object as NFD uses it to determine the node which it is targeting.
|
||||||
|
@ -130,7 +129,7 @@ spec:
|
||||||
rules:
|
rules:
|
||||||
- name: "my sample rule"
|
- name: "my sample rule"
|
||||||
labels:
|
labels:
|
||||||
"my-sample-feature": "true"
|
"feature.node.kubernetes.io/my-sample-feature": "true"
|
||||||
matchFeatures:
|
matchFeatures:
|
||||||
- feature: kernel.loadedmodule
|
- feature: kernel.loadedmodule
|
||||||
matchExpressions:
|
matchExpressions:
|
||||||
|
@ -141,7 +140,7 @@ spec:
|
||||||
```
|
```
|
||||||
|
|
||||||
It specifies one rule which creates node label
|
It specifies one rule which creates node label
|
||||||
`feature.node.kubenernetes.io/my-sample-feature=true` if both of the following
|
`feature.node.kubernetes.io/my-sample-feature=true` if both of the following
|
||||||
conditions are true (`matchFeatures` implements a logical AND over the
|
conditions are true (`matchFeatures` implements a logical AND over the
|
||||||
matchers):
|
matchers):
|
||||||
|
|
||||||
|
@ -216,9 +215,9 @@ having the following contents (or alternatively a shell script
|
||||||
following stdout output):
|
following stdout output):
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
my-feature.1
|
feature.node.kubernetes.io/my-feature.1
|
||||||
my-feature.2=myvalue
|
feature.node.kubernetes.io/my-feature.2=myvalue
|
||||||
my.namespace/my-feature.3=456
|
vendor.io/my-feature.3=456
|
||||||
```
|
```
|
||||||
|
|
||||||
This will translate into the following node labels:
|
This will translate into the following node labels:
|
||||||
|
@ -226,7 +225,7 @@ This will translate into the following node labels:
|
||||||
```yaml
|
```yaml
|
||||||
feature.node.kubernetes.io/my-feature.1: "true"
|
feature.node.kubernetes.io/my-feature.1: "true"
|
||||||
feature.node.kubernetes.io/my-feature.2: "myvalue"
|
feature.node.kubernetes.io/my-feature.2: "myvalue"
|
||||||
my.namespace/my-feature.3: "456"
|
vendor.io/my-feature.3: "456"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Feature files
|
### Feature files
|
||||||
|
@ -277,12 +276,12 @@ key-value pairs, separated by newlines:
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
# This is a comment
|
# This is a comment
|
||||||
<name>[=<value>]
|
<key>[=<value>]
|
||||||
```
|
```
|
||||||
|
|
||||||
The label value defaults to `true`, if not specified.
|
The label value defaults to `true`, if not specified.
|
||||||
|
|
||||||
Label namespace may be specified with `<namespace>/<name>[=<value>]`.
|
Label namespace must be specified with `<namespace>/<name>[=<value>]`.
|
||||||
|
|
||||||
> **NOTE:** The feature file size limit it 64kB. The feature file will be
|
> **NOTE:** The feature file size limit it 64kB. The feature file will be
|
||||||
> ignored if the size limit is exceeded.
|
> ignored if the size limit is exceeded.
|
||||||
|
@ -302,20 +301,20 @@ Considering the following file:
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
# +expiry-time=2012-07-28T11:22:33Z
|
# +expiry-time=2012-07-28T11:22:33Z
|
||||||
featureKey=featureValue
|
vendor.io/feature1=featureValue
|
||||||
|
|
||||||
# +expiry-time=2080-07-28T11:22:33Z
|
# +expiry-time=2080-07-28T11:22:33Z
|
||||||
featureKey2=featureValue2
|
vendor.io/feature2=featureValue2
|
||||||
|
|
||||||
# +expiry-time=2070-07-28T11:22:33Z
|
# +expiry-time=2070-07-28T11:22:33Z
|
||||||
featureKey3=featureValue3
|
vendor.io/feature3=featureValue3
|
||||||
|
|
||||||
# +expiry-time=2002-07-28T11:22:33Z
|
# +expiry-time=2002-07-28T11:22:33Z
|
||||||
featureKey4=featureValue4
|
vendor.io/feature4=featureValue4
|
||||||
```
|
```
|
||||||
|
|
||||||
After processing the above file, only `featureKey2` and `featureKey3` would be
|
After processing the above file, only `vendor.io/feature2` and
|
||||||
included in the list of accepted features.
|
`vendor.io/feature3` would be included in the list of accepted features.
|
||||||
|
|
||||||
> **NOTE:** The time format that we are supporting is RFC3339. Also, the `expiry-time`
|
> **NOTE:** The time format that we are supporting is RFC3339. Also, the `expiry-time`
|
||||||
> tag is only evaluated in each re-discovery period, and the expiration of
|
> tag is only evaluated in each re-discovery period, and the expiration of
|
||||||
|
@ -329,11 +328,12 @@ Considering the following file:
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
# +no-feature
|
# +no-feature
|
||||||
label-only=value
|
vendor.io/label-only=value
|
||||||
|
|
||||||
my-feature=value
|
vendor.io/my-feature=value
|
||||||
|
|
||||||
|
vendor.io/foo=bar
|
||||||
|
|
||||||
foo=bar
|
|
||||||
# +no-label
|
# +no-label
|
||||||
foo=baz
|
foo=baz
|
||||||
```
|
```
|
||||||
|
@ -343,19 +343,27 @@ Processing the above file would result in the following Features:
|
||||||
```yaml
|
```yaml
|
||||||
local.features:
|
local.features:
|
||||||
foo: baz
|
foo: baz
|
||||||
my-feature: value
|
vendor.io/my-feature: value
|
||||||
local.labels:
|
local.labels:
|
||||||
label-only: value
|
vendor.io/label-only: value
|
||||||
my-feature: value
|
vendor.io/my-feature: value
|
||||||
```
|
```
|
||||||
|
|
||||||
and the following labels added to the Node:
|
and the following labels added to the Node:
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
feature.node.kubernetes.io/label-only=value
|
vendor.io/label-only=value
|
||||||
feature.node.kubernetes.io/my-feature=value
|
vendor.io/my-feature=value
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **NOTE:** use of unprefixed label names (like `foo=bar`) should not be used.
|
||||||
|
> In NFD {{ site.version }} unprefixed names will be automatically prefixed
|
||||||
|
> with `feature.node.kubernetes.io/` but this will change in a future version
|
||||||
|
> (see
|
||||||
|
> [autoDefaultNs config option](../reference/master-configuration-reference.md#autoDefaultNs).
|
||||||
|
> Unprefixed names for plain Features (tagged with `# +no-label`) can be used
|
||||||
|
> without restrictions, however.
|
||||||
|
|
||||||
### Mounts
|
### Mounts
|
||||||
|
|
||||||
The standard NFD deployments contain `hostPath` mounts for
|
The standard NFD deployments contain `hostPath` mounts for
|
||||||
|
@ -395,7 +403,7 @@ sources:
|
||||||
custom:
|
custom:
|
||||||
- name: "my sample rule"
|
- name: "my sample rule"
|
||||||
labels:
|
labels:
|
||||||
"my-sample-feature": "true"
|
"feature.node.kubenernetes.io/my-sample-feature": "true"
|
||||||
matchFeatures:
|
matchFeatures:
|
||||||
- feature: kernel.loadedmodule
|
- feature: kernel.loadedmodule
|
||||||
matchExpressions:
|
matchExpressions:
|
||||||
|
@ -434,7 +442,7 @@ following content:
|
||||||
```yaml
|
```yaml
|
||||||
- name: "my e1000 rule"
|
- name: "my e1000 rule"
|
||||||
labels:
|
labels:
|
||||||
"e1000.present": "true"
|
"feature.node.kubenernetes.io/e1000.present": "true"
|
||||||
matchFeatures:
|
matchFeatures:
|
||||||
- feature: kernel.loadedmodule
|
- feature: kernel.loadedmodule
|
||||||
matchExpressions:
|
matchExpressions:
|
||||||
|
@ -459,8 +467,7 @@ Feature labels have the following format:
|
||||||
|
|
||||||
The namespace part (i.e. prefix) of the labels is controlled by nfd:
|
The namespace part (i.e. prefix) of the labels is controlled by nfd:
|
||||||
|
|
||||||
- All built-in labels use `feature.node.kubernetes.io`. This is also
|
- All built-in labels use `feature.node.kubernetes.io`.
|
||||||
the default for user defined features that don't specify any namespace.
|
|
||||||
- Namespaces may be excluded with the
|
- Namespaces may be excluded with the
|
||||||
[`-deny-label-ns`](../reference/master-commandline-reference.md#-deny-label-ns)
|
[`-deny-label-ns`](../reference/master-commandline-reference.md#-deny-label-ns)
|
||||||
command line flag of nfd-master
|
command line flag of nfd-master
|
||||||
|
@ -468,6 +475,9 @@ The namespace part (i.e. prefix) of the labels is controlled by nfd:
|
||||||
[`-extra-label-ns`](../reference/master-commandline-reference.md#-extra-label-ns)
|
[`-extra-label-ns`](../reference/master-commandline-reference.md#-extra-label-ns)
|
||||||
command line flag of nfd-master.
|
command line flag of nfd-master.
|
||||||
e.g: `nfd-master -deny-label-ns="*" -extra-label-ns=example.com`
|
e.g: `nfd-master -deny-label-ns="*" -extra-label-ns=example.com`
|
||||||
|
- Built-in default namespaces `feature.node.kubernetes.io` and
|
||||||
|
`profile.node.kubernetes.io` (and their sub-namespaces) are always allowed
|
||||||
|
and cannot be denied.
|
||||||
|
|
||||||
## Feature rule format
|
## Feature rule format
|
||||||
|
|
||||||
|
@ -485,7 +495,7 @@ Take this rule as a referential example:
|
||||||
```yaml
|
```yaml
|
||||||
- name: "my feature rule"
|
- name: "my feature rule"
|
||||||
labels:
|
labels:
|
||||||
"my-special-feature": "my-value"
|
"feature.node.kubernetes.io/my-special-feature": "my-value"
|
||||||
matchFeatures:
|
matchFeatures:
|
||||||
- feature: cpu.cpuid
|
- feature: cpu.cpuid
|
||||||
matchExpressions:
|
matchExpressions:
|
||||||
|
@ -500,7 +510,7 @@ Take this rule as a referential example:
|
||||||
class: {op: In, value: ["0200"]}
|
class: {op: In, value: ["0200"]}
|
||||||
```
|
```
|
||||||
|
|
||||||
This will yield `feature.node.kubenernetes.io/my-special-feature=my-value` node
|
This will yield `feature.node.kubernetes.io/my-special-feature=my-value` node
|
||||||
label if all of these are true (`matchFeatures` implements a logical AND over
|
label if all of these are true (`matchFeatures` implements a logical AND over
|
||||||
the matchers):
|
the matchers):
|
||||||
|
|
||||||
|
@ -529,8 +539,8 @@ spec:
|
||||||
rules:
|
rules:
|
||||||
- name: "my dynamic label value rule"
|
- name: "my dynamic label value rule"
|
||||||
labels:
|
labels:
|
||||||
linux-lsm-enabled: "@kernel.config.LSM"
|
feature.node.kubernetes.io/linux-lsm-enabled: "@kernel.config.LSM"
|
||||||
custom-label: "customlabel"
|
feature.node.kubernetes.io/custom-label: "customlabel"
|
||||||
```
|
```
|
||||||
|
|
||||||
Label `linux-lsm-enabled` uses the `@` notation for dynamic values.
|
Label `linux-lsm-enabled` uses the `@` notation for dynamic values.
|
||||||
|
@ -576,8 +586,7 @@ spec:
|
||||||
rules:
|
rules:
|
||||||
- name: "annotation-example"
|
- name: "annotation-example"
|
||||||
annotations:
|
annotations:
|
||||||
defaul-ns-annotation: "foo"
|
feature.node.kubernetes.io/defaul-ns-annotation: "foo"
|
||||||
feature.node.kubernetes.io/defaul-ns-annotation-2: "bar"
|
|
||||||
custom.vendor.io/feature: "baz"
|
custom.vendor.io/feature: "baz"
|
||||||
matchFeatures:
|
matchFeatures:
|
||||||
- feature: kernel.version
|
- feature: kernel.version
|
||||||
|
@ -591,7 +600,6 @@ This will yield into the following node annotations:
|
||||||
annotations:
|
annotations:
|
||||||
...
|
...
|
||||||
feature.node.kubernetes.io/defaul-ns-annotation: "foo"
|
feature.node.kubernetes.io/defaul-ns-annotation: "foo"
|
||||||
feature.node.kubernetes.io/defaul-ns-annotation-2: "bar"
|
|
||||||
custom.vendor.io/feature: "baz"
|
custom.vendor.io/feature: "baz"
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
@ -602,8 +610,10 @@ NFD enforces some limitations to the namespace (or prefix)/ of the annotations:
|
||||||
generally be used
|
generally be used
|
||||||
- the only exception is `feature.node.kubernetes.io/` and its sub-namespaces
|
- the only exception is `feature.node.kubernetes.io/` and its sub-namespaces
|
||||||
(like `sub.ns.feature.node.kubernetes.io`)
|
(like `sub.ns.feature.node.kubernetes.io`)
|
||||||
- unprefixed names will get prefixed with `feature.node.kubernetes.io/`
|
- unprefixed names (like `my-annotation`) should not be used. In NFD {{
|
||||||
automatically (e.g. `foo` becomes `feature.node.kubernetes.io/foo`)
|
site.version }} unprefixed names will be automatically prefixed with
|
||||||
|
`feature.node.kubernetes.io/` but this will change in a future version (see
|
||||||
|
[autoDefaultNs config option](../reference/master-configuration-reference.md#autoDefaultNs).
|
||||||
|
|
||||||
> **NOTE:** The `annotations` field has will only advertise features via node
|
> **NOTE:** The `annotations` field has will only advertise features via node
|
||||||
> annotations the features won't be advertised as node labels unless they are
|
> annotations the features won't be advertised as node labels unless they are
|
||||||
|
@ -719,8 +729,10 @@ Resources names:
|
||||||
generally be used
|
generally be used
|
||||||
- the only exception is `feature.node.kubernetes.io/` and its sub-namespaces
|
- the only exception is `feature.node.kubernetes.io/` and its sub-namespaces
|
||||||
(like `sub.ns.feature.node.kubernetes.io`)
|
(like `sub.ns.feature.node.kubernetes.io`)
|
||||||
- unprefixed names will get prefixed with `feature.node.kubernetes.io/`
|
- unprefixed names (like `my-er`) site.version }} unprefixed names will be
|
||||||
automatically (e.g. `foo` becomes `feature.node.kubernetes.io/foo`)
|
automatically prefixed with `feature.node.kubernetes.io/` but this will
|
||||||
|
change in a future version (see
|
||||||
|
[autoDefaultNs config option](../reference/master-configuration-reference.md#autoDefaultNs).
|
||||||
|
|
||||||
> **NOTE:** `.extendedResources` is not supported by the
|
> **NOTE:** `.extendedResources` is not supported by the
|
||||||
> [custom feature source](#custom-feature-source) -- it can only be used in
|
> [custom feature source](#custom-feature-source) -- it can only be used in
|
||||||
|
|
|
@ -6,7 +6,7 @@ spec:
|
||||||
rules:
|
rules:
|
||||||
- name: "my sample rule"
|
- name: "my sample rule"
|
||||||
labels:
|
labels:
|
||||||
"my-sample-feature": "true"
|
"vendor.io/my-sample-feature": "true"
|
||||||
matchFeatures:
|
matchFeatures:
|
||||||
- feature: kernel.loadedmodule
|
- feature: kernel.loadedmodule
|
||||||
matchExpressions:
|
matchExpressions:
|
||||||
|
@ -28,4 +28,4 @@ spec:
|
||||||
matchFeatures:
|
matchFeatures:
|
||||||
- feature: kernel.version
|
- feature: kernel.version
|
||||||
matchExpressions:
|
matchExpressions:
|
||||||
major: {op: Exists}
|
major: {op: Exists}
|
||||||
|
|
|
@ -370,12 +370,12 @@ func TestSetLabels(t *testing.T) {
|
||||||
mockNode := newMockNode()
|
mockNode := newMockNode()
|
||||||
mockCtx := context.Background()
|
mockCtx := context.Background()
|
||||||
// In the gRPC request the label names may omit the default ns
|
// In the gRPC request the label names may omit the default ns
|
||||||
mockLabels := map[string]string{"feature-1": "1", "feature-2": "val-2", "feature-3": "3"}
|
mockLabels := map[string]string{"feature.node.kubernetes.io/feature-1": "1", "example.io/feature-2": "val-2", "feature.node.kubernetes.io/feature-3": "3"}
|
||||||
mockReq := &labeler.SetLabelsRequest{NodeName: workerName, NfdVersion: workerVer, Labels: mockLabels}
|
mockReq := &labeler.SetLabelsRequest{NodeName: workerName, NfdVersion: workerVer, Labels: mockLabels}
|
||||||
|
|
||||||
mockLabelNames := make([]string, 0, len(mockLabels))
|
mockLabelNames := make([]string, 0, len(mockLabels))
|
||||||
for k := range mockLabels {
|
for k := range mockLabels {
|
||||||
mockLabelNames = append(mockLabelNames, k)
|
mockLabelNames = append(mockLabelNames, strings.TrimPrefix(k, nfdv1alpha1.FeatureLabelNs+"/"))
|
||||||
}
|
}
|
||||||
sort.Strings(mockLabelNames)
|
sort.Strings(mockLabelNames)
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ func TestSetLabels(t *testing.T) {
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, strings.Join(mockLabelNames, ",")),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, strings.Join(mockLabelNames, ",")),
|
||||||
}
|
}
|
||||||
for k, v := range mockLabels {
|
for k, v := range mockLabels {
|
||||||
expectedPatches = append(expectedPatches, apihelper.NewJsonPatch("add", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/"+k, v))
|
expectedPatches = append(expectedPatches, apihelper.NewJsonPatch("add", "/metadata/labels", k, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
|
@ -401,8 +401,8 @@ func TestSetLabels(t *testing.T) {
|
||||||
|
|
||||||
Convey("When -label-whitelist is specified", func() {
|
Convey("When -label-whitelist is specified", func() {
|
||||||
expectedPatches := []apihelper.JsonPatch{
|
expectedPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "feature-2"),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/feature-2", mockLabels["feature-2"]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockMaster.config.LabelWhiteList.Regexp = *regexp.MustCompile("^f.*2$")
|
mockMaster.config.LabelWhiteList.Regexp = *regexp.MustCompile("^f.*2$")
|
||||||
|
@ -460,13 +460,13 @@ func TestSetLabels(t *testing.T) {
|
||||||
|
|
||||||
Convey("When -resource-labels is specified", func() {
|
Convey("When -resource-labels is specified", func() {
|
||||||
expectedPatches := []apihelper.JsonPatch{
|
expectedPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "feature-2"),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.ExtendedResourceAnnotation, "feature-1,feature-3"),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.ExtendedResourceAnnotation, "feature-1,feature-3"),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/feature-2", mockLabels["feature-2"]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
|
||||||
}
|
}
|
||||||
expectedStatusPatches := []apihelper.JsonPatch{
|
expectedStatusPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/status/capacity", nfdv1alpha1.FeatureLabelNs+"/feature-1", mockLabels["feature-1"]),
|
apihelper.NewJsonPatch("add", "/status/capacity", "feature.node.kubernetes.io/feature-1", mockLabels["feature.node.kubernetes.io/feature-1"]),
|
||||||
apihelper.NewJsonPatch("add", "/status/capacity", nfdv1alpha1.FeatureLabelNs+"/feature-3", mockLabels["feature-3"]),
|
apihelper.NewJsonPatch("add", "/status/capacity", "feature.node.kubernetes.io/feature-3", mockLabels["feature.node.kubernetes.io/feature-3"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockMaster.config.ResourceLabels = map[string]struct{}{"feature.node.kubernetes.io/feature-3": {}, "feature-1": {}}
|
mockMaster.config.ResourceLabels = map[string]struct{}{"feature.node.kubernetes.io/feature-3": {}, "feature-1": {}}
|
||||||
|
@ -502,29 +502,92 @@ func TestSetLabels(t *testing.T) {
|
||||||
func TestFilterLabels(t *testing.T) {
|
func TestFilterLabels(t *testing.T) {
|
||||||
mockHelper := &apihelper.MockAPIHelpers{}
|
mockHelper := &apihelper.MockAPIHelpers{}
|
||||||
mockMaster := newMockMaster(mockHelper)
|
mockMaster := newMockMaster(mockHelper)
|
||||||
|
mockMaster.deniedNs = deniedNs{
|
||||||
|
normal: map[string]struct{}{"": struct{}{}, "kubernetes.io": struct{}{}, "denied.ns": struct{}{}},
|
||||||
|
wildcard: map[string]struct{}{".kubernetes.io": struct{}{}, ".denied.subns": struct{}{}},
|
||||||
|
}
|
||||||
|
|
||||||
Convey("When using dynamic values", t, func() {
|
type TC struct {
|
||||||
labelName := "ns/testLabel"
|
description string
|
||||||
labelValue := "@test.feature.LSM"
|
labelName string
|
||||||
features := nfdv1alpha1.Features{
|
labelValue string
|
||||||
Attributes: map[string]nfdv1alpha1.AttributeFeatureSet{
|
features nfdv1alpha1.Features
|
||||||
"test.feature": nfdv1alpha1.AttributeFeatureSet{
|
expectErr bool
|
||||||
Elements: map[string]string{
|
expectedValue string
|
||||||
"LSM": "123",
|
}
|
||||||
|
|
||||||
|
tcs := []TC{
|
||||||
|
TC{
|
||||||
|
description: "Static value",
|
||||||
|
labelName: "example.io/test",
|
||||||
|
labelValue: "test-val",
|
||||||
|
expectedValue: "test-val",
|
||||||
|
},
|
||||||
|
TC{
|
||||||
|
description: "Dynamic value",
|
||||||
|
labelName: "example.io/testLabel",
|
||||||
|
labelValue: "@test.feature.LSM",
|
||||||
|
features: nfdv1alpha1.Features{
|
||||||
|
Attributes: map[string]nfdv1alpha1.AttributeFeatureSet{
|
||||||
|
"test.feature": nfdv1alpha1.AttributeFeatureSet{
|
||||||
|
Elements: map[string]string{
|
||||||
|
"LSM": "123",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
expectedValue: "123",
|
||||||
labelValue, err := mockMaster.filterFeatureLabel(labelName, labelValue, &features)
|
},
|
||||||
|
TC{
|
||||||
|
description: "Unprefixed should be denied",
|
||||||
|
labelName: "test-label",
|
||||||
|
labelValue: "test-value",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
TC{
|
||||||
|
description: "kubernetes.io ns should be denied",
|
||||||
|
labelName: "kubernetes.io/test-label",
|
||||||
|
labelValue: "test-value",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
TC{
|
||||||
|
description: "*.kubernetes.io ns should be denied",
|
||||||
|
labelName: "sub.ns.kubernetes.io/test-label",
|
||||||
|
labelValue: "test-value",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
TC{
|
||||||
|
description: "denied.ns ns should be denied",
|
||||||
|
labelName: "denied.ns/test-label",
|
||||||
|
labelValue: "test-value",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
TC{
|
||||||
|
description: "*.denied.subns ns should be denied",
|
||||||
|
labelName: "my.denied.subns/test-label",
|
||||||
|
labelValue: "test-value",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
Convey("Operation should succeed", func() {
|
for _, tc := range tcs {
|
||||||
So(err, ShouldBeNil)
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
})
|
labelValue, err := mockMaster.filterFeatureLabel(tc.labelName, tc.labelValue, &tc.features)
|
||||||
|
|
||||||
Convey("Label value should change", func() {
|
if tc.expectErr {
|
||||||
So(labelValue, ShouldEqual, "123")
|
Convey("Label should be filtered out", t, func() {
|
||||||
|
So(err, ShouldBeError)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Convey("Label should not be filtered out", t, func() {
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
Convey("Label value should be correct", t, func() {
|
||||||
|
So(labelValue, ShouldEqual, tc.expectedValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreatePatches(t *testing.T) {
|
func TestCreatePatches(t *testing.T) {
|
||||||
|
|
|
@ -71,6 +71,7 @@ type Annotations map[string]string
|
||||||
|
|
||||||
// NFDConfig contains the configuration settings of NfdMaster.
|
// NFDConfig contains the configuration settings of NfdMaster.
|
||||||
type NFDConfig struct {
|
type NFDConfig struct {
|
||||||
|
AutoDefaultNs bool
|
||||||
DenyLabelNs utils.StringSetVal
|
DenyLabelNs utils.StringSetVal
|
||||||
ExtraLabelNs utils.StringSetVal
|
ExtraLabelNs utils.StringSetVal
|
||||||
LabelWhiteList utils.RegexpVal
|
LabelWhiteList utils.RegexpVal
|
||||||
|
@ -196,6 +197,7 @@ func newDefaultConfig() *NFDConfig {
|
||||||
DenyLabelNs: utils.StringSetVal{},
|
DenyLabelNs: utils.StringSetVal{},
|
||||||
ExtraLabelNs: utils.StringSetVal{},
|
ExtraLabelNs: utils.StringSetVal{},
|
||||||
NoPublish: false,
|
NoPublish: false,
|
||||||
|
AutoDefaultNs: true,
|
||||||
NfdApiParallelism: 10,
|
NfdApiParallelism: 10,
|
||||||
ResourceLabels: utils.StringSetVal{},
|
ResourceLabels: utils.StringSetVal{},
|
||||||
EnableTaints: false,
|
EnableTaints: false,
|
||||||
|
@ -551,7 +553,6 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels, features *nfdv1alpha1.Fea
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *nfdMaster) filterFeatureLabel(name, value string, features *nfdv1alpha1.Features) (string, error) {
|
func (m *nfdMaster) filterFeatureLabel(name, value string, features *nfdv1alpha1.Features) (string, error) {
|
||||||
|
|
||||||
//Validate label name
|
//Validate label name
|
||||||
if errs := k8svalidation.IsQualifiedName(name); len(errs) > 0 {
|
if errs := k8svalidation.IsQualifiedName(name); len(errs) > 0 {
|
||||||
return "", fmt.Errorf("invalid name %q: %s", name, strings.Join(errs, "; "))
|
return "", fmt.Errorf("invalid name %q: %s", name, strings.Join(errs, "; "))
|
||||||
|
@ -763,10 +764,14 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error {
|
||||||
// NOTE: changing the rule api to support handle multiple objects instead
|
// NOTE: changing the rule api to support handle multiple objects instead
|
||||||
// of merging would probably perform better with lot less data to copy.
|
// of merging would probably perform better with lot less data to copy.
|
||||||
features = objs[0].Spec.DeepCopy()
|
features = objs[0].Spec.DeepCopy()
|
||||||
features.Labels = addNsToMapKeys(features.Labels, nfdv1alpha1.FeatureLabelNs)
|
if m.config.AutoDefaultNs {
|
||||||
|
features.Labels = addNsToMapKeys(features.Labels, nfdv1alpha1.FeatureLabelNs)
|
||||||
|
}
|
||||||
for _, o := range objs[1:] {
|
for _, o := range objs[1:] {
|
||||||
s := o.Spec.DeepCopy()
|
s := o.Spec.DeepCopy()
|
||||||
s.Labels = addNsToMapKeys(s.Labels, nfdv1alpha1.FeatureLabelNs)
|
if m.config.AutoDefaultNs {
|
||||||
|
s.Labels = addNsToMapKeys(s.Labels, nfdv1alpha1.FeatureLabelNs)
|
||||||
|
}
|
||||||
s.MergeInto(features)
|
s.MergeInto(features)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -789,7 +794,7 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error {
|
||||||
|
|
||||||
// filterExtendedResources filters extended resources and returns a map
|
// filterExtendedResources filters extended resources and returns a map
|
||||||
// of valid extended resources.
|
// of valid extended resources.
|
||||||
func filterExtendedResources(features *nfdv1alpha1.Features, extendedResources ExtendedResources) ExtendedResources {
|
func (m *nfdMaster) filterExtendedResources(features *nfdv1alpha1.Features, extendedResources ExtendedResources) ExtendedResources {
|
||||||
outExtendedResources := ExtendedResources{}
|
outExtendedResources := ExtendedResources{}
|
||||||
for name, value := range extendedResources {
|
for name, value := range extendedResources {
|
||||||
capacity, err := filterExtendedResource(name, value, features)
|
capacity, err := filterExtendedResource(name, value, features)
|
||||||
|
@ -836,7 +841,11 @@ func filterExtendedResource(name, value string, features *nfdv1alpha1.Features)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName string, labels map[string]string, features *nfdv1alpha1.Features) error {
|
func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName string, labels map[string]string, features *nfdv1alpha1.Features) error {
|
||||||
labels = addNsToMapKeys(labels, nfdv1alpha1.FeatureLabelNs)
|
if m.config.AutoDefaultNs {
|
||||||
|
labels = addNsToMapKeys(labels, nfdv1alpha1.FeatureLabelNs)
|
||||||
|
} else if labels == nil {
|
||||||
|
labels = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(nodeName, features)
|
crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(nodeName, features)
|
||||||
|
|
||||||
|
@ -853,7 +862,7 @@ func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName stri
|
||||||
for k, v := range crExtendedResources {
|
for k, v := range crExtendedResources {
|
||||||
extendedResources[k] = v
|
extendedResources[k] = v
|
||||||
}
|
}
|
||||||
extendedResources = filterExtendedResources(features, extendedResources)
|
extendedResources = m.filterExtendedResources(features, extendedResources)
|
||||||
|
|
||||||
// Annotations
|
// Annotations
|
||||||
annotations := m.filterFeatureAnnotations(crAnnotations)
|
annotations := m.filterFeatureAnnotations(crAnnotations)
|
||||||
|
@ -1011,13 +1020,22 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
taints = append(taints, ruleOut.Taints...)
|
taints = append(taints, ruleOut.Taints...)
|
||||||
for k, v := range addNsToMapKeys(ruleOut.Labels, nfdv1alpha1.FeatureLabelNs) {
|
|
||||||
|
l := ruleOut.Labels
|
||||||
|
e := ruleOut.ExtendedResources
|
||||||
|
a := ruleOut.Annotations
|
||||||
|
if m.config.AutoDefaultNs {
|
||||||
|
l = addNsToMapKeys(ruleOut.Labels, nfdv1alpha1.FeatureLabelNs)
|
||||||
|
e = addNsToMapKeys(ruleOut.ExtendedResources, nfdv1alpha1.ExtendedResourceNs)
|
||||||
|
a = addNsToMapKeys(ruleOut.Annotations, nfdv1alpha1.FeatureAnnotationNs)
|
||||||
|
}
|
||||||
|
for k, v := range l {
|
||||||
labels[k] = v
|
labels[k] = v
|
||||||
}
|
}
|
||||||
for k, v := range addNsToMapKeys(ruleOut.ExtendedResources, nfdv1alpha1.ExtendedResourceNs) {
|
for k, v := range e {
|
||||||
extendedResources[k] = v
|
extendedResources[k] = v
|
||||||
}
|
}
|
||||||
for k, v := range addNsToMapKeys(ruleOut.Annotations, nfdv1alpha1.FeatureAnnotationNs) {
|
for k, v := range a {
|
||||||
annotations[k] = v
|
annotations[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1430,9 +1448,13 @@ func (m *nfdMaster) filterFeatureAnnotations(annotations map[string]string) map[
|
||||||
}
|
}
|
||||||
// Check annotation namespace, filter out if ns is not whitelisted
|
// Check annotation namespace, filter out if ns is not whitelisted
|
||||||
if ns != nfdv1alpha1.FeatureAnnotationNs && !strings.HasSuffix(ns, nfdv1alpha1.FeatureAnnotationSubNsSuffix) {
|
if ns != nfdv1alpha1.FeatureAnnotationNs && !strings.HasSuffix(ns, nfdv1alpha1.FeatureAnnotationSubNsSuffix) {
|
||||||
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
// If the namespace is denied the annotation will be ignored
|
||||||
|
if ns == "" {
|
||||||
|
klog.ErrorS(fmt.Errorf("labels without namespace (prefix/) not allowed"), fmt.Sprintf("Ignoring annotation %s", annotation))
|
||||||
|
continue
|
||||||
|
}
|
||||||
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") || ns == nfdv1alpha1.AnnotationNs {
|
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") || ns == nfdv1alpha1.AnnotationNs {
|
||||||
klog.ErrorS(fmt.Errorf("namespace %v is not allowed", ns), fmt.Sprintf("Ignoring annotation %v\n", annotation))
|
klog.ErrorS(fmt.Errorf("namespace %q is not allowed", ns), fmt.Sprintf("Ignoring annotation %s", annotation))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/vektra/errors"
|
"github.com/vektra/errors"
|
||||||
|
|
||||||
|
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||||
"sigs.k8s.io/node-feature-discovery/pkg/labeler"
|
"sigs.k8s.io/node-feature-discovery/pkg/labeler"
|
||||||
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
||||||
"sigs.k8s.io/node-feature-discovery/source"
|
"sigs.k8s.io/node-feature-discovery/source"
|
||||||
|
@ -84,7 +85,7 @@ func makeFakeFeatures(names []string) (source.FeatureLabels, Labels) {
|
||||||
labels := Labels{}
|
labels := Labels{}
|
||||||
for _, f := range names {
|
for _, f := range names {
|
||||||
features[f] = true
|
features[f] = true
|
||||||
labelName := fakeLabelSourceName + "-" + f
|
labelName := nfdv1alpha1.FeatureLabelNs + "/" + fakeLabelSourceName + "-" + f
|
||||||
if strings.IndexByte(f, '/') >= 0 {
|
if strings.IndexByte(f, '/') >= 0 {
|
||||||
labelName = f
|
labelName = f
|
||||||
}
|
}
|
||||||
|
@ -352,9 +353,9 @@ func TestCreateFeatureLabels(t *testing.T) {
|
||||||
|
|
||||||
Convey("Proper fake labels are returned", func() {
|
Convey("Proper fake labels are returned", func() {
|
||||||
So(len(labels), ShouldEqual, 3)
|
So(len(labels), ShouldEqual, 3)
|
||||||
So(labels, ShouldContainKey, "fake-fakefeature1")
|
So(labels, ShouldContainKey, nfdv1alpha1.FeatureLabelNs+"/"+"fake-fakefeature1")
|
||||||
So(labels, ShouldContainKey, "fake-fakefeature2")
|
So(labels, ShouldContainKey, nfdv1alpha1.FeatureLabelNs+"/"+"fake-fakefeature2")
|
||||||
So(labels, ShouldContainKey, "fake-fakefeature3")
|
So(labels, ShouldContainKey, nfdv1alpha1.FeatureLabelNs+"/"+"fake-fakefeature3")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Convey("When fake feature source is configured with a whitelist that doesn't match", func() {
|
Convey("When fake feature source is configured with a whitelist that doesn't match", func() {
|
||||||
|
|
|
@ -580,34 +580,29 @@ func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) (
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefix for labels in the default namespace
|
|
||||||
prefix := source.Name() + "-"
|
|
||||||
switch source.Name() {
|
|
||||||
case "local", "custom":
|
|
||||||
// Do not prefix labels from the custom rules, hooks or feature files
|
|
||||||
prefix = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range features {
|
for k, v := range features {
|
||||||
// Split label name into namespace and name compoents. Use dummy 'ns'
|
name := k
|
||||||
// default namespace because there is no function to validate just
|
switch sourceName := source.Name(); sourceName {
|
||||||
// the name part
|
case "local", "custom":
|
||||||
split := strings.SplitN(k, "/", 2)
|
// No mangling of labels from the custom rules, hooks or feature files
|
||||||
|
default:
|
||||||
label := prefix + split[0]
|
// Prefix for labels from other sources
|
||||||
nameForValidation := "ns/" + label
|
if !strings.Contains(name, "/") {
|
||||||
nameForWhiteListing := label
|
name = nfdv1alpha1.FeatureLabelNs + "/" + sourceName + "-" + name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Split label name into namespace and name compoents
|
||||||
|
split := strings.SplitN(name, "/", 2)
|
||||||
|
|
||||||
|
nameForWhiteListing := name
|
||||||
if len(split) == 2 {
|
if len(split) == 2 {
|
||||||
label = k
|
|
||||||
nameForValidation = label
|
|
||||||
nameForWhiteListing = split[1]
|
nameForWhiteListing = split[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate label name.
|
// Validate label name.
|
||||||
errs := validation.IsQualifiedName(nameForValidation)
|
errs := validation.IsQualifiedName(name)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
klog.InfoS("ignoring label with invalid name", "lableKey", label, "errors", errs)
|
klog.InfoS("ignoring label with invalid name", "lableKey", name, "errors", errs)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,7 +610,7 @@ func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) (
|
||||||
// Validate label value
|
// Validate label value
|
||||||
errs = validation.IsValidLabelValue(value)
|
errs = validation.IsValidLabelValue(value)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
klog.InfoS("ignoring label with invalide value", "labelKey", label, "labelValue", value, "errors", errs)
|
klog.InfoS("ignoring label with invalide value", "labelKey", name, "labelValue", value, "errors", errs)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,7 +620,7 @@ func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) (
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
labels[label] = value
|
labels[name] = value
|
||||||
}
|
}
|
||||||
return labels, nil
|
return labels, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue