2019-02-08 21:43:54 +02:00
|
|
|
/*
|
2021-02-19 15:43:31 +02:00
|
|
|
Copyright 2019-2021 The Kubernetes Authors.
|
2019-02-08 21:43:54 +02:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2022-12-23 10:45:07 +02:00
|
|
|
package nfdworker
|
2019-02-08 21:43:54 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
2021-02-11 22:12:49 +02:00
|
|
|
"path/filepath"
|
2019-02-08 21:43:54 +02:00
|
|
|
"regexp"
|
nfd-worker: fix --label-whitelist
Unify handling of --label-whitelist in nfd-worker and nfd-master. That is,
in nfd-worker, apply the regexp filter on non-namespaced part of the
label name.
Brief history:
1. Originally the whitelist regexp was applied on the full namespaced
label name (that would be e.g.
'feature.node.kubernetes.io/cpu-cpuid.AVX' in the current nfd version)
2. Commit 81752b2d changed the behavior so that the regexp was applied
on the non-namespaced part (that would be `cpu-cpuid.AVX`)
3. Commit 40918827 added support for custom label namespaces. With this
change, the label whitelist handling diverged between nfd-worker and
nfd-master. In nfd-master the whitelist regexp is always applied on
the non-namespaced label name. However, in nfd-worker the whitelist
handling is two-fold (and inconsistent): for labels in the standard
nfd namespace regexp is applied on the non-namespaced part (e.g.
`cpu-cpuid.AVX`, but, for labels in custom namespaces the regexp is
applied on the full name (e.g. `example.com/my-feature`).
This patch changes nfd-worker to behave similarly to nfd-master. The
namespace part is now always omitted, which should be easier for the
users to comprehend.
Also, fixes a bug in the label name prefixing so that the name of the
feature source is not prefixed into labels with custom label namespace
(effectively mangling the intended namespace). For example, previously a
'example.com/feature' label from the 'custom' feature source would be
prefixed with the source name, mangling it to
'custom-example.com/feature'.
2020-04-28 10:38:38 +03:00
|
|
|
"strings"
|
2019-02-08 21:43:54 +02:00
|
|
|
"testing"
|
2021-02-11 22:12:49 +02:00
|
|
|
"time"
|
2019-02-08 21:43:54 +02:00
|
|
|
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"github.com/stretchr/testify/mock"
|
|
|
|
"github.com/vektra/errors"
|
2024-07-05 15:22:58 +02:00
|
|
|
fakeclient "k8s.io/client-go/kubernetes/fake"
|
2021-02-11 22:12:49 +02:00
|
|
|
|
2024-02-27 14:42:23 +01:00
|
|
|
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
|
2019-02-08 21:43:54 +02:00
|
|
|
"sigs.k8s.io/node-feature-discovery/pkg/labeler"
|
2021-02-19 15:43:31 +02:00
|
|
|
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
2019-02-08 21:43:54 +02:00
|
|
|
"sigs.k8s.io/node-feature-discovery/source"
|
2020-04-21 22:03:37 +03:00
|
|
|
"sigs.k8s.io/node-feature-discovery/source/cpu"
|
|
|
|
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
|
|
|
"sigs.k8s.io/node-feature-discovery/source/pci"
|
2019-02-08 21:43:54 +02:00
|
|
|
)
|
|
|
|
|
2021-03-01 07:45:32 +02:00
|
|
|
const fakeLabelSourceName string = "testSource"
|
nfd-worker: fix --label-whitelist
Unify handling of --label-whitelist in nfd-worker and nfd-master. That is,
in nfd-worker, apply the regexp filter on non-namespaced part of the
label name.
Brief history:
1. Originally the whitelist regexp was applied on the full namespaced
label name (that would be e.g.
'feature.node.kubernetes.io/cpu-cpuid.AVX' in the current nfd version)
2. Commit 81752b2d changed the behavior so that the regexp was applied
on the non-namespaced part (that would be `cpu-cpuid.AVX`)
3. Commit 40918827 added support for custom label namespaces. With this
change, the label whitelist handling diverged between nfd-worker and
nfd-master. In nfd-master the whitelist regexp is always applied on
the non-namespaced label name. However, in nfd-worker the whitelist
handling is two-fold (and inconsistent): for labels in the standard
nfd namespace regexp is applied on the non-namespaced part (e.g.
`cpu-cpuid.AVX`, but, for labels in custom namespaces the regexp is
applied on the full name (e.g. `example.com/my-feature`).
This patch changes nfd-worker to behave similarly to nfd-master. The
namespace part is now always omitted, which should be easier for the
users to comprehend.
Also, fixes a bug in the label name prefixing so that the name of the
feature source is not prefixed into labels with custom label namespace
(effectively mangling the intended namespace). For example, previously a
'example.com/feature' label from the 'custom' feature source would be
prefixed with the source name, mangling it to
'custom-example.com/feature'.
2020-04-28 10:38:38 +03:00
|
|
|
|
2021-03-01 18:39:49 +02:00
|
|
|
func TestGetLabelsWithMockSources(t *testing.T) {
|
2019-02-08 21:43:54 +02:00
|
|
|
Convey("When I discover features from fake source and update the node using fake client", t, func() {
|
2021-03-01 07:45:32 +02:00
|
|
|
mockLabelSource := new(source.MockLabelSource)
|
nfd-worker: fix --label-whitelist
Unify handling of --label-whitelist in nfd-worker and nfd-master. That is,
in nfd-worker, apply the regexp filter on non-namespaced part of the
label name.
Brief history:
1. Originally the whitelist regexp was applied on the full namespaced
label name (that would be e.g.
'feature.node.kubernetes.io/cpu-cpuid.AVX' in the current nfd version)
2. Commit 81752b2d changed the behavior so that the regexp was applied
on the non-namespaced part (that would be `cpu-cpuid.AVX`)
3. Commit 40918827 added support for custom label namespaces. With this
change, the label whitelist handling diverged between nfd-worker and
nfd-master. In nfd-master the whitelist regexp is always applied on
the non-namespaced label name. However, in nfd-worker the whitelist
handling is two-fold (and inconsistent): for labels in the standard
nfd namespace regexp is applied on the non-namespaced part (e.g.
`cpu-cpuid.AVX`, but, for labels in custom namespaces the regexp is
applied on the full name (e.g. `example.com/my-feature`).
This patch changes nfd-worker to behave similarly to nfd-master. The
namespace part is now always omitted, which should be easier for the
users to comprehend.
Also, fixes a bug in the label name prefixing so that the name of the
feature source is not prefixed into labels with custom label namespace
(effectively mangling the intended namespace). For example, previously a
'example.com/feature' label from the 'custom' feature source would be
prefixed with the source name, mangling it to
'custom-example.com/feature'.
2020-04-28 10:38:38 +03:00
|
|
|
allFeatureNames := []string{"testfeature1", "testfeature2", "test.ns/test", "test.ns/foo", "/no-ns-label", "invalid/test/feature"}
|
|
|
|
whiteListFeatureNames := []string{"testfeature1", "testfeature2", "test.ns/test"}
|
|
|
|
|
|
|
|
fakeFeatures, _ := makeFakeFeatures(allFeatureNames)
|
|
|
|
_, fakeFeatureLabels := makeFakeFeatures(whiteListFeatureNames)
|
|
|
|
|
2021-03-01 07:45:32 +02:00
|
|
|
fakeLabelSource := source.LabelSource(mockLabelSource)
|
2019-02-08 21:43:54 +02:00
|
|
|
|
2021-02-19 15:43:31 +02:00
|
|
|
labelWhiteList := utils.RegexpVal{Regexp: *regexp.MustCompile("^test")}
|
nfd-worker: fix --label-whitelist
Unify handling of --label-whitelist in nfd-worker and nfd-master. That is,
in nfd-worker, apply the regexp filter on non-namespaced part of the
label name.
Brief history:
1. Originally the whitelist regexp was applied on the full namespaced
label name (that would be e.g.
'feature.node.kubernetes.io/cpu-cpuid.AVX' in the current nfd version)
2. Commit 81752b2d changed the behavior so that the regexp was applied
on the non-namespaced part (that would be `cpu-cpuid.AVX`)
3. Commit 40918827 added support for custom label namespaces. With this
change, the label whitelist handling diverged between nfd-worker and
nfd-master. In nfd-master the whitelist regexp is always applied on
the non-namespaced label name. However, in nfd-worker the whitelist
handling is two-fold (and inconsistent): for labels in the standard
nfd namespace regexp is applied on the non-namespaced part (e.g.
`cpu-cpuid.AVX`, but, for labels in custom namespaces the regexp is
applied on the full name (e.g. `example.com/my-feature`).
This patch changes nfd-worker to behave similarly to nfd-master. The
namespace part is now always omitted, which should be easier for the
users to comprehend.
Also, fixes a bug in the label name prefixing so that the name of the
feature source is not prefixed into labels with custom label namespace
(effectively mangling the intended namespace). For example, previously a
'example.com/feature' label from the 'custom' feature source would be
prefixed with the source name, mangling it to
'custom-example.com/feature'.
2020-04-28 10:38:38 +03:00
|
|
|
|
2019-02-08 21:43:54 +02:00
|
|
|
Convey("When I successfully get the labels from the mock source", func() {
|
2021-03-01 07:45:32 +02:00
|
|
|
mockLabelSource.On("Name").Return(fakeLabelSourceName)
|
2021-03-01 18:39:49 +02:00
|
|
|
mockLabelSource.On("GetLabels").Return(fakeFeatures, nil)
|
2019-02-08 21:43:54 +02:00
|
|
|
|
2021-03-01 07:45:32 +02:00
|
|
|
returnedLabels, err := getFeatureLabels(fakeLabelSource, labelWhiteList.Regexp)
|
2019-02-08 21:43:54 +02:00
|
|
|
Convey("Proper label is returned", func() {
|
|
|
|
So(returnedLabels, ShouldResemble, fakeFeatureLabels)
|
|
|
|
})
|
|
|
|
Convey("Error is nil", func() {
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
Convey("When I fail to get the labels from the mock source", func() {
|
|
|
|
expectedError := errors.New("fake error")
|
2021-03-01 18:39:49 +02:00
|
|
|
mockLabelSource.On("GetLabels").Return(nil, expectedError)
|
2019-02-08 21:43:54 +02:00
|
|
|
|
2021-03-01 07:45:32 +02:00
|
|
|
returnedLabels, err := getFeatureLabels(fakeLabelSource, labelWhiteList.Regexp)
|
2019-02-08 21:43:54 +02:00
|
|
|
Convey("No label is returned", func() {
|
|
|
|
So(returnedLabels, ShouldBeNil)
|
|
|
|
})
|
|
|
|
Convey("Error is produced", func() {
|
|
|
|
So(err, ShouldEqual, expectedError)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-03-01 07:45:32 +02:00
|
|
|
func makeFakeFeatures(names []string) (source.FeatureLabels, Labels) {
|
|
|
|
features := source.FeatureLabels{}
|
nfd-worker: fix --label-whitelist
Unify handling of --label-whitelist in nfd-worker and nfd-master. That is,
in nfd-worker, apply the regexp filter on non-namespaced part of the
label name.
Brief history:
1. Originally the whitelist regexp was applied on the full namespaced
label name (that would be e.g.
'feature.node.kubernetes.io/cpu-cpuid.AVX' in the current nfd version)
2. Commit 81752b2d changed the behavior so that the regexp was applied
on the non-namespaced part (that would be `cpu-cpuid.AVX`)
3. Commit 40918827 added support for custom label namespaces. With this
change, the label whitelist handling diverged between nfd-worker and
nfd-master. In nfd-master the whitelist regexp is always applied on
the non-namespaced label name. However, in nfd-worker the whitelist
handling is two-fold (and inconsistent): for labels in the standard
nfd namespace regexp is applied on the non-namespaced part (e.g.
`cpu-cpuid.AVX`, but, for labels in custom namespaces the regexp is
applied on the full name (e.g. `example.com/my-feature`).
This patch changes nfd-worker to behave similarly to nfd-master. The
namespace part is now always omitted, which should be easier for the
users to comprehend.
Also, fixes a bug in the label name prefixing so that the name of the
feature source is not prefixed into labels with custom label namespace
(effectively mangling the intended namespace). For example, previously a
'example.com/feature' label from the 'custom' feature source would be
prefixed with the source name, mangling it to
'custom-example.com/feature'.
2020-04-28 10:38:38 +03:00
|
|
|
labels := Labels{}
|
|
|
|
for _, f := range names {
|
|
|
|
features[f] = true
|
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.
2023-11-08 09:51:19 +02:00
|
|
|
labelName := nfdv1alpha1.FeatureLabelNs + "/" + fakeLabelSourceName + "-" + f
|
nfd-worker: fix --label-whitelist
Unify handling of --label-whitelist in nfd-worker and nfd-master. That is,
in nfd-worker, apply the regexp filter on non-namespaced part of the
label name.
Brief history:
1. Originally the whitelist regexp was applied on the full namespaced
label name (that would be e.g.
'feature.node.kubernetes.io/cpu-cpuid.AVX' in the current nfd version)
2. Commit 81752b2d changed the behavior so that the regexp was applied
on the non-namespaced part (that would be `cpu-cpuid.AVX`)
3. Commit 40918827 added support for custom label namespaces. With this
change, the label whitelist handling diverged between nfd-worker and
nfd-master. In nfd-master the whitelist regexp is always applied on
the non-namespaced label name. However, in nfd-worker the whitelist
handling is two-fold (and inconsistent): for labels in the standard
nfd namespace regexp is applied on the non-namespaced part (e.g.
`cpu-cpuid.AVX`, but, for labels in custom namespaces the regexp is
applied on the full name (e.g. `example.com/my-feature`).
This patch changes nfd-worker to behave similarly to nfd-master. The
namespace part is now always omitted, which should be easier for the
users to comprehend.
Also, fixes a bug in the label name prefixing so that the name of the
feature source is not prefixed into labels with custom label namespace
(effectively mangling the intended namespace). For example, previously a
'example.com/feature' label from the 'custom' feature source would be
prefixed with the source name, mangling it to
'custom-example.com/feature'.
2020-04-28 10:38:38 +03:00
|
|
|
if strings.IndexByte(f, '/') >= 0 {
|
|
|
|
labelName = f
|
|
|
|
}
|
|
|
|
labels[labelName] = "true"
|
|
|
|
}
|
|
|
|
|
|
|
|
return features, labels
|
|
|
|
}
|
|
|
|
|
2020-04-21 22:03:37 +03:00
|
|
|
func TestConfigParse(t *testing.T) {
|
|
|
|
Convey("When parsing configuration", t, func() {
|
2024-07-05 15:22:58 +02:00
|
|
|
w, err := NewNfdWorker(WithArgs(&Args{}),
|
|
|
|
WithKubernetesClient(fakeclient.NewSimpleClientset()))
|
2020-04-21 22:03:37 +03:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
worker := w.(*nfdWorker)
|
2021-11-25 10:58:08 +02:00
|
|
|
overrides := `{"core": {"labelSources": ["fake"],"noPublish": true},"sources": {"cpu": {"cpuid": {"attributeBlacklist": ["foo","bar"]}}}}`
|
2021-02-11 22:12:49 +02:00
|
|
|
|
|
|
|
Convey("and no core cmdline flags have been specified", func() {
|
|
|
|
So(worker.configure("non-existing-file", overrides), ShouldBeNil)
|
|
|
|
|
|
|
|
Convey("core overrides should be in effect", func() {
|
2021-11-25 10:58:08 +02:00
|
|
|
So(worker.config.Core.LabelSources, ShouldResemble, []string{"fake"})
|
2021-12-03 09:22:43 +02:00
|
|
|
So(worker.config.Core.FeatureSources, ShouldResemble, []string{"all"})
|
2021-02-11 22:12:49 +02:00
|
|
|
So(worker.config.Core.NoPublish, ShouldBeTrue)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
Convey("and a non-accessible file, but core cmdline flags and some overrides are specified", func() {
|
2021-12-03 09:22:43 +02:00
|
|
|
worker.args = Args{Overrides: ConfigOverrideArgs{
|
|
|
|
LabelSources: &utils.StringSliceVal{"cpu", "kernel", "pci"},
|
|
|
|
FeatureSources: &utils.StringSliceVal{"cpu"}}}
|
2020-11-30 17:23:28 +02:00
|
|
|
So(worker.configure("non-existing-file", overrides), ShouldBeNil)
|
2020-04-21 22:03:37 +03:00
|
|
|
|
2021-02-11 22:12:49 +02:00
|
|
|
Convey("core cmdline flags should be in effect instead overrides", func() {
|
2021-11-25 10:58:08 +02:00
|
|
|
So(worker.config.Core.LabelSources, ShouldResemble, []string{"cpu", "kernel", "pci"})
|
2021-12-03 09:22:43 +02:00
|
|
|
So(worker.config.Core.FeatureSources, ShouldResemble, []string{"cpu"})
|
2021-02-11 22:12:49 +02:00
|
|
|
})
|
2020-04-21 22:03:37 +03:00
|
|
|
Convey("overrides should take effect", func() {
|
2021-02-11 22:12:49 +02:00
|
|
|
So(worker.config.Core.NoPublish, ShouldBeTrue)
|
|
|
|
|
2021-03-01 09:02:22 +02:00
|
|
|
c := source.GetConfigurableSource("cpu").GetConfig().(*cpu.Config)
|
2020-04-21 22:03:37 +03:00
|
|
|
So(c.Cpuid.AttributeBlacklist, ShouldResemble, []string{"foo", "bar"})
|
2019-02-08 21:43:54 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
// Create a temporary config file
|
2022-09-08 13:23:49 +03:00
|
|
|
f, err := os.CreateTemp("", "nfd-test-")
|
2019-02-08 21:43:54 +02:00
|
|
|
defer os.Remove(f.Name())
|
|
|
|
So(err, ShouldBeNil)
|
2021-02-11 22:12:49 +02:00
|
|
|
_, err = f.WriteString(`
|
|
|
|
core:
|
|
|
|
noPublish: false
|
2021-12-03 09:22:43 +02:00
|
|
|
featureSources: ["memory", "storage"]
|
2021-02-11 22:12:49 +02:00
|
|
|
sources: ["system"]
|
|
|
|
labelWhiteList: "foo"
|
|
|
|
sleepInterval: "10s"
|
|
|
|
sources:
|
2019-02-08 21:43:54 +02:00
|
|
|
kernel:
|
|
|
|
configOpts:
|
|
|
|
- "DMI"
|
|
|
|
pci:
|
|
|
|
deviceClassWhitelist:
|
|
|
|
- "ff"`)
|
|
|
|
f.Close()
|
2020-05-19 14:57:39 +03:00
|
|
|
So(err, ShouldBeNil)
|
2019-02-08 21:43:54 +02:00
|
|
|
|
2020-04-21 22:03:37 +03:00
|
|
|
Convey("and a proper config file is specified", func() {
|
2021-11-25 10:58:08 +02:00
|
|
|
worker.args = Args{Overrides: ConfigOverrideArgs{LabelSources: &utils.StringSliceVal{"cpu", "kernel", "pci"}}}
|
2020-11-30 17:23:28 +02:00
|
|
|
So(worker.configure(f.Name(), ""), ShouldBeNil)
|
2020-04-21 22:03:37 +03:00
|
|
|
|
|
|
|
Convey("specified configuration should take effect", func() {
|
2021-02-11 22:12:49 +02:00
|
|
|
// Verify core config
|
|
|
|
So(worker.config.Core.NoPublish, ShouldBeFalse)
|
2021-12-03 09:22:43 +02:00
|
|
|
So(worker.config.Core.FeatureSources, ShouldResemble, []string{"memory", "storage"})
|
2021-11-25 10:58:08 +02:00
|
|
|
So(worker.config.Core.LabelSources, ShouldResemble, []string{"cpu", "kernel", "pci"}) // from cmdline
|
2021-02-11 22:12:49 +02:00
|
|
|
So(worker.config.Core.LabelWhiteList.String(), ShouldEqual, "foo")
|
|
|
|
So(worker.config.Core.SleepInterval.Duration, ShouldEqual, 10*time.Second)
|
|
|
|
|
|
|
|
// Verify feature source config
|
2020-04-21 22:03:37 +03:00
|
|
|
So(err, ShouldBeNil)
|
2021-03-01 09:02:22 +02:00
|
|
|
c := source.GetConfigurableSource("kernel").GetConfig()
|
2020-04-21 22:03:37 +03:00
|
|
|
So(c.(*kernel.Config).ConfigOpts, ShouldResemble, []string{"DMI"})
|
2021-03-01 09:02:22 +02:00
|
|
|
c = source.GetConfigurableSource("pci").GetConfig()
|
2020-04-21 22:03:37 +03:00
|
|
|
So(c.(*pci.Config).DeviceClassWhitelist, ShouldResemble, []string{"ff"})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
Convey("and a proper config file and overrides are given", func() {
|
2022-11-22 12:36:32 +02:00
|
|
|
worker.args = Args{Overrides: ConfigOverrideArgs{FeatureSources: &utils.StringSliceVal{"cpu"}}}
|
2021-11-25 10:58:08 +02:00
|
|
|
overrides := `{"core": {"labelSources": ["fake"],"noPublish": true},"sources": {"pci": {"deviceClassWhitelist": ["03"]}}}`
|
2020-11-30 17:23:28 +02:00
|
|
|
So(worker.configure(f.Name(), overrides), ShouldBeNil)
|
2019-02-08 21:43:54 +02:00
|
|
|
|
2020-04-21 22:03:37 +03:00
|
|
|
Convey("overrides should take precedence over the config file", func() {
|
2021-02-11 22:12:49 +02:00
|
|
|
// Verify core config
|
|
|
|
So(worker.config.Core.NoPublish, ShouldBeTrue)
|
2022-11-22 12:36:32 +02:00
|
|
|
So(worker.config.Core.FeatureSources, ShouldResemble, []string{"cpu"}) // from cmdline
|
|
|
|
So(worker.config.Core.LabelSources, ShouldResemble, []string{"fake"}) // from overrides
|
2021-02-11 22:12:49 +02:00
|
|
|
So(worker.config.Core.LabelWhiteList.String(), ShouldEqual, "foo")
|
|
|
|
|
|
|
|
// Verify feature source config
|
2019-02-08 21:43:54 +02:00
|
|
|
So(err, ShouldBeNil)
|
2021-03-01 09:02:22 +02:00
|
|
|
c := source.GetConfigurableSource("kernel").GetConfig()
|
2020-04-21 22:03:37 +03:00
|
|
|
So(c.(*kernel.Config).ConfigOpts, ShouldResemble, []string{"DMI"})
|
2021-03-01 09:02:22 +02:00
|
|
|
c = source.GetConfigurableSource("pci").GetConfig()
|
2020-04-21 22:03:37 +03:00
|
|
|
So(c.(*pci.Config).DeviceClassWhitelist, ShouldResemble, []string{"03"})
|
2019-02-08 21:43:54 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-02-11 22:12:49 +02:00
|
|
|
func TestDynamicConfig(t *testing.T) {
|
|
|
|
Convey("When running nfd-worker", t, func() {
|
2022-09-08 13:23:49 +03:00
|
|
|
tmpDir, err := os.MkdirTemp("", "*.nfd-test")
|
2021-02-11 22:12:49 +02:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
|
|
|
|
// Create (temporary) dir for config
|
|
|
|
configDir := filepath.Join(tmpDir, "subdir-1", "subdir-2", "worker.conf")
|
|
|
|
err = os.MkdirAll(configDir, 0755)
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
|
|
|
// Create config file
|
|
|
|
configFile := filepath.Join(configDir, "worker.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(`
|
|
|
|
core:
|
|
|
|
labelWhiteList: "fake"
|
|
|
|
`)
|
|
|
|
|
|
|
|
noPublish := true
|
2024-07-05 15:22:58 +02:00
|
|
|
w, err := NewNfdWorker(WithArgs(&Args{
|
2021-02-19 15:43:31 +02:00
|
|
|
ConfigFile: configFile,
|
|
|
|
Overrides: ConfigOverrideArgs{
|
2021-12-03 08:42:59 +02:00
|
|
|
FeatureSources: &utils.StringSliceVal{"fake"},
|
|
|
|
LabelSources: &utils.StringSliceVal{"fake"},
|
|
|
|
NoPublish: &noPublish},
|
2024-07-05 15:22:58 +02:00
|
|
|
}), WithKubernetesClient(fakeclient.NewSimpleClientset()))
|
2021-02-11 22:12:49 +02:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
worker := w.(*nfdWorker)
|
|
|
|
|
|
|
|
Convey("config file updates should take effect", func() {
|
|
|
|
go func() { _ = w.Run() }()
|
|
|
|
defer w.Stop()
|
|
|
|
|
|
|
|
// Check initial config
|
|
|
|
So(func() interface{} { return worker.config.Core.LabelWhiteList.String() },
|
|
|
|
withTimeout, 2*time.Second, ShouldEqual, "fake")
|
|
|
|
|
|
|
|
// Update config and verify the effect
|
|
|
|
writeConfig(`
|
|
|
|
core:
|
|
|
|
labelWhiteList: "foo"
|
|
|
|
`)
|
|
|
|
So(func() interface{} { return worker.config.Core.LabelWhiteList.String() },
|
|
|
|
withTimeout, 2*time.Second, ShouldEqual, "foo")
|
|
|
|
|
|
|
|
// Removing config file should get back our defaults
|
|
|
|
err = os.RemoveAll(tmpDir)
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
So(func() interface{} { return worker.config.Core.LabelWhiteList.String() },
|
|
|
|
withTimeout, 2*time.Second, ShouldEqual, "")
|
|
|
|
|
|
|
|
// Re-creating config dir and file should change the config
|
|
|
|
err = os.MkdirAll(configDir, 0755)
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
writeConfig(`
|
|
|
|
core:
|
|
|
|
labelWhiteList: "bar"
|
|
|
|
`)
|
|
|
|
So(func() interface{} { return worker.config.Core.LabelWhiteList.String() },
|
|
|
|
withTimeout, 2*time.Second, ShouldEqual, "bar")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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):
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 19:40:01 +03:00
|
|
|
func TestNewNfdWorker(t *testing.T) {
|
|
|
|
Convey("When creating new NfdWorker instance", t, func() {
|
2019-02-08 21:43:54 +02:00
|
|
|
|
2021-02-19 15:43:31 +02:00
|
|
|
emptyRegexp := utils.RegexpVal{Regexp: *regexp.MustCompile("")}
|
2020-12-01 14:54:59 +02:00
|
|
|
|
2020-04-21 19:40:01 +03:00
|
|
|
Convey("without any args specified", func() {
|
2021-02-19 15:43:31 +02:00
|
|
|
args := &Args{}
|
2024-07-05 15:22:58 +02:00
|
|
|
w, err := NewNfdWorker(WithArgs(args),
|
|
|
|
WithKubernetesClient(fakeclient.NewSimpleClientset()))
|
2020-04-21 19:40:01 +03:00
|
|
|
Convey("no error should be returned", func() {
|
2019-02-08 21:43:54 +02:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
})
|
2020-04-21 22:03:37 +03:00
|
|
|
worker := w.(*nfdWorker)
|
2020-11-30 17:23:28 +02:00
|
|
|
So(worker.configure("", ""), ShouldBeNil)
|
2020-12-01 15:53:04 +02:00
|
|
|
Convey("all sources should be enabled and the whitelist regexp should be empty", func() {
|
2021-12-03 08:42:59 +02:00
|
|
|
So(len(worker.featureSources), ShouldEqual, len(source.GetAllFeatureSources())-1)
|
2021-11-25 17:18:11 +02:00
|
|
|
So(len(worker.labelSources), ShouldEqual, len(source.GetAllLabelSources())-1)
|
2020-12-01 14:54:59 +02:00
|
|
|
So(worker.config.Core.LabelWhiteList, ShouldResemble, emptyRegexp)
|
2019-02-08 21:43:54 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-04-21 19:40:01 +03:00
|
|
|
Convey("with non-empty Sources arg specified", func() {
|
2021-12-03 09:22:43 +02:00
|
|
|
args := &Args{Overrides: ConfigOverrideArgs{
|
|
|
|
LabelSources: &utils.StringSliceVal{"fake"},
|
|
|
|
FeatureSources: &utils.StringSliceVal{"cpu"}}}
|
2024-07-05 15:22:58 +02:00
|
|
|
w, err := NewNfdWorker(WithArgs(args),
|
|
|
|
WithKubernetesClient(fakeclient.NewSimpleClientset()))
|
2020-04-21 19:40:01 +03:00
|
|
|
Convey("no error should be returned", func() {
|
2019-02-08 21:43:54 +02:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
})
|
2020-04-21 22:03:37 +03:00
|
|
|
worker := w.(*nfdWorker)
|
2020-11-30 17:23:28 +02:00
|
|
|
So(worker.configure("", ""), ShouldBeNil)
|
2020-04-21 19:40:01 +03:00
|
|
|
Convey("proper sources should be enabled", func() {
|
2021-12-03 09:22:43 +02:00
|
|
|
So(len(worker.featureSources), ShouldEqual, 1)
|
|
|
|
So(worker.featureSources[0].Name(), ShouldEqual, "cpu")
|
2021-08-27 12:21:16 +03:00
|
|
|
So(len(worker.labelSources), ShouldEqual, 1)
|
|
|
|
So(worker.labelSources[0].Name(), ShouldEqual, "fake")
|
2020-12-01 14:54:59 +02:00
|
|
|
So(worker.config.Core.LabelWhiteList, ShouldResemble, emptyRegexp)
|
2019-02-08 21:43:54 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCreateFeatureLabels(t *testing.T) {
|
|
|
|
Convey("When creating feature labels from the configured sources", t, func() {
|
2021-03-01 09:02:22 +02:00
|
|
|
cs := source.GetConfigurableSource("fake")
|
|
|
|
cs.SetConfig(cs.NewConfig())
|
|
|
|
sources := []source.LabelSource{source.GetLabelSource("fake")}
|
2021-03-01 09:14:19 +02:00
|
|
|
|
2019-02-08 21:43:54 +02:00
|
|
|
Convey("When fake feature source is configured", func() {
|
2021-02-19 15:43:31 +02:00
|
|
|
emptyLabelWL := regexp.MustCompile("")
|
|
|
|
labels := createFeatureLabels(sources, *emptyLabelWL)
|
2019-02-08 21:43:54 +02:00
|
|
|
|
|
|
|
Convey("Proper fake labels are returned", func() {
|
|
|
|
So(len(labels), ShouldEqual, 3)
|
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.
2023-11-08 09:51:19 +02:00
|
|
|
So(labels, ShouldContainKey, nfdv1alpha1.FeatureLabelNs+"/"+"fake-fakefeature1")
|
|
|
|
So(labels, ShouldContainKey, nfdv1alpha1.FeatureLabelNs+"/"+"fake-fakefeature2")
|
|
|
|
So(labels, ShouldContainKey, nfdv1alpha1.FeatureLabelNs+"/"+"fake-fakefeature3")
|
2019-02-08 21:43:54 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
Convey("When fake feature source is configured with a whitelist that doesn't match", func() {
|
2021-02-19 15:43:31 +02:00
|
|
|
labels := createFeatureLabels(sources, *regexp.MustCompile(".*rdt.*"))
|
2019-02-08 21:43:54 +02:00
|
|
|
|
|
|
|
Convey("fake labels are not returned", func() {
|
|
|
|
So(len(labels), ShouldEqual, 0)
|
|
|
|
So(labels, ShouldNotContainKey, "fake-fakefeature1")
|
|
|
|
So(labels, ShouldNotContainKey, "fake-fakefeature2")
|
|
|
|
So(labels, ShouldNotContainKey, "fake-fakefeature3")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAdvertiseFeatureLabels(t *testing.T) {
|
|
|
|
Convey("When advertising labels", t, func() {
|
2024-07-05 15:22:58 +02:00
|
|
|
w, err := NewNfdWorker(WithArgs(&Args{}), WithKubernetesClient(fakeclient.NewSimpleClientset()))
|
2021-08-27 12:52:24 +03:00
|
|
|
So(err, ShouldBeNil)
|
|
|
|
worker := w.(*nfdWorker)
|
|
|
|
|
2019-02-08 21:43:54 +02:00
|
|
|
mockClient := &labeler.MockLabelerClient{}
|
2022-08-12 12:27:20 +03:00
|
|
|
worker.grpcClient = mockClient
|
2021-08-27 12:52:24 +03:00
|
|
|
|
2019-02-08 21:43:54 +02:00
|
|
|
labels := map[string]string{"feature-1": "value-1"}
|
|
|
|
|
|
|
|
Convey("Correct labeling request is sent", func() {
|
|
|
|
mockClient.On("SetLabels", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*labeler.SetLabelsRequest")).Return(&labeler.SetLabelsReply{}, nil)
|
2021-08-27 12:52:24 +03:00
|
|
|
err := worker.advertiseFeatureLabels(labels)
|
2019-02-08 21:43:54 +02:00
|
|
|
Convey("There should be no error", func() {
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
Convey("Labeling request fails", func() {
|
|
|
|
mockErr := errors.New("mock-error")
|
|
|
|
mockClient.On("SetLabels", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*labeler.SetLabelsRequest")).Return(&labeler.SetLabelsReply{}, mockErr)
|
2021-08-27 12:52:24 +03:00
|
|
|
err := worker.advertiseFeatureLabels(labels)
|
2019-02-08 21:43:54 +02:00
|
|
|
Convey("An error should be returned", func() {
|
|
|
|
So(err, ShouldEqual, mockErr)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|