mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
Merge pull request #1084 from AhmedGrati/feat-add-master-config-file
feat: add master config file
This commit is contained in:
commit
193c552b33
28 changed files with 739 additions and 90 deletions
6
Makefile
6
Makefile
|
@ -120,8 +120,13 @@ templates:
|
||||||
@# Need to prepend each line in the sample config with spaces in order to
|
@# Need to prepend each line in the sample config with spaces in order to
|
||||||
@# fit correctly in the configmap spec.
|
@# fit correctly in the configmap spec.
|
||||||
@sed s'/^/ /' deployment/components/worker-config/nfd-worker.conf.example > nfd-worker.conf.tmp
|
@sed s'/^/ /' deployment/components/worker-config/nfd-worker.conf.example > nfd-worker.conf.tmp
|
||||||
|
@sed s'/^/ /' deployment/components/master-config/nfd-master.conf.example > nfd-master.conf.tmp
|
||||||
@sed s'/^/ /' deployment/components/topology-updater-config/nfd-topology-updater.conf.example > nfd-topology-updater.conf.tmp
|
@sed s'/^/ /' deployment/components/topology-updater-config/nfd-topology-updater.conf.example > nfd-topology-updater.conf.tmp
|
||||||
@# The sed magic below replaces the block of text between the lines with start and end markers
|
@# The sed magic below replaces the block of text between the lines with start and end markers
|
||||||
|
@start=NFD-MASTER-CONF-START-DO-NOT-REMOVE; \
|
||||||
|
end=NFD-MASTER-CONF-END-DO-NOT-REMOVE; \
|
||||||
|
sed -e "/$$start/,/$$end/{ /$$start/{ p; r nfd-master.conf.tmp" \
|
||||||
|
-e "}; /$$end/p; d }" -i deployment/helm/node-feature-discovery/values.yaml
|
||||||
@start=NFD-WORKER-CONF-START-DO-NOT-REMOVE; \
|
@start=NFD-WORKER-CONF-START-DO-NOT-REMOVE; \
|
||||||
end=NFD-WORKER-CONF-END-DO-NOT-REMOVE; \
|
end=NFD-WORKER-CONF-END-DO-NOT-REMOVE; \
|
||||||
sed -e "/$$start/,/$$end/{ /$$start/{ p; r nfd-worker.conf.tmp" \
|
sed -e "/$$start/,/$$end/{ /$$start/{ p; r nfd-worker.conf.tmp" \
|
||||||
|
@ -130,6 +135,7 @@ templates:
|
||||||
end=NFD-TOPOLOGY-UPDATER-CONF-END-DO-NOT-REMOVE; \
|
end=NFD-TOPOLOGY-UPDATER-CONF-END-DO-NOT-REMOVE; \
|
||||||
sed -e "/$$start/,/$$end/{ /$$start/{ p; r nfd-topology-updater.conf.tmp" \
|
sed -e "/$$start/,/$$end/{ /$$start/{ p; r nfd-topology-updater.conf.tmp" \
|
||||||
-e "}; /$$end/p; d }" -i deployment/helm/node-feature-discovery/values.yaml
|
-e "}; /$$end/p; d }" -i deployment/helm/node-feature-discovery/values.yaml
|
||||||
|
@rm nfd-master.conf.tmp
|
||||||
@rm nfd-worker.conf.tmp
|
@rm nfd-worker.conf.tmp
|
||||||
@rm nfd-topology-updater.conf.tmp
|
@rm nfd-topology-updater.conf.tmp
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ func main() {
|
||||||
|
|
||||||
printVersion := flags.Bool("version", false, "Print version and exit.")
|
printVersion := flags.Bool("version", false, "Print version and exit.")
|
||||||
|
|
||||||
args := initFlags(flags)
|
args, overrides := initFlags(flags)
|
||||||
// Inject klog flags
|
// Inject klog flags
|
||||||
klog.InitFlags(flags)
|
klog.InitFlags(flags)
|
||||||
|
|
||||||
|
@ -55,6 +54,18 @@ func main() {
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "featurerules-controller":
|
case "featurerules-controller":
|
||||||
klog.Warningf("-featurerules-controller is deprecated, use '-crd-controller' flag instead")
|
klog.Warningf("-featurerules-controller is deprecated, use '-crd-controller' flag instead")
|
||||||
|
case "extra-label-ns":
|
||||||
|
args.Overrides.ExtraLabelNs = overrides.ExtraLabelNs
|
||||||
|
case "deny-label-ns":
|
||||||
|
args.Overrides.DenyLabelNs = overrides.DenyLabelNs
|
||||||
|
case "label-whitelist":
|
||||||
|
args.Overrides.LabelWhiteList = overrides.LabelWhiteList
|
||||||
|
case "resource-labels":
|
||||||
|
args.Overrides.ResourceLabels = overrides.ResourceLabels
|
||||||
|
case "enable-taints":
|
||||||
|
args.Overrides.EnableTaints = overrides.EnableTaints
|
||||||
|
case "no-publish":
|
||||||
|
args.Overrides.NoPublish = overrides.NoPublish
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -82,35 +93,23 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFlags(flagset *flag.FlagSet) *master.Args {
|
func initFlags(flagset *flag.FlagSet) (*master.Args, *master.ConfigOverrideArgs) {
|
||||||
args := &master.Args{
|
args := &master.Args{}
|
||||||
LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")},
|
|
||||||
DenyLabelNs: map[string]struct{}{"*.kubernetes.io": {}},
|
|
||||||
}
|
|
||||||
|
|
||||||
flagset.StringVar(&args.CaFile, "ca-file", "",
|
flagset.StringVar(&args.CaFile, "ca-file", "",
|
||||||
"Root certificate for verifying connections")
|
"Root certificate for verifying connections")
|
||||||
flagset.StringVar(&args.CertFile, "cert-file", "",
|
flagset.StringVar(&args.CertFile, "cert-file", "",
|
||||||
"Certificate used for authenticating connections")
|
"Certificate used for authenticating connections")
|
||||||
flagset.Var(&args.DenyLabelNs, "deny-label-ns",
|
|
||||||
"Comma separated list of denied label namespaces")
|
|
||||||
flagset.Var(&args.ExtraLabelNs, "extra-label-ns",
|
|
||||||
"Comma separated list of allowed extra label namespaces")
|
|
||||||
flagset.StringVar(&args.Instance, "instance", "",
|
flagset.StringVar(&args.Instance, "instance", "",
|
||||||
"Instance name. Used to separate annotation namespaces for multiple parallel deployments.")
|
"Instance name. Used to separate annotation namespaces for multiple parallel deployments.")
|
||||||
flagset.StringVar(&args.KeyFile, "key-file", "",
|
flagset.StringVar(&args.KeyFile, "key-file", "",
|
||||||
"Private key matching -cert-file")
|
"Private key matching -cert-file")
|
||||||
|
flagset.StringVar(&args.ConfigFile, "config", "/etc/kubernetes/node-feature-discovery/nfd-master.conf",
|
||||||
|
"Config file to use.")
|
||||||
flagset.StringVar(&args.Kubeconfig, "kubeconfig", "",
|
flagset.StringVar(&args.Kubeconfig, "kubeconfig", "",
|
||||||
"Kubeconfig to use")
|
"Kubeconfig to use")
|
||||||
flagset.Var(&args.LabelWhiteList, "label-whitelist",
|
|
||||||
"Regular expression to filter label names to publish to the Kubernetes API server. "+
|
|
||||||
"NB: the label namespace is omitted i.e. the filter is only applied to the name part after '/'.")
|
|
||||||
flagset.BoolVar(&args.EnableNodeFeatureApi, "enable-nodefeature-api", false,
|
flagset.BoolVar(&args.EnableNodeFeatureApi, "enable-nodefeature-api", false,
|
||||||
"Enable the NodeFeature CRD API for receiving node features. This will automatically disable the gRPC communication.")
|
"Enable the NodeFeature CRD API for receiving node features. This will automatically disable the gRPC communication.")
|
||||||
flagset.BoolVar(&args.NoPublish, "no-publish", false,
|
|
||||||
"Do not publish feature labels")
|
|
||||||
flagset.BoolVar(&args.EnableTaints, "enable-taints", false,
|
|
||||||
"Enable node tainting feature")
|
|
||||||
flagset.BoolVar(&args.CrdController, "featurerules-controller", true,
|
flagset.BoolVar(&args.CrdController, "featurerules-controller", true,
|
||||||
"Enable NFD CRD API controller. DEPRECATED: use -crd-controller instead")
|
"Enable NFD CRD API controller. DEPRECATED: use -crd-controller instead")
|
||||||
flagset.BoolVar(&args.CrdController, "crd-controller", true,
|
flagset.BoolVar(&args.CrdController, "crd-controller", true,
|
||||||
|
@ -119,11 +118,32 @@ func initFlags(flagset *flag.FlagSet) *master.Args {
|
||||||
"Port on which to listen for connections.")
|
"Port on which to listen for connections.")
|
||||||
flagset.BoolVar(&args.Prune, "prune", false,
|
flagset.BoolVar(&args.Prune, "prune", false,
|
||||||
"Prune all NFD related attributes from all nodes of the cluaster and exit.")
|
"Prune all NFD related attributes from all nodes of the cluaster and exit.")
|
||||||
flagset.Var(&args.ResourceLabels, "resource-labels",
|
|
||||||
"Comma separated list of labels to be exposed as extended resources.")
|
|
||||||
flagset.BoolVar(&args.VerifyNodeName, "verify-node-name", false,
|
flagset.BoolVar(&args.VerifyNodeName, "verify-node-name", false,
|
||||||
"Verify worker node name against the worker's TLS certificate. "+
|
"Verify worker node name against the worker's TLS certificate. "+
|
||||||
"Only takes effect when TLS authentication has been enabled.")
|
"Only takes effect when TLS authentication has been enabled.")
|
||||||
|
flagset.StringVar(&args.Options, "options", "",
|
||||||
|
"Specify config options from command line. Config options are specified "+
|
||||||
|
"in the same format as in the config file (i.e. json or yaml). These options")
|
||||||
|
|
||||||
return args
|
overrides := &master.ConfigOverrideArgs{
|
||||||
|
LabelWhiteList: &utils.RegexpVal{},
|
||||||
|
DenyLabelNs: &utils.StringSetVal{},
|
||||||
|
ExtraLabelNs: &utils.StringSetVal{},
|
||||||
|
ResourceLabels: &utils.StringSetVal{},
|
||||||
|
}
|
||||||
|
flagset.Var(overrides.ExtraLabelNs, "extra-label-ns",
|
||||||
|
"Comma separated list of allowed extra label namespaces")
|
||||||
|
flagset.Var(overrides.LabelWhiteList, "label-whitelist",
|
||||||
|
"Regular expression to filter label names to publish to the Kubernetes API server. "+
|
||||||
|
"NB: the label namespace is omitted i.e. the filter is only applied to the name part after '/'.")
|
||||||
|
overrides.EnableTaints = flagset.Bool("enable-taints", false,
|
||||||
|
"Enable node tainting feature")
|
||||||
|
overrides.NoPublish = flagset.Bool("no-publish", false,
|
||||||
|
"Do not publish feature labels")
|
||||||
|
flagset.Var(overrides.DenyLabelNs, "deny-label-ns",
|
||||||
|
"Comma separated list of denied label namespaces")
|
||||||
|
flagset.Var(overrides.ResourceLabels, "resource-labels",
|
||||||
|
"Comma separated list of labels to be exposed as extended resources.")
|
||||||
|
|
||||||
|
return args, overrides
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,3 @@ spec:
|
||||||
failureThreshold: 10
|
failureThreshold: 10
|
||||||
command:
|
command:
|
||||||
- "nfd-master"
|
- "nfd-master"
|
||||||
args: []
|
|
||||||
volumeMounts: []
|
|
||||||
volumes: []
|
|
||||||
|
|
|
@ -35,3 +35,7 @@ patches:
|
||||||
target:
|
target:
|
||||||
labelSelector: app=nfd
|
labelSelector: app=nfd
|
||||||
name: nfd
|
name: nfd
|
||||||
|
- path: master-mounts.yaml
|
||||||
|
target:
|
||||||
|
labelSelector: app=nfd
|
||||||
|
name: nfd-master
|
||||||
|
|
13
deployment/components/common/master-mounts.yaml
Normal file
13
deployment/components/common/master-mounts.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
- op: add
|
||||||
|
path: /spec/template/spec/volumes
|
||||||
|
value:
|
||||||
|
- name: nfd-master-conf
|
||||||
|
configMap:
|
||||||
|
name: nfd-master-conf
|
||||||
|
|
||||||
|
- op: add
|
||||||
|
path: /spec/template/spec/containers/0/volumeMounts
|
||||||
|
value:
|
||||||
|
- name: nfd-master-conf
|
||||||
|
mountPath: "/etc/kubernetes/node-feature-discovery"
|
||||||
|
readOnly: true
|
10
deployment/components/master-config/kustomization.yaml
Normal file
10
deployment/components/master-config/kustomization.yaml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1alpha1
|
||||||
|
kind: Component
|
||||||
|
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
|
||||||
|
configMapGenerator:
|
||||||
|
- files:
|
||||||
|
- nfd-master.conf=nfd-master.conf.example
|
||||||
|
name: nfd-master-conf
|
|
@ -0,0 +1,6 @@
|
||||||
|
# noPublish: false
|
||||||
|
# extraLabelNs: ["added.ns.io","added.kubernets.io"]
|
||||||
|
# denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
||||||
|
# resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
|
||||||
|
# enableTaints: false
|
||||||
|
# labelWhiteList: "foo"
|
|
@ -112,10 +112,20 @@ spec:
|
||||||
- name: nfd-master-cert
|
- name: nfd-master-cert
|
||||||
mountPath: "/etc/kubernetes/node-feature-discovery/certs"
|
mountPath: "/etc/kubernetes/node-feature-discovery/certs"
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
- name: nfd-master-conf
|
||||||
|
mountPath: "/etc/kubernetes/node-feature-discovery"
|
||||||
|
readOnly: true
|
||||||
volumes:
|
volumes:
|
||||||
- name: nfd-master-cert
|
- name: nfd-master-cert
|
||||||
secret:
|
secret:
|
||||||
secretName: nfd-master-cert
|
secretName: nfd-master-cert
|
||||||
|
- name: nfd-master-conf
|
||||||
|
configMap:
|
||||||
|
name: {{ include "node-feature-discovery.fullname" . }}-master-conf
|
||||||
|
items:
|
||||||
|
- key: nfd-master.conf
|
||||||
|
path: nfd-master.conf
|
||||||
|
|
||||||
## /TLS ##
|
## /TLS ##
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.master.nodeSelector }}
|
{{- with .Values.master.nodeSelector }}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: {{ include "node-feature-discovery.fullname" . }}-master-conf
|
||||||
|
namespace: {{ include "node-feature-discovery.namespace" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "node-feature-discovery.labels" . | nindent 4 }}
|
||||||
|
data:
|
||||||
|
nfd-master.conf: |-
|
||||||
|
{{- .Values.master.config | toYaml | nindent 4 }}
|
|
@ -13,6 +13,14 @@ namespaceOverride: ""
|
||||||
enableNodeFeatureApi: false
|
enableNodeFeatureApi: false
|
||||||
|
|
||||||
master:
|
master:
|
||||||
|
config: ### <NFD-MASTER-CONF-START-DO-NOT-REMOVE>
|
||||||
|
# noPublish: false
|
||||||
|
# extraLabelNs: ["added.ns.io","added.kubernets.io"]
|
||||||
|
# denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
||||||
|
# resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
|
||||||
|
# enableTaints: false
|
||||||
|
# labelWhiteList: "foo"
|
||||||
|
### <NFD-MASTER-CONF-END-DO-NOT-REMOVE>
|
||||||
# The TCP port that nfd-master listens for incoming requests. Default: 8080
|
# The TCP port that nfd-master listens for incoming requests. Default: 8080
|
||||||
port: 8080
|
port: 8080
|
||||||
instance:
|
instance:
|
||||||
|
|
|
@ -14,3 +14,4 @@ resources:
|
||||||
components:
|
components:
|
||||||
- ../../components/worker-config
|
- ../../components/worker-config
|
||||||
- ../../components/common
|
- ../../components/common
|
||||||
|
- ../../components/master-config
|
||||||
|
|
|
@ -15,3 +15,4 @@ resources:
|
||||||
components:
|
components:
|
||||||
- ../../components/worker-config
|
- ../../components/worker-config
|
||||||
- ../../components/common
|
- ../../components/common
|
||||||
|
- ../../components/master-config
|
||||||
|
|
|
@ -15,3 +15,4 @@ resources:
|
||||||
components:
|
components:
|
||||||
- ../../components/worker-config
|
- ../../components/worker-config
|
||||||
- ../../components/common
|
- ../../components/common
|
||||||
|
- ../../components/master-config
|
||||||
|
|
|
@ -22,3 +22,4 @@ components:
|
||||||
- ../../components/common
|
- ../../components/common
|
||||||
- ../../components/topology-updater
|
- ../../components/topology-updater
|
||||||
- ../../components/topology-updater-config
|
- ../../components/topology-updater-config
|
||||||
|
- ../../components/master-config
|
||||||
|
|
|
@ -130,6 +130,7 @@ We have introduced the following Chart parameters.
|
||||||
| `master.annotations` | dict | {} | NFD master pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) |
|
| `master.annotations` | dict | {} | NFD master pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) |
|
||||||
| `master.affinity` | dict | | NFD master pod required [node affinity](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/) |
|
| `master.affinity` | dict | | NFD master pod required [node affinity](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/) |
|
||||||
| `master.deploymentAnnotations` | dict | {} | NFD master deployment [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) |
|
| `master.deploymentAnnotations` | dict | {} | NFD master deployment [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) |
|
||||||
|
| `master.config` | dict | | NFD master [configuration](../reference/master-configuration-reference) |
|
||||||
|
|
||||||
### Worker pod parameters
|
### Worker pod parameters
|
||||||
|
|
||||||
|
|
|
@ -268,6 +268,34 @@ Example:
|
||||||
nfd-master -resource-labels=vendor-1.com/feature-1,vendor-2.io/feature-2
|
nfd-master -resource-labels=vendor-1.com/feature-1,vendor-2.io/feature-2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### -config
|
||||||
|
|
||||||
|
The `-config` flag specifies the path of the nfd-master configuration file to
|
||||||
|
use.
|
||||||
|
|
||||||
|
Default: /etc/kubernetes/node-feature-discovery/nfd-master.conf
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nfd-master -config=/opt/nfd/master.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### -options
|
||||||
|
|
||||||
|
The `-options` flag may be used to specify and override configuration file
|
||||||
|
options directly from the command line. The required format is the same as in
|
||||||
|
the config file i.e. JSON or YAML. Configuration options specified via this
|
||||||
|
flag will override those from the configuration file:
|
||||||
|
|
||||||
|
Default: *empty*
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nfd-master -options='{"noPublish": true}'
|
||||||
|
```
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
The following logging-related flags are inherited from the
|
The following logging-related flags are inherited from the
|
||||||
|
|
111
docs/reference/master-configuration-reference.md
Normal file
111
docs/reference/master-configuration-reference.md
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
---
|
||||||
|
title: "Master config reference"
|
||||||
|
layout: default
|
||||||
|
sort: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Configuration file reference of nfd-master
|
||||||
|
{: .no_toc}
|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
{: .no_toc .text-delta}
|
||||||
|
|
||||||
|
1. TOC
|
||||||
|
{:toc}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
See the
|
||||||
|
[sample configuration file](https://github.com/kubernetes-sigs/node-feature-discovery/blob/{{site.release}}/deployment/components/master-config/nfd-master.conf.example)
|
||||||
|
for a full example configuration.
|
||||||
|
|
||||||
|
## noPublish
|
||||||
|
|
||||||
|
`noPublish` option disables updates to the Node objects in the Kubernetes
|
||||||
|
API server, making a "dry-run" flag for nfd-master. No Labels, Annotations, Taints
|
||||||
|
or ExtendedResources of nodes are updated.
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
noPublish: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## extraLabelNs
|
||||||
|
`extraLabelNs` specifies a list of allowed feature
|
||||||
|
label namespaces. This option can be used to allow
|
||||||
|
other vendor or application specific namespaces for custom labels from the
|
||||||
|
local and custom feature sources, even though these labels were denied using
|
||||||
|
the `denyLabelNs` parameter.
|
||||||
|
|
||||||
|
The same namespace control and this option applies to Extended Resources (created
|
||||||
|
with `resourceLabels`), too.
|
||||||
|
|
||||||
|
Default: *empty*
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extraLabelNs: ["added.ns.io","added.kubernets.io"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## denyLabelNs
|
||||||
|
`denyLabelNs` specifies a list of excluded
|
||||||
|
label namespaces. By default, nfd-master allows creating labels in all
|
||||||
|
namespaces, excluding `kubernetes.io` namespace and its sub-namespaces
|
||||||
|
(i.e. `*.kubernetes.io`). However, you should note that
|
||||||
|
`kubernetes.io` and its sub-namespaces are always denied.
|
||||||
|
This option can be used to exclude some vendors or application specific
|
||||||
|
namespaces.
|
||||||
|
Note that the namespaces `feature.node.kubernetes.io` and `profile.node.kubernetes.io`
|
||||||
|
and their sub-namespaces are always allowed and cannot be denied.
|
||||||
|
|
||||||
|
Default: *empty*
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## resourceLabels
|
||||||
|
The `resourceLabels` option specifies a list of features to be
|
||||||
|
advertised as extended resources instead of labels. Features that have integer
|
||||||
|
values can be published as Extended Resources by listing them in this option.
|
||||||
|
|
||||||
|
Default: *empty*
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## enableTaints
|
||||||
|
`enableTaints` enables/disables node tainting feature of NFD.
|
||||||
|
|
||||||
|
Default: *false*
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
enableTaints: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## labelWhiteList
|
||||||
|
`labelWhiteList` specifies a regular expression for filtering feature
|
||||||
|
labels based on their name. Each label must match against the given reqular
|
||||||
|
expression in order to be published.
|
||||||
|
|
||||||
|
Note: The regular expression is only matches against the "basename" part of the
|
||||||
|
label, i.e. to the part of the name after '/'. The label namespace is omitted.
|
||||||
|
|
||||||
|
Default: *empty*
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labelWhiteList: "foo"
|
||||||
|
```
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Topology Garbage Collector Cmdline Reference"
|
title: "Topology Garbage Collector Cmdline Reference"
|
||||||
layout: default
|
layout: default
|
||||||
sort: 6
|
sort: 7
|
||||||
---
|
---
|
||||||
|
|
||||||
# NFD-Topology-Garbage-Collector Commandline Flags
|
# NFD-Topology-Garbage-Collector Commandline Flags
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Topology Updater Cmdline Reference"
|
title: "Topology Updater Cmdline Reference"
|
||||||
layout: default
|
layout: default
|
||||||
sort: 4
|
sort: 5
|
||||||
---
|
---
|
||||||
|
|
||||||
# NFD-Topology-Updater Commandline Flags
|
# NFD-Topology-Updater Commandline Flags
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Topology-Updater config reference"
|
title: "Topology-Updater config reference"
|
||||||
layout: default
|
layout: default
|
||||||
sort: 5
|
sort: 6
|
||||||
---
|
---
|
||||||
|
|
||||||
# Configuration file reference of nfd-topology-updater
|
# Configuration file reference of nfd-topology-updater
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
title: "Worker config reference"
|
title: "Worker config reference"
|
||||||
layout: default
|
layout: default
|
||||||
sort: 3
|
sort: 4
|
||||||
---
|
---
|
||||||
|
|
||||||
# Configuration file reference of nfd-worker
|
# Configuration file reference of nfd-worker
|
||||||
|
|
|
@ -52,6 +52,39 @@ enabled.
|
||||||
> present when gRPC interface is disabled
|
> present when gRPC interface is disabled
|
||||||
> and [NodeFeature](custom-resources.md#nodefeature-custom-resource) API is used.
|
> and [NodeFeature](custom-resources.md#nodefeature-custom-resource) API is used.
|
||||||
|
|
||||||
|
## Master configuration
|
||||||
|
|
||||||
|
NFD-Master supports dynamic configuration through a configuration file. The
|
||||||
|
default location is `/etc/kubernetes/node-feature-discovery/nfd-master.conf`,
|
||||||
|
but, this can be changed by specifying the`-config` command line flag.
|
||||||
|
Configuration file is re-read whenever it is modified which makes run-time
|
||||||
|
re-configuration of nfd-master straightforward.
|
||||||
|
|
||||||
|
Master configuration file is read inside the container, and thus, Volumes and
|
||||||
|
VolumeMounts are needed to make your configuration available for NFD. The
|
||||||
|
preferred method is to use a ConfigMap which provides easy deployment and
|
||||||
|
re-configurability.
|
||||||
|
|
||||||
|
The provided nfd-master deployment templates create an empty configmap and
|
||||||
|
mount it inside the nfd-master containers. In kustomize deployments,
|
||||||
|
configuration can be edited with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl -n ${NFD_NS} edit configmap nfd-master-conf
|
||||||
|
```
|
||||||
|
|
||||||
|
In Helm deployments,
|
||||||
|
[Master pod parameter](../deployment/helm.md#master-pod-parameters)
|
||||||
|
`master.config` can be used to edit the respective configuration.
|
||||||
|
|
||||||
|
See
|
||||||
|
[nfd-master configuration file reference](../reference/master-configuration-reference.md)
|
||||||
|
for more details.
|
||||||
|
The (empty-by-default)
|
||||||
|
[example config](https://github.com/kubernetes-sigs/node-feature-discovery/blob/{{site.release}}/deployment/components/master-config/nfd-master.conf.example)
|
||||||
|
contains all available configuration options and can be used as a reference
|
||||||
|
for creating a configuration.
|
||||||
|
|
||||||
## Deployment notes
|
## Deployment notes
|
||||||
|
|
||||||
NFD-Master runs as a deployment, by default
|
NFD-Master runs as a deployment, by default
|
||||||
|
|
|
@ -18,10 +18,13 @@ package nfdmaster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/smartystreets/assertions"
|
"github.com/smartystreets/assertions"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
@ -56,7 +59,7 @@ func newMockNode() *corev1.Node {
|
||||||
func newMockMaster(apihelper apihelper.APIHelpers) *nfdMaster {
|
func newMockMaster(apihelper apihelper.APIHelpers) *nfdMaster {
|
||||||
return &nfdMaster{
|
return &nfdMaster{
|
||||||
nodeName: mockNodeName,
|
nodeName: mockNodeName,
|
||||||
args: Args{LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")}},
|
config: &NFDConfig{LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")}},
|
||||||
apihelper: apihelper,
|
apihelper: apihelper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,7 +344,7 @@ func TestSetLabels(t *testing.T) {
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/feature-2", mockLabels["feature-2"]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/feature-2", mockLabels["feature-2"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockMaster.args.LabelWhiteList.Regexp = *regexp.MustCompile("^f.*2$")
|
mockMaster.config.LabelWhiteList.Regexp = *regexp.MustCompile("^f.*2$")
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
||||||
mockHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedPatches))).Return(nil)
|
mockHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedPatches))).Return(nil)
|
||||||
|
@ -378,7 +381,7 @@ func TestSetLabels(t *testing.T) {
|
||||||
|
|
||||||
mockMaster.deniedNs.normal = map[string]struct{}{"random.denied.ns": {}}
|
mockMaster.deniedNs.normal = map[string]struct{}{"random.denied.ns": {}}
|
||||||
mockMaster.deniedNs.wildcard = map[string]struct{}{"kubernetes.io": {}}
|
mockMaster.deniedNs.wildcard = map[string]struct{}{"kubernetes.io": {}}
|
||||||
mockMaster.args.ExtraLabelNs = map[string]struct{}{"valid.ns": {}}
|
mockMaster.config.ExtraLabelNs = map[string]struct{}{"valid.ns": {}}
|
||||||
mockMaster.args.Instance = instance
|
mockMaster.args.Instance = instance
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
||||||
|
@ -404,7 +407,7 @@ func TestSetLabels(t *testing.T) {
|
||||||
apihelper.NewJsonPatch("add", "/status/capacity", nfdv1alpha1.FeatureLabelNs+"/feature-3", mockLabels["feature-3"]),
|
apihelper.NewJsonPatch("add", "/status/capacity", nfdv1alpha1.FeatureLabelNs+"/feature-3", mockLabels["feature-3"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockMaster.args.ResourceLabels = map[string]struct{}{"feature-3": {}, "feature-1": {}}
|
mockMaster.config.ResourceLabels = map[string]struct{}{"feature-3": {}, "feature-1": {}}
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
||||||
mockHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedPatches))).Return(nil)
|
mockHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedPatches))).Return(nil)
|
||||||
|
@ -424,7 +427,7 @@ func TestSetLabels(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
mockMaster.args.NoPublish = true
|
mockMaster.config.NoPublish = true
|
||||||
Convey("With '-no-publish'", func() {
|
Convey("With '-no-publish'", func() {
|
||||||
_, err := mockMaster.SetLabels(mockCtx, mockReq)
|
_, err := mockMaster.SetLabels(mockCtx, mockReq)
|
||||||
Convey("Operation should succeed", func() {
|
Convey("Operation should succeed", func() {
|
||||||
|
@ -513,6 +516,182 @@ func TestRemoveLabelsWithPrefix(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigParse(t *testing.T) {
|
||||||
|
Convey("When parsing configuration", t, func() {
|
||||||
|
m, err := NewNfdMaster(&Args{})
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
master := m.(*nfdMaster)
|
||||||
|
overrides := `{"noPublish": true, "enableTaints": true, "extraLabelNs": ["added.ns.io","added.kubernetes.io"], "denyLabelNs": ["denied.ns.io","denied.kubernetes.io"], "resourceLabels": ["vendor-1.com/feature-1","vendor-2.io/feature-2"], "labelWhiteList": "foo"}`
|
||||||
|
|
||||||
|
Convey("and no core cmdline flags have been specified", func() {
|
||||||
|
So(master.configure("non-existing-file", overrides), ShouldBeNil)
|
||||||
|
Convey("overrides should be in effect", func() {
|
||||||
|
So(master.config.NoPublish, ShouldResemble, true)
|
||||||
|
So(master.config.EnableTaints, ShouldResemble, true)
|
||||||
|
So(master.config.ExtraLabelNs, ShouldResemble, utils.StringSetVal{"added.ns.io": struct{}{}, "added.kubernetes.io": struct{}{}})
|
||||||
|
So(master.config.DenyLabelNs, ShouldResemble, utils.StringSetVal{"denied.ns.io": struct{}{}, "denied.kubernetes.io": struct{}{}})
|
||||||
|
So(master.config.ResourceLabels, ShouldResemble, utils.StringSetVal{"vendor-1.com/feature-1": struct{}{}, "vendor-2.io/feature-2": struct{}{}})
|
||||||
|
So(master.config.LabelWhiteList.String(), ShouldEqual, "foo")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Convey("and a non-accessible file, but cmdline flags and some overrides are specified", func() {
|
||||||
|
master.args = Args{Overrides: ConfigOverrideArgs{
|
||||||
|
ExtraLabelNs: &utils.StringSetVal{"override.added.ns.io": struct{}{}},
|
||||||
|
DenyLabelNs: &utils.StringSetVal{"override.denied.ns.io": struct{}{}}}}
|
||||||
|
So(master.configure("non-existing-file", overrides), ShouldBeNil)
|
||||||
|
|
||||||
|
Convey("cmdline flags should be in effect instead overrides", func() {
|
||||||
|
So(master.config.ExtraLabelNs, ShouldResemble, utils.StringSetVal{"override.added.ns.io": struct{}{}})
|
||||||
|
So(master.config.DenyLabelNs, ShouldResemble, utils.StringSetVal{"override.denied.ns.io": struct{}{}})
|
||||||
|
})
|
||||||
|
Convey("overrides should take effect", func() {
|
||||||
|
So(master.config.NoPublish, ShouldBeTrue)
|
||||||
|
So(master.config.EnableTaints, ShouldBeTrue)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// Create a temporary config file
|
||||||
|
f, err := os.CreateTemp("", "nfd-test-")
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
_, err = f.WriteString(`
|
||||||
|
noPublish: true
|
||||||
|
denyLabelNs: ["denied.ns.io","denied.kubernetes.io"]
|
||||||
|
resourceLabels: ["vendor-1.com/feature-1","vendor-2.io/feature-2"]
|
||||||
|
enableTaints: false
|
||||||
|
labelWhiteList: "foo"
|
||||||
|
`)
|
||||||
|
f.Close()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
Convey("and a proper config file is specified", func() {
|
||||||
|
master.args = Args{Overrides: ConfigOverrideArgs{ExtraLabelNs: &utils.StringSetVal{"override.added.ns.io": struct{}{}}}}
|
||||||
|
So(master.configure(f.Name(), ""), ShouldBeNil)
|
||||||
|
Convey("specified configuration should take effect", func() {
|
||||||
|
// Verify core config
|
||||||
|
So(master.config.NoPublish, ShouldBeTrue)
|
||||||
|
So(master.config.EnableTaints, ShouldBeFalse)
|
||||||
|
So(master.config.ExtraLabelNs, ShouldResemble, utils.StringSetVal{"override.added.ns.io": struct{}{}})
|
||||||
|
So(master.config.ResourceLabels, ShouldResemble, utils.StringSetVal{"vendor-1.com/feature-1": struct{}{}, "vendor-2.io/feature-2": struct{}{}}) // from cmdline
|
||||||
|
So(master.config.DenyLabelNs, ShouldResemble, utils.StringSetVal{"denied.ns.io": struct{}{}, "denied.kubernetes.io": struct{}{}})
|
||||||
|
So(master.config.LabelWhiteList.String(), ShouldEqual, "foo")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("and a proper config file and overrides are given", func() {
|
||||||
|
master.args = Args{Overrides: ConfigOverrideArgs{DenyLabelNs: &utils.StringSetVal{"denied.ns.io": struct{}{}}}}
|
||||||
|
overrides := `{"extraLabelNs": ["added.ns.io"], "noPublish": true}`
|
||||||
|
So(master.configure(f.Name(), overrides), ShouldBeNil)
|
||||||
|
|
||||||
|
Convey("overrides should take precedence over the config file", func() {
|
||||||
|
// Verify core config
|
||||||
|
So(master.config.ExtraLabelNs, ShouldResemble, utils.StringSetVal{"added.ns.io": struct{}{}}) // from overrides
|
||||||
|
So(master.config.DenyLabelNs, ShouldResemble, utils.StringSetVal{"denied.ns.io": struct{}{}}) // from cmdline
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDynamicConfig(t *testing.T) {
|
||||||
|
Convey("When running nfd-master", t, func() {
|
||||||
|
tmpDir, err := os.MkdirTemp("", "*.nfd-test")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
// Create (temporary) dir for config
|
||||||
|
configDir := filepath.Join(tmpDir, "subdir-1", "subdir-2", "master.conf")
|
||||||
|
err = os.MkdirAll(configDir, 0755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
// Create config file
|
||||||
|
configFile := filepath.Join(configDir, "master.conf")
|
||||||
|
|
||||||
|
writeConfig := func(data string) {
|
||||||
|
f, err := os.Create(configFile)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
_, err = f.WriteString(data)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = f.Close()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
}
|
||||||
|
writeConfig(`
|
||||||
|
extraLabelNs: ["added.ns.io"]
|
||||||
|
`)
|
||||||
|
|
||||||
|
noPublish := true
|
||||||
|
m, err := NewNfdMaster(&Args{
|
||||||
|
ConfigFile: configFile,
|
||||||
|
Overrides: ConfigOverrideArgs{
|
||||||
|
NoPublish: &noPublish,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
master := m.(*nfdMaster)
|
||||||
|
|
||||||
|
Convey("config file updates should take effect", func() {
|
||||||
|
go func() { _ = m.Run() }()
|
||||||
|
defer m.Stop()
|
||||||
|
// Check initial config
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
So(func() interface{} { return master.config.ExtraLabelNs },
|
||||||
|
withTimeout, 2*time.Second, ShouldResemble, utils.StringSetVal{"added.ns.io": struct{}{}})
|
||||||
|
|
||||||
|
// Update config and verify the effect
|
||||||
|
writeConfig(`
|
||||||
|
extraLabelNs: ["override.ns.io"]
|
||||||
|
`)
|
||||||
|
So(func() interface{} { return master.config.ExtraLabelNs },
|
||||||
|
withTimeout, 2*time.Second, ShouldResemble, utils.StringSetVal{"override.ns.io": struct{}{}})
|
||||||
|
|
||||||
|
// Removing config file should get back our defaults
|
||||||
|
err = os.RemoveAll(tmpDir)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(func() interface{} { return master.config.ExtraLabelNs },
|
||||||
|
withTimeout, 2*time.Second, ShouldResemble, utils.StringSetVal{})
|
||||||
|
|
||||||
|
// Re-creating config dir and file should change the config
|
||||||
|
err = os.MkdirAll(configDir, 0755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
writeConfig(`
|
||||||
|
extraLabelNs: ["another.override.ns"]
|
||||||
|
`)
|
||||||
|
So(func() interface{} { return master.config.ExtraLabelNs },
|
||||||
|
withTimeout, 2*time.Second, ShouldResemble, utils.StringSetVal{"another.override.ns": struct{}{}})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// withTimeout is a custom assertion for polling a value asynchronously
|
||||||
|
// actual is a function for getting the actual value
|
||||||
|
// expected[0] is a time.Duration value specifying the timeout
|
||||||
|
// expected[1] is the "real" assertion function to be called
|
||||||
|
// expected[2:] are the arguments for the "real" assertion function
|
||||||
|
func withTimeout(actual interface{}, expected ...interface{}) string {
|
||||||
|
getter, ok := actual.(func() interface{})
|
||||||
|
if !ok {
|
||||||
|
return "not getterFunc"
|
||||||
|
}
|
||||||
|
t, ok := expected[0].(time.Duration)
|
||||||
|
if !ok {
|
||||||
|
return "not time.Duration"
|
||||||
|
}
|
||||||
|
f, ok := expected[1].(func(interface{}, ...interface{}) string)
|
||||||
|
if !ok {
|
||||||
|
return "not an assert func"
|
||||||
|
}
|
||||||
|
timeout := time.After(t)
|
||||||
|
for {
|
||||||
|
result := f(getter(), expected[2:]...)
|
||||||
|
if result == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return result
|
||||||
|
case <-time.After(10 * time.Millisecond):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func jsonPatchMatcher(expected []apihelper.JsonPatch) func([]apihelper.JsonPatch) bool {
|
func jsonPatchMatcher(expected []apihelper.JsonPatch) func([]apihelper.JsonPatch) bool {
|
||||||
return func(actual []apihelper.JsonPatch) bool {
|
return func(actual []apihelper.JsonPatch) bool {
|
||||||
// We don't care about modifying the original slices
|
// We don't care about modifying the original slices
|
||||||
|
|
|
@ -21,7 +21,9 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -42,6 +44,7 @@ import (
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
controller "k8s.io/kubernetes/pkg/controller"
|
controller "k8s.io/kubernetes/pkg/controller"
|
||||||
taintutils "k8s.io/kubernetes/pkg/util/taints"
|
taintutils "k8s.io/kubernetes/pkg/util/taints"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
|
"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
|
||||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||||
|
@ -59,24 +62,42 @@ type ExtendedResources map[string]string
|
||||||
// Annotations are used for NFD-related node metadata
|
// Annotations are used for NFD-related node metadata
|
||||||
type Annotations map[string]string
|
type Annotations map[string]string
|
||||||
|
|
||||||
|
// NFDConfig contains the configuration settings of NfdMaster.
|
||||||
|
type NFDConfig struct {
|
||||||
|
DenyLabelNs utils.StringSetVal
|
||||||
|
ExtraLabelNs utils.StringSetVal
|
||||||
|
LabelWhiteList utils.RegexpVal
|
||||||
|
NoPublish bool
|
||||||
|
ResourceLabels utils.StringSetVal
|
||||||
|
EnableTaints bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigOverrideArgs are args that override config file options
|
||||||
|
type ConfigOverrideArgs struct {
|
||||||
|
DenyLabelNs *utils.StringSetVal
|
||||||
|
ExtraLabelNs *utils.StringSetVal
|
||||||
|
LabelWhiteList *utils.RegexpVal
|
||||||
|
ResourceLabels *utils.StringSetVal
|
||||||
|
EnableTaints *bool
|
||||||
|
NoPublish *bool
|
||||||
|
}
|
||||||
|
|
||||||
// Args holds command line arguments
|
// Args holds command line arguments
|
||||||
type Args struct {
|
type Args struct {
|
||||||
CaFile string
|
CaFile string
|
||||||
CertFile string
|
CertFile string
|
||||||
DenyLabelNs utils.StringSetVal
|
ConfigFile string
|
||||||
ExtraLabelNs utils.StringSetVal
|
|
||||||
Instance string
|
Instance string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
Kubeconfig string
|
Kubeconfig string
|
||||||
LabelWhiteList utils.RegexpVal
|
|
||||||
CrdController bool
|
CrdController bool
|
||||||
EnableNodeFeatureApi bool
|
EnableNodeFeatureApi bool
|
||||||
NoPublish bool
|
|
||||||
EnableTaints bool
|
|
||||||
Port int
|
Port int
|
||||||
Prune bool
|
Prune bool
|
||||||
VerifyNodeName bool
|
VerifyNodeName bool
|
||||||
ResourceLabels utils.StringSetVal
|
Options string
|
||||||
|
|
||||||
|
Overrides ConfigOverrideArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
type deniedNs struct {
|
type deniedNs struct {
|
||||||
|
@ -93,15 +114,17 @@ type NfdMaster interface {
|
||||||
type nfdMaster struct {
|
type nfdMaster struct {
|
||||||
*nfdController
|
*nfdController
|
||||||
|
|
||||||
args Args
|
args Args
|
||||||
namespace string
|
namespace string
|
||||||
nodeName string
|
nodeName string
|
||||||
server *grpc.Server
|
configFilePath string
|
||||||
stop chan struct{}
|
server *grpc.Server
|
||||||
ready chan bool
|
stop chan struct{}
|
||||||
apihelper apihelper.APIHelpers
|
ready chan bool
|
||||||
kubeconfig *restclient.Config
|
apihelper apihelper.APIHelpers
|
||||||
|
kubeconfig *restclient.Config
|
||||||
deniedNs
|
deniedNs
|
||||||
|
config *NFDConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNfdMaster creates a new NfdMaster server instance.
|
// NewNfdMaster creates a new NfdMaster server instance.
|
||||||
|
@ -133,29 +156,25 @@ func NewNfdMaster(args *Args) (NfdMaster, error) {
|
||||||
return nfd, fmt.Errorf("-ca-file needs to be specified alongside -cert-file and -key-file")
|
return nfd, fmt.Errorf("-ca-file needs to be specified alongside -cert-file and -key-file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if args.DenyLabelNs == nil {
|
|
||||||
args.DenyLabelNs = make(utils.StringSetVal)
|
|
||||||
}
|
|
||||||
// Pre-process DenyLabelNS into 2 lists: one for normal ns, and the other for wildcard ns
|
|
||||||
normalDeniedNs, wildcardDeniedNs := preProcessDeniedNamespaces(args.DenyLabelNs)
|
|
||||||
nfd.deniedNs.normal = normalDeniedNs
|
|
||||||
nfd.deniedNs.wildcard = wildcardDeniedNs
|
|
||||||
// We forcibly deny kubernetes.io
|
|
||||||
nfd.deniedNs.normal["kubernetes.io"] = struct{}{}
|
|
||||||
nfd.deniedNs.wildcard[".kubernetes.io"] = struct{}{}
|
|
||||||
|
|
||||||
// Initialize Kubernetes API helpers
|
if args.ConfigFile != "" {
|
||||||
if !args.NoPublish {
|
nfd.configFilePath = filepath.Clean(args.ConfigFile)
|
||||||
kubeconfig, err := nfd.getKubeconfig()
|
|
||||||
if err != nil {
|
|
||||||
return nfd, err
|
|
||||||
}
|
|
||||||
nfd.apihelper = apihelper.K8sHelpers{Kubeconfig: kubeconfig}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nfd, nil
|
return nfd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newDefaultConfig() *NFDConfig {
|
||||||
|
return &NFDConfig{
|
||||||
|
LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")},
|
||||||
|
DenyLabelNs: utils.StringSetVal{},
|
||||||
|
ExtraLabelNs: utils.StringSetVal{},
|
||||||
|
NoPublish: false,
|
||||||
|
ResourceLabels: utils.StringSetVal{},
|
||||||
|
EnableTaints: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Run NfdMaster server. The method returns in case of fatal errors or if Stop()
|
// Run NfdMaster server. The method returns in case of fatal errors or if Stop()
|
||||||
// is called.
|
// is called.
|
||||||
func (m *nfdMaster) Run() error {
|
func (m *nfdMaster) Run() error {
|
||||||
|
@ -182,13 +201,21 @@ func (m *nfdMaster) Run() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !m.args.NoPublish {
|
// Create watcher for config file and read initial configuration
|
||||||
|
configWatch, err := utils.CreateFsWatcher(time.Second, m.configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := m.configure(m.configFilePath, m.args.Options); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !m.config.NoPublish {
|
||||||
err := m.updateMasterNode()
|
err := m.updateMasterNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to update master node: %v", err)
|
return fmt.Errorf("failed to update master node: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run gRPC server
|
// Run gRPC server
|
||||||
grpcErr := make(chan error, 1)
|
grpcErr := make(chan error, 1)
|
||||||
go m.runGrpcServer(grpcErr)
|
go m.runGrpcServer(grpcErr)
|
||||||
|
@ -208,6 +235,15 @@ func (m *nfdMaster) Run() error {
|
||||||
case err := <-grpcErr:
|
case err := <-grpcErr:
|
||||||
return fmt.Errorf("error in serving gRPC: %w", err)
|
return fmt.Errorf("error in serving gRPC: %w", err)
|
||||||
|
|
||||||
|
case <-configWatch.Events:
|
||||||
|
klog.Infof("reloading configuration")
|
||||||
|
if err := m.configure(m.configFilePath, m.args.Options); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Update all nodes when the configuration changes
|
||||||
|
if m.nfdController != nil {
|
||||||
|
m.nfdController.updateAllNodesChan <- struct{}{}
|
||||||
|
}
|
||||||
case <-m.stop:
|
case <-m.stop:
|
||||||
klog.Infof("shutting down nfd-master")
|
klog.Infof("shutting down nfd-master")
|
||||||
return nil
|
return nil
|
||||||
|
@ -423,7 +459,7 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels) (Labels, ExtendedResource
|
||||||
!strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) {
|
!strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) {
|
||||||
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
||||||
if isNamespaceDenied(ns, m.deniedNs.wildcard, m.deniedNs.normal) {
|
if isNamespaceDenied(ns, m.deniedNs.wildcard, m.deniedNs.normal) {
|
||||||
if _, ok := m.args.ExtraLabelNs[ns]; !ok {
|
if _, ok := m.config.ExtraLabelNs[ns]; !ok {
|
||||||
klog.Errorf("Namespace %q is not allowed. Ignoring label %q\n", ns, label)
|
klog.Errorf("Namespace %q is not allowed. Ignoring label %q\n", ns, label)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -431,8 +467,8 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels) (Labels, ExtendedResource
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if label doesn't match labelWhiteList
|
// Skip if label doesn't match labelWhiteList
|
||||||
if !m.args.LabelWhiteList.Regexp.MatchString(name) {
|
if !m.config.LabelWhiteList.Regexp.MatchString(name) {
|
||||||
klog.Errorf("%s (%s) does not match the whitelist (%s) and will not be published.", name, label, m.args.LabelWhiteList.Regexp.String())
|
klog.Errorf("%s (%s) does not match the whitelist (%s) and will not be published.", name, label, m.config.LabelWhiteList.Regexp.String())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
outLabels[label] = value
|
outLabels[label] = value
|
||||||
|
@ -440,7 +476,7 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels) (Labels, ExtendedResource
|
||||||
|
|
||||||
// Remove labels which are intended to be extended resources
|
// Remove labels which are intended to be extended resources
|
||||||
extendedResources := ExtendedResources{}
|
extendedResources := ExtendedResources{}
|
||||||
for extendedResourceName := range m.args.ResourceLabels {
|
for extendedResourceName := range m.config.ResourceLabels {
|
||||||
// Add possibly missing default ns
|
// Add possibly missing default ns
|
||||||
extendedResourceName = addNs(extendedResourceName, nfdv1alpha1.FeatureLabelNs)
|
extendedResourceName = addNs(extendedResourceName, nfdv1alpha1.FeatureLabelNs)
|
||||||
if value, ok := outLabels[extendedResourceName]; ok {
|
if value, ok := outLabels[extendedResourceName]; ok {
|
||||||
|
@ -498,8 +534,7 @@ func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.Se
|
||||||
default:
|
default:
|
||||||
klog.Infof("received labeling request for node %q", r.NodeName)
|
klog.Infof("received labeling request for node %q", r.NodeName)
|
||||||
}
|
}
|
||||||
|
if !m.config.NoPublish {
|
||||||
if !m.args.NoPublish {
|
|
||||||
cli, err := m.apihelper.GetClient()
|
cli, err := m.apihelper.GetClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &pb.SetLabelsReply{}, err
|
return &pb.SetLabelsReply{}, err
|
||||||
|
@ -562,7 +597,7 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error {
|
||||||
return objs[i].Namespace < objs[j].Namespace
|
return objs[i].Namespace < objs[j].Namespace
|
||||||
})
|
})
|
||||||
|
|
||||||
if m.args.NoPublish {
|
if m.config.NoPublish {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,7 +655,7 @@ func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName stri
|
||||||
labels, extendedResources := m.filterFeatureLabels(labels)
|
labels, extendedResources := m.filterFeatureLabels(labels)
|
||||||
|
|
||||||
var taints []corev1.Taint
|
var taints []corev1.Taint
|
||||||
if m.args.EnableTaints {
|
if m.config.EnableTaints {
|
||||||
taints = crTaints
|
taints = crTaints
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -921,6 +956,75 @@ func (m *nfdMaster) createExtendedResourcePatches(n *corev1.Node, extendedResour
|
||||||
return patches
|
return patches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse configuration options
|
||||||
|
func (m *nfdMaster) configure(filepath string, overrides string) error {
|
||||||
|
// Create a new default config
|
||||||
|
c := newDefaultConfig()
|
||||||
|
|
||||||
|
// Try to read and parse config file
|
||||||
|
if filepath != "" {
|
||||||
|
data, err := os.ReadFile(filepath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
klog.Infof("config file %q not found, using defaults", filepath)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("error reading config file: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = yaml.Unmarshal(data, c)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse config file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.Infof("configuration file %q parsed", filepath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse config overrides
|
||||||
|
if err := yaml.Unmarshal([]byte(overrides), c); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse -options: %s", err)
|
||||||
|
}
|
||||||
|
if m.args.Overrides.NoPublish != nil {
|
||||||
|
c.NoPublish = *m.args.Overrides.NoPublish
|
||||||
|
}
|
||||||
|
if m.args.Overrides.DenyLabelNs != nil {
|
||||||
|
c.DenyLabelNs = *m.args.Overrides.DenyLabelNs
|
||||||
|
}
|
||||||
|
if m.args.Overrides.ExtraLabelNs != nil {
|
||||||
|
c.ExtraLabelNs = *m.args.Overrides.ExtraLabelNs
|
||||||
|
}
|
||||||
|
if m.args.Overrides.ResourceLabels != nil {
|
||||||
|
c.ResourceLabels = *m.args.Overrides.ResourceLabels
|
||||||
|
}
|
||||||
|
if m.args.Overrides.EnableTaints != nil {
|
||||||
|
c.EnableTaints = *m.args.Overrides.EnableTaints
|
||||||
|
}
|
||||||
|
if m.args.Overrides.LabelWhiteList != nil {
|
||||||
|
c.LabelWhiteList = *m.args.Overrides.LabelWhiteList
|
||||||
|
}
|
||||||
|
|
||||||
|
m.config = c
|
||||||
|
if !c.NoPublish {
|
||||||
|
kubeconfig, err := m.getKubeconfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.apihelper = apihelper.K8sHelpers{Kubeconfig: kubeconfig}
|
||||||
|
}
|
||||||
|
// Pre-process DenyLabelNS into 2 lists: one for normal ns, and the other for wildcard ns
|
||||||
|
normalDeniedNs, wildcardDeniedNs := preProcessDeniedNamespaces(c.DenyLabelNs)
|
||||||
|
m.deniedNs.normal = normalDeniedNs
|
||||||
|
m.deniedNs.wildcard = wildcardDeniedNs
|
||||||
|
// We forcibly deny kubernetes.io
|
||||||
|
m.deniedNs.normal["kubernetes.io"] = struct{}{}
|
||||||
|
m.deniedNs.wildcard[".kubernetes.io"] = struct{}{}
|
||||||
|
|
||||||
|
utils.KlogDump(1, "effective configuration:", " ", m.config)
|
||||||
|
klog.Infof("master (re-)configuration successfully completed")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// addNs adds a namespace if one isn't already found from src string
|
// addNs adds a namespace if one isn't already found from src string
|
||||||
func addNs(src string, nsToAdd string) string {
|
func addNs(src string, nsToAdd string) string {
|
||||||
if strings.Contains(src, "/") {
|
if strings.Contains(src, "/") {
|
||||||
|
|
|
@ -35,5 +35,11 @@ func TestNewNfdMaster(t *testing.T) {
|
||||||
So(err3, ShouldNotBeNil)
|
So(err3, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Convey("When -config is supplied", func() {
|
||||||
|
_, err := m.NewNfdMaster(&m.Args{CertFile: "crt", KeyFile: "key", CaFile: "ca", ConfigFile: "master-config.yaml"})
|
||||||
|
Convey("An error should not be returned", func() {
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,9 +38,12 @@ type testContext struct {
|
||||||
|
|
||||||
func setupTest(args *master.Args) testContext {
|
func setupTest(args *master.Args) testContext {
|
||||||
// Fixed port and no-publish, for convenience
|
// Fixed port and no-publish, for convenience
|
||||||
args.NoPublish = true
|
publish := true
|
||||||
|
args.Overrides = master.ConfigOverrideArgs{
|
||||||
|
NoPublish: &publish,
|
||||||
|
LabelWhiteList: &utils.RegexpVal{Regexp: *regexp.MustCompile("")},
|
||||||
|
}
|
||||||
args.Port = 8192
|
args.Port = 8192
|
||||||
args.LabelWhiteList.Regexp = *regexp.MustCompile("")
|
|
||||||
m, err := master.NewNfdMaster(args)
|
m, err := master.NewNfdMaster(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Test setup failed: %v\n", err)
|
fmt.Printf("Test setup failed: %v\n", err)
|
||||||
|
|
|
@ -169,9 +169,10 @@ var _ = SIGDescribe("NFD master and worker", func() {
|
||||||
|
|
||||||
Context("when deploying a single nfd-master pod", Ordered, func() {
|
Context("when deploying a single nfd-master pod", Ordered, func() {
|
||||||
var (
|
var (
|
||||||
crds []*apiextensionsv1.CustomResourceDefinition
|
crds []*apiextensionsv1.CustomResourceDefinition
|
||||||
extClient *extclient.Clientset
|
extClient *extclient.Clientset
|
||||||
nfdClient *nfdclient.Clientset
|
nfdClient *nfdclient.Clientset
|
||||||
|
customMasterPodSpecOpts *[]testpod.SpecOption
|
||||||
)
|
)
|
||||||
|
|
||||||
checkNodeFeatureObject := func(name string) {
|
checkNodeFeatureObject := func(name string) {
|
||||||
|
@ -203,7 +204,7 @@ var _ = SIGDescribe("NFD master and worker", func() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
BeforeEach(func() {
|
JustBeforeEach(func() {
|
||||||
// Drop the pod security admission label as nfd-worker needs host mounts
|
// Drop the pod security admission label as nfd-worker needs host mounts
|
||||||
if _, ok := f.Namespace.Labels[admissionapi.EnforceLevelLabel]; ok {
|
if _, ok := f.Namespace.Labels[admissionapi.EnforceLevelLabel]; ok {
|
||||||
e2elog.Logf("Deleting %s label from the test namespace", admissionapi.EnforceLevelLabel)
|
e2elog.Logf("Deleting %s label from the test namespace", admissionapi.EnforceLevelLabel)
|
||||||
|
@ -221,15 +222,21 @@ var _ = SIGDescribe("NFD master and worker", func() {
|
||||||
|
|
||||||
// Launch nfd-master
|
// Launch nfd-master
|
||||||
By("Creating nfd master pod and nfd-master service")
|
By("Creating nfd master pod and nfd-master service")
|
||||||
podSpecOpts := createPodSpecOpts(
|
var podSpecOpts []testpod.SpecOption
|
||||||
testpod.SpecWithContainerImage(dockerImage()),
|
if customMasterPodSpecOpts == nil {
|
||||||
testpod.SpecWithTolerations(testTolerations),
|
podSpecOpts = createPodSpecOpts(
|
||||||
testpod.SpecWithContainerExtraArgs("-enable-taints"),
|
testpod.SpecWithContainerImage(dockerImage()),
|
||||||
testpod.SpecWithContainerExtraArgs(
|
testpod.SpecWithTolerations(testTolerations),
|
||||||
"-deny-label-ns=*.denied.ns,random.unwanted.ns,*.vendor.io",
|
testpod.SpecWithContainerExtraArgs("-enable-taints"),
|
||||||
"-extra-label-ns=custom.vendor.io",
|
testpod.SpecWithContainerExtraArgs(
|
||||||
),
|
"-deny-label-ns=*.denied.ns,random.unwanted.ns,*.vendor.io",
|
||||||
)
|
"-extra-label-ns=custom.vendor.io",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
podSpecOpts = createPodSpecOpts(*customMasterPodSpecOpts...)
|
||||||
|
}
|
||||||
|
|
||||||
masterPod := e2epod.NewPodClient(f).CreateSync(testpod.NFDMaster(podSpecOpts...))
|
masterPod := e2epod.NewPodClient(f).CreateSync(testpod.NFDMaster(podSpecOpts...))
|
||||||
|
|
||||||
// Create nfd-master service
|
// Create nfd-master service
|
||||||
|
@ -257,6 +264,7 @@ var _ = SIGDescribe("NFD master and worker", func() {
|
||||||
|
|
||||||
cleanupNode(f.ClientSet)
|
cleanupNode(f.ClientSet)
|
||||||
cleanupCRs(nfdClient, f.Namespace.Name)
|
cleanupCRs(nfdClient, f.Namespace.Name)
|
||||||
|
customMasterPodSpecOpts = nil
|
||||||
})
|
})
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -264,7 +272,6 @@ var _ = SIGDescribe("NFD master and worker", func() {
|
||||||
//
|
//
|
||||||
Context("and a single worker pod with fake source enabled", func() {
|
Context("and a single worker pod with fake source enabled", func() {
|
||||||
It("it should decorate the node with the fake feature labels", func() {
|
It("it should decorate the node with the fake feature labels", func() {
|
||||||
|
|
||||||
fakeFeatureLabels := map[string]string{
|
fakeFeatureLabels := map[string]string{
|
||||||
nfdv1alpha1.FeatureLabelNs + "/fake-fakefeature1": "true",
|
nfdv1alpha1.FeatureLabelNs + "/fake-fakefeature1": "true",
|
||||||
nfdv1alpha1.FeatureLabelNs + "/fake-fakefeature2": "true",
|
nfdv1alpha1.FeatureLabelNs + "/fake-fakefeature2": "true",
|
||||||
|
@ -771,6 +778,77 @@ core:
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("and check whether master config passed successfully or not", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
customMasterPodSpecOpts = &[]testpod.SpecOption{
|
||||||
|
testpod.SpecWithContainerImage(dockerImage()),
|
||||||
|
testpod.SpecWithConfigMap("nfd-master-conf", "/etc/kubernetes/node-feature-discovery"),
|
||||||
|
testpod.SpecWithTolerations(testTolerations),
|
||||||
|
}
|
||||||
|
cm := testutils.NewConfigMap("nfd-master-conf", "nfd-master.conf", `
|
||||||
|
denyLabelNs: ["*.denied.ns","random.unwanted.ns"]
|
||||||
|
`)
|
||||||
|
_, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), cm, metav1.CreateOptions{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
})
|
||||||
|
It("master configuration should take place", func() {
|
||||||
|
// deploy node feature object
|
||||||
|
if !useNodeFeatureApi {
|
||||||
|
Skip("NodeFeature API not enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes, err := getNonControlPlaneNodes(f.ClientSet)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
targetNodeName := nodes[0].Name
|
||||||
|
Expect(targetNodeName).ToNot(BeEmpty(), "No suitable worker node found")
|
||||||
|
|
||||||
|
// Apply Node Feature object
|
||||||
|
By("Create NodeFeature object")
|
||||||
|
nodeFeatures, err := testutils.CreateOrUpdateNodeFeaturesFromFile(nfdClient, "nodefeature-3.yaml", f.Namespace.Name, targetNodeName)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// Verify that denied label was not added
|
||||||
|
By("Verifying that denied labels were not added")
|
||||||
|
expectedLabels := map[string]k8sLabels{
|
||||||
|
targetNodeName: {
|
||||||
|
nfdv1alpha1.FeatureLabelNs + "/e2e-nodefeature-test-4": "obj-4",
|
||||||
|
"custom.vendor.io/e2e-nodefeature-test-3": "vendor-ns",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Expect(checkForNodeLabels(
|
||||||
|
f.ClientSet,
|
||||||
|
expectedLabels,
|
||||||
|
nodes,
|
||||||
|
)).NotTo(HaveOccurred())
|
||||||
|
By("Deleting NodeFeature object")
|
||||||
|
err = nfdClient.NfdV1alpha1().NodeFeatures(f.Namespace.Name).Delete(context.TODO(), nodeFeatures[0], metav1.DeleteOptions{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// TODO: Find a better way to handle the timeout that happens to reflect the configmap changes
|
||||||
|
Skip("Testing the master dynamic configuration")
|
||||||
|
// Verify that config changes were applied
|
||||||
|
By("Updating the master config")
|
||||||
|
Expect(testutils.UpdateConfigMap(f.ClientSet, "nfd-master-conf", f.Namespace.Name, "nfd-master.conf", `
|
||||||
|
denyLabelNs: []
|
||||||
|
`))
|
||||||
|
By("Verifying that denied labels were removed")
|
||||||
|
expectedLabels = map[string]k8sLabels{
|
||||||
|
targetNodeName: {
|
||||||
|
nfdv1alpha1.FeatureLabelNs + "/e2e-nodefeature-test-4": "obj-4",
|
||||||
|
"custom.vendor.io/e2e-nodefeature-test-3": "vendor-ns",
|
||||||
|
"random.denied.ns/e2e-nodefeature-test-1": "denied-ns",
|
||||||
|
"random.unwanted.ns/e2e-nodefeature-test-2": "unwanted-ns",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Expect(checkForNodeLabels(
|
||||||
|
f.ClientSet,
|
||||||
|
expectedLabels,
|
||||||
|
nodes,
|
||||||
|
)).NotTo(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,12 @@ limitations under the License.
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateConfigMap is a helper for creating a simple ConfigMap object with one key.
|
// CreateConfigMap is a helper for creating a simple ConfigMap object with one key.
|
||||||
|
@ -30,3 +34,17 @@ func NewConfigMap(name, key, data string) *corev1.ConfigMap {
|
||||||
Data: map[string]string{key: data},
|
Data: map[string]string{key: data},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateConfigMap is a helper for updating a ConfigMap object.
|
||||||
|
func UpdateConfigMap(c clientset.Interface, name, namespace, key, data string) error {
|
||||||
|
cm, err := c.CoreV1().ConfigMaps(namespace).Get(context.TODO(), name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("configmap %s is not found", name)
|
||||||
|
}
|
||||||
|
cm.Data[key] = data
|
||||||
|
_, err = c.CoreV1().ConfigMaps(namespace).Update(context.TODO(), cm, metav1.UpdateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while updating configmap with name %s", name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue