1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2024-12-15 17:50:49 +00:00

Merge pull request #307 from marquiz/devel/label-whitelist

nfd-worker: fix --label-whitelist
This commit is contained in:
Kubernetes Prow Robot 2020-05-20 13:54:20 -07:00 committed by GitHub
commit ad5eef1514
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 39 deletions

View file

@ -74,7 +74,10 @@ nfd-master.
has been enabled.
--no-publish Do not publish feature labels
--label-whitelist=<pattern> Regular expression to filter label names to
publish to the Kubernetes API server. [Default: ]
publish to the Kubernetes API server.
NB: the label namespace is omitted i.e. the filter
is only applied to the name part after '/'.
[Default: ]
--extra-label-ns=<list> Comma separated list of allowed extra label namespaces
[Default: ]
--resource-labels=<list> Comma separated list of labels to be exposed as extended resources.
@ -129,11 +132,14 @@ nfd-worker.
in testing
[Default: ]
--sources=<sources> Comma separated list of feature sources.
[Default: cpu,iommu,kernel,local,memory,network,pci,storage,system,usb]
[Default: cpu,custom,iommu,kernel,local,memory,network,pci,storage,system,usb]
--no-publish Do not publish discovered features to the
cluster-local Kubernetes API server.
--label-whitelist=<pattern> Regular expression to filter label names to
publish to the Kubernetes API server. [Default: ]
publish to the Kubernetes API server.
NB: the label namespace is omitted i.e. the filter
is only applied to the name part after '/'.
[Default: ]
--oneshot Label once and exit.
--sleep-interval=<seconds> Time to sleep between re-labeling. Non-positive
value implies no re-labeling (i.e. infinite

View file

@ -85,7 +85,10 @@ func argsParse(argv []string) (master.Args, error) {
has been enabled.
--no-publish Do not publish feature labels
--label-whitelist=<pattern> Regular expression to filter label names to
publish to the Kubernetes API server. [Default: ]
publish to the Kubernetes API server.
NB: the label namespace is omitted i.e. the filter
is only applied to the name part after '/'.
[Default: ]
--extra-label-ns=<list> Comma separated list of allowed extra label namespaces
[Default: ]
--resource-labels=<list> Comma separated list of labels to be exposed as extended resources.

View file

@ -95,7 +95,10 @@ func argsParse(argv []string) (worker.Args, error) {
--no-publish Do not publish discovered features to the
cluster-local Kubernetes API server.
--label-whitelist=<pattern> Regular expression to filter label names to
publish to the Kubernetes API server. [Default: ]
publish to the Kubernetes API server.
NB: the label namespace is omitted i.e. the filter
is only applied to the name part after '/'.
[Default: ]
--oneshot Label once and exit.
--sleep-interval=<seconds> Time to sleep between re-labeling. Non-positive
value implies no re-labeling (i.e. infinite

View file

@ -21,6 +21,7 @@ import (
"io/ioutil"
"os"
"regexp"
"strings"
"testing"
. "github.com/smartystreets/goconvey/convey"
@ -32,25 +33,26 @@ import (
"sigs.k8s.io/node-feature-discovery/source/panic_fake"
)
const fakeFeatureSourceName string = "testSource"
func TestDiscoveryWithMockSources(t *testing.T) {
Convey("When I discover features from fake source and update the node using fake client", t, func() {
mockFeatureSource := new(source.MockFeatureSource)
fakeFeatureSourceName := string("testSource")
fakeFeatureNames := []string{"testfeature1", "testfeature2", "testfeature3"}
fakeFeatures := source.Features{}
fakeFeatureLabels := Labels{}
for _, f := range fakeFeatureNames {
fakeFeatures[f] = true
labelName := fakeFeatureSourceName + "-" + f
fakeFeatureLabels[labelName] = "true"
}
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)
fakeFeatureSource := source.FeatureSource(mockFeatureSource)
labelWhiteList := regexp.MustCompile("^test")
Convey("When I successfully get the labels from the mock source", func() {
mockFeatureSource.On("Name").Return(fakeFeatureSourceName)
mockFeatureSource.On("Discover").Return(fakeFeatures, nil)
returnedLabels, err := getFeatureLabels(fakeFeatureSource)
returnedLabels, err := getFeatureLabels(fakeFeatureSource, labelWhiteList)
Convey("Proper label is returned", func() {
So(returnedLabels, ShouldResemble, fakeFeatureLabels)
})
@ -63,7 +65,7 @@ func TestDiscoveryWithMockSources(t *testing.T) {
expectedError := errors.New("fake error")
mockFeatureSource.On("Discover").Return(nil, expectedError)
returnedLabels, err := getFeatureLabels(fakeFeatureSource)
returnedLabels, err := getFeatureLabels(fakeFeatureSource, labelWhiteList)
Convey("No label is returned", func() {
So(returnedLabels, ShouldBeNil)
})
@ -74,6 +76,21 @@ func TestDiscoveryWithMockSources(t *testing.T) {
})
}
func makeFakeFeatures(names []string) (source.Features, Labels) {
features := source.Features{}
labels := Labels{}
for _, f := range names {
features[f] = true
labelName := fakeFeatureSourceName + "-" + f
if strings.IndexByte(f, '/') >= 0 {
labelName = f
}
labels[labelName] = "true"
}
return features, labels
}
func TestConfigParse(t *testing.T) {
Convey("When parsing configuration file", t, func() {
Convey("When non-accessible file is given", func() {
@ -209,7 +226,7 @@ func TestGetFeatureLabels(t *testing.T) {
Convey("When I get feature labels and panic occurs during discovery of a feature source", t, func() {
fakePanicFeatureSource := source.FeatureSource(new(panicfake.Source))
returnedLabels, err := getFeatureLabels(fakePanicFeatureSource)
returnedLabels, err := getFeatureLabels(fakePanicFeatureSource, regexp.MustCompile(""))
Convey("No label is returned", func() {
So(len(returnedLabels), ShouldEqual, 0)
})

View file

@ -315,7 +315,7 @@ func createFeatureLabels(sources []source.FeatureSource, labelWhiteList *regexp.
// Do feature discovery from all configured sources.
for _, source := range sources {
labelsFromSource, err := getFeatureLabels(source)
labelsFromSource, err := getFeatureLabels(source, labelWhiteList)
if err != nil {
stderrLogger.Printf("discovery failed for source [%s]: %s", source.Name(), err.Error())
stderrLogger.Printf("continuing ...")
@ -325,11 +325,6 @@ func createFeatureLabels(sources []source.FeatureSource, labelWhiteList *regexp.
for name, value := range labelsFromSource {
// Log discovered feature.
stdoutLogger.Printf("%s = %s", name, value)
// Skip if label doesn't match labelWhiteList
if !labelWhiteList.Match([]byte(name)) {
stderrLogger.Printf("%s does not match the whitelist (%s) and will not be published.", name, labelWhiteList.String())
continue
}
labels[name] = value
}
}
@ -338,7 +333,7 @@ func createFeatureLabels(sources []source.FeatureSource, labelWhiteList *regexp.
// getFeatureLabels returns node labels for features discovered by the
// supplied source.
func getFeatureLabels(source source.FeatureSource) (labels Labels, err error) {
func getFeatureLabels(source source.FeatureSource, labelWhiteList *regexp.Regexp) (labels Labels, err error) {
defer func() {
if r := recover(); r != nil {
stderrLogger.Printf("panic occurred during discovery of source [%s]: %v", source.Name(), r)
@ -351,24 +346,33 @@ func getFeatureLabels(source source.FeatureSource) (labels Labels, err error) {
if err != nil {
return nil, err
}
// Prefix for labels in the default namespace
prefix := source.Name() + "-"
switch source.(type) {
case local.Source:
// Do not prefix labels from the hooks
prefix = ""
}
for k, v := range features {
// Validate label name
prefix := source.Name() + "-"
switch source.(type) {
case local.Source:
// Do not prefix labels from the hooks
prefix = ""
// Split label name into namespace and name compoents. Use dummy 'ns'
// default namespace because there is no function to validate just
// the name part
split := strings.SplitN(k, "/", 2)
label := prefix + split[0]
nameForValidation := "ns/" + label
nameForWhiteListing := label
if len(split) == 2 {
label = k
nameForValidation = label
nameForWhiteListing = split[1]
}
label := prefix + k
// Validate label name. Use dummy namespace 'ns' because there is no
// function to validate just the name part
labelName := "ns/" + label
// Do not use dummy namespace if there is already a namespace
if strings.Contains(label, "/") {
labelName = label
}
errs := validation.IsQualifiedName(labelName)
// Validate label name.
errs := validation.IsQualifiedName(nameForValidation)
if len(errs) > 0 {
stderrLogger.Printf("Ignoring invalid feature name '%s': %s", label, errs)
continue
@ -382,6 +386,12 @@ func getFeatureLabels(source source.FeatureSource) (labels Labels, err error) {
continue
}
// Skip if label doesn't match labelWhiteList
if !labelWhiteList.MatchString(nameForWhiteListing) {
stderrLogger.Printf("%q does not match the whitelist (%s) and will not be published.", nameForWhiteListing, labelWhiteList.String())
continue
}
labels[label] = value
}
return labels, nil