mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-05 08:17:04 +00:00
nfd-worker: switch to flag in command line parsing
This commit is contained in:
parent
47033db9c1
commit
3fd61eacdb
8 changed files with 251 additions and 283 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Copyright 2019-2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -17,14 +17,13 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"os"
|
||||
|
||||
"github.com/docopt/docopt-go"
|
||||
worker "sigs.k8s.io/node-feature-discovery/pkg/nfd-worker"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/version"
|
||||
)
|
||||
|
||||
|
@ -34,17 +33,22 @@ const (
|
|||
)
|
||||
|
||||
func main() {
|
||||
flags := flag.NewFlagSet(ProgramName, flag.ExitOnError)
|
||||
|
||||
printVersion := flags.Bool("version", false, "Print version and exit.")
|
||||
|
||||
args := parseArgs(flags, os.Args[1:]...)
|
||||
|
||||
if *printVersion {
|
||||
fmt.Println(ProgramName, version.Get())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Assert that the version is known
|
||||
if version.Undefined() {
|
||||
log.Printf("WARNING: version not set! Set -ldflags \"-X sigs.k8s.io/node-feature-discovery/pkg/version.version=`git describe --tags --dirty --always`\" during build or run.")
|
||||
}
|
||||
|
||||
// Parse command-line arguments.
|
||||
args, err := argsParse(nil)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse command line: %v", err)
|
||||
}
|
||||
|
||||
// Get new NfdWorker instance
|
||||
instance, err := worker.NewNfdWorker(args)
|
||||
if err != nil {
|
||||
|
@ -56,104 +60,74 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
// argsParse parses the command line arguments passed to the program.
|
||||
// The argument argv is passed only for testing purposes.
|
||||
func argsParse(argv []string) (worker.Args, error) {
|
||||
args := worker.Args{}
|
||||
usage := fmt.Sprintf(`%s.
|
||||
func parseArgs(flags *flag.FlagSet, osArgs ...string) *worker.Args {
|
||||
args, overrides := initFlags(flags)
|
||||
|
||||
Usage:
|
||||
%s [--no-publish] [--sources=<sources>] [--label-whitelist=<pattern>]
|
||||
[--oneshot | --sleep-interval=<seconds>] [--config=<path>]
|
||||
[--options=<config>] [--server=<server>] [--server-name-override=<name>]
|
||||
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
|
||||
%s -h | --help
|
||||
%s --version
|
||||
_ = flags.Parse(osArgs)
|
||||
if len(flags.Args()) > 0 {
|
||||
fmt.Printf("unknown command line argument: %s\n", flags.Args()[0])
|
||||
flags.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
Options:
|
||||
-h --help Show this screen.
|
||||
--version Output version and exit.
|
||||
--config=<path> Config file to use.
|
||||
[Default: /etc/kubernetes/node-feature-discovery/nfd-worker.conf]
|
||||
--options=<config> 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
|
||||
will override settings read from the config file.
|
||||
[Default: ]
|
||||
--ca-file=<path> Root certificate for verifying connections
|
||||
[Default: ]
|
||||
--cert-file=<path> Certificate used for authenticating connections
|
||||
[Default: ]
|
||||
--key-file=<path> Private key matching --cert-file
|
||||
[Default: ]
|
||||
--server=<server> NFD server address to connecto to.
|
||||
[Default: localhost:8080]
|
||||
--server-name-override=<name> Name (CN) expect from server certificate, useful
|
||||
in testing
|
||||
[Default: ]
|
||||
--sources=<sources> Comma separated list of feature sources. Special
|
||||
value 'all' enables all feature sources.
|
||||
(DEPRECATED: This parameter should be set via the
|
||||
config file)
|
||||
--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.
|
||||
NB: the label namespace is omitted i.e. the filter
|
||||
is only applied to the name part after '/'.
|
||||
(DEPRECATED: This parameter should be set via the
|
||||
config file)
|
||||
--oneshot Label once and exit.
|
||||
--sleep-interval=<seconds> Time to sleep between re-labeling. Non-positive
|
||||
value implies no re-labeling (i.e. infinite
|
||||
sleep).
|
||||
(DEPRECATED: This parameter should be set via the
|
||||
config file)`,
|
||||
ProgramName,
|
||||
ProgramName,
|
||||
ProgramName,
|
||||
ProgramName,
|
||||
)
|
||||
|
||||
arguments, _ := docopt.ParseArgs(usage, argv,
|
||||
fmt.Sprintf("%s %s", ProgramName, version.Get()))
|
||||
|
||||
// Parse argument values as usable types.
|
||||
args.CaFile = arguments["--ca-file"].(string)
|
||||
args.CertFile = arguments["--cert-file"].(string)
|
||||
args.ConfigFile = arguments["--config"].(string)
|
||||
args.KeyFile = arguments["--key-file"].(string)
|
||||
args.Options = arguments["--options"].(string)
|
||||
args.Server = arguments["--server"].(string)
|
||||
args.ServerNameOverride = arguments["--server-name-override"].(string)
|
||||
args.Oneshot = arguments["--oneshot"].(bool)
|
||||
|
||||
// Parse deprecated/override args
|
||||
if v := arguments["--label-whitelist"]; v != nil {
|
||||
s := v.(string)
|
||||
// Compile labelWhiteList regex
|
||||
if r, err := regexp.Compile(s); err != nil {
|
||||
return args, fmt.Errorf("error parsing --label-whitelist regex (%s): %v", s, err)
|
||||
} else {
|
||||
args.LabelWhiteList = r
|
||||
// Handle overrides
|
||||
flags.Visit(func(f *flag.Flag) {
|
||||
switch f.Name {
|
||||
case "no-publish":
|
||||
args.Overrides.NoPublish = overrides.NoPublish
|
||||
case "label-whitelist":
|
||||
log.Printf("WARNING: --label-whitelist is deprecated, use 'core.labelWhiteList' option in the config file, instead")
|
||||
args.Overrides.LabelWhiteList = overrides.LabelWhiteList
|
||||
case "sleep-interval":
|
||||
log.Printf("WARNING: --sleep-interval is deprecated, use 'core.sleepInterval' option in the config file, instead")
|
||||
args.Overrides.SleepInterval = overrides.SleepInterval
|
||||
case "sources":
|
||||
log.Printf("WARNING: --sources is deprecated, use 'core.sources' option in the config file, instead")
|
||||
args.Overrides.Sources = overrides.Sources
|
||||
}
|
||||
}
|
||||
if arguments["--no-publish"].(bool) {
|
||||
b := true
|
||||
args.NoPublish = &b
|
||||
}
|
||||
if v := arguments["--sleep-interval"]; v != nil {
|
||||
log.Printf("WARNING: --sleep-interval is deprecated, use 'core.sleepInterval' option in the config file, instead")
|
||||
if s, err := time.ParseDuration(v.(string)); err != nil {
|
||||
return args, fmt.Errorf("invalid --sleep-interval specified: %s", err.Error())
|
||||
} else {
|
||||
args.SleepInterval = &s
|
||||
}
|
||||
}
|
||||
if v := arguments["--sources"]; v != nil {
|
||||
fmt.Println(v)
|
||||
s := strings.Split(v.(string), ",")
|
||||
args.Sources = &s
|
||||
}
|
||||
return args, nil
|
||||
})
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
func initFlags(flagset *flag.FlagSet) (*worker.Args, *worker.ConfigOverrideArgs) {
|
||||
args := &worker.Args{}
|
||||
|
||||
flagset.StringVar(&args.CaFile, "ca-file", "",
|
||||
"Root certificate for verifying connections")
|
||||
flagset.StringVar(&args.CertFile, "cert-file", "",
|
||||
"Certificate used for authenticating connections")
|
||||
flagset.StringVar(&args.ConfigFile, "config", "/etc/kubernetes/node-feature-discovery/nfd-worker.conf",
|
||||
"Config file to use.")
|
||||
flagset.StringVar(&args.KeyFile, "key-file", "",
|
||||
"Private key matching -cert-file")
|
||||
flagset.BoolVar(&args.Oneshot, "oneshot", false,
|
||||
"Do not publish feature labels")
|
||||
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")
|
||||
flagset.StringVar(&args.Server, "server", "localhost:8080",
|
||||
"NFD server address to connecto to.")
|
||||
flagset.StringVar(&args.ServerNameOverride, "server-name-override", "",
|
||||
"Hostname expected from server certificate, useful in testing")
|
||||
|
||||
// Flags overlapping with config file options
|
||||
overrides := &worker.ConfigOverrideArgs{
|
||||
LabelWhiteList: &utils.RegexpVal{},
|
||||
Sources: &utils.StringSliceVal{},
|
||||
}
|
||||
overrides.NoPublish = flagset.Bool("no-publish", false,
|
||||
"Do not publish discovered features, disable connection to nfd-master.")
|
||||
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 '/'. "+
|
||||
"DEPRECATED: This parameter should be set via the config file.")
|
||||
overrides.SleepInterval = flagset.Duration("sleep-interval", 0,
|
||||
"Time to sleep between re-labeling. Non-positive value implies no re-labeling (i.e. infinite sleep). "+
|
||||
"DEPRECATED: This parameter should be set via the config file")
|
||||
flagset.Var(overrides.Sources, "sources",
|
||||
"Comma separated list of feature sources. Special value 'all' enables all feature sources. "+
|
||||
"DEPRECATED: This parameter should be set via the config file")
|
||||
|
||||
return args, overrides
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Copyright 2019-2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -17,62 +17,44 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
||||
)
|
||||
|
||||
func TestArgsParse(t *testing.T) {
|
||||
func TestParseArgs(t *testing.T) {
|
||||
Convey("When parsing command line arguments", t, func() {
|
||||
Convey("When --no-publish and --oneshot flags are passed", func() {
|
||||
args, err := argsParse([]string{"--no-publish", "--oneshot"})
|
||||
flags := flag.NewFlagSet(ProgramName, flag.ExitOnError)
|
||||
|
||||
Convey("noPublish is set and args.sources is set to the default value", func() {
|
||||
So(args.SleepInterval, ShouldEqual, nil)
|
||||
So(*args.NoPublish, ShouldBeTrue)
|
||||
Convey("When no override args are specified", func() {
|
||||
args := parseArgs(flags, "--oneshot")
|
||||
|
||||
Convey("overrides should be nil", func() {
|
||||
So(args.Oneshot, ShouldBeTrue)
|
||||
So(args.Sources, ShouldBeNil)
|
||||
So(args.LabelWhiteList, ShouldBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
So(args.Overrides.NoPublish, ShouldBeNil)
|
||||
So(args.Overrides.LabelWhiteList, ShouldBeNil)
|
||||
So(args.Overrides.SleepInterval, ShouldBeNil)
|
||||
So(args.Overrides.Sources, ShouldBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When --sources flag is passed and set to some values, --sleep-inteval is specified", func() {
|
||||
args, err := argsParse([]string{"--sources=fake1,fake2,fake3", "--sleep-interval=30s"})
|
||||
Convey("When all override args are specified", func() {
|
||||
args := parseArgs(flags,
|
||||
"--no-publish",
|
||||
"-label-whitelist=.*rdt.*",
|
||||
"-sources=fake1,fake2,fake3",
|
||||
"-sleep-interval=30s")
|
||||
|
||||
Convey("args.sources is set to appropriate values", func() {
|
||||
So(*args.SleepInterval, ShouldEqual, 30*time.Second)
|
||||
So(args.NoPublish, ShouldBeNil)
|
||||
So(args.Oneshot, ShouldBeFalse)
|
||||
So(*args.Sources, ShouldResemble, []string{"fake1", "fake2", "fake3"})
|
||||
So(args.LabelWhiteList, ShouldBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When --label-whitelist flag is passed and set to some value", func() {
|
||||
args, err := argsParse([]string{"--label-whitelist=.*rdt.*"})
|
||||
|
||||
Convey("args.labelWhiteList is set to appropriate value and args.sources is set to default value", func() {
|
||||
So(args.NoPublish, ShouldBeNil)
|
||||
So(args.Sources, ShouldBeNil)
|
||||
So(args.LabelWhiteList.String(), ShouldResemble, ".*rdt.*")
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When valid args are specified", func() {
|
||||
args, err := argsParse([]string{"--no-publish", "--sources=fake1,fake2,fake3", "--ca-file=ca", "--cert-file=crt", "--key-file=key"})
|
||||
|
||||
Convey("--no-publish is set and args.sources is set to appropriate values", func() {
|
||||
So(*args.NoPublish, ShouldBeTrue)
|
||||
So(args.CaFile, ShouldEqual, "ca")
|
||||
So(args.CertFile, ShouldEqual, "crt")
|
||||
So(args.KeyFile, ShouldEqual, "key")
|
||||
So(*args.Sources, ShouldResemble, []string{"fake1", "fake2", "fake3"})
|
||||
So(args.LabelWhiteList, ShouldBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
So(*args.Overrides.NoPublish, ShouldBeTrue)
|
||||
So(*args.Overrides.SleepInterval, ShouldEqual, 30*time.Second)
|
||||
So(*args.Overrides.Sources, ShouldResemble, utils.StringSliceVal{"fake1", "fake2", "fake3"})
|
||||
So(args.Overrides.LabelWhiteList.Regexp.String(), ShouldResemble, ".*rdt.*")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -211,52 +211,34 @@ pass the `--no-publish` flag to nfd-worker.
|
|||
Command line flags of nfd-worker:
|
||||
|
||||
```bash
|
||||
$ docker run --rm ${NFD_CONTAINER_IMAGE} nfd-worker --help
|
||||
...
|
||||
Usage:
|
||||
nfd-worker [--no-publish] [--sources=<sources>] [--label-whitelist=<pattern>]
|
||||
[--oneshot | --sleep-interval=<seconds>] [--config=<path>]
|
||||
[--options=<config>] [--server=<server>] [--server-name-override=<name>]
|
||||
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
|
||||
nfd-worker -h | --help
|
||||
nfd-worker --version
|
||||
|
||||
Options:
|
||||
-h --help Show this screen.
|
||||
--version Output version and exit.
|
||||
--config=<path> Config file to use.
|
||||
[Default: /etc/kubernetes/node-feature-discovery/nfd-worker.conf]
|
||||
--options=<config> 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
|
||||
will override settings read from the config file.
|
||||
[Default: ]
|
||||
--ca-file=<path> Root certificate for verifying connections
|
||||
[Default: ]
|
||||
--cert-file=<path> Certificate used for authenticating connections
|
||||
[Default: ]
|
||||
--key-file=<path> Private key matching --cert-file
|
||||
[Default: ]
|
||||
--server=<server> NFD server address to connecto to.
|
||||
[Default: localhost:8080]
|
||||
--server-name-override=<name> Name (CN) expect from server certificate, useful
|
||||
in testing
|
||||
[Default: ]
|
||||
--sources=<sources> Comma separated list of feature sources. Special
|
||||
value 'all' enables all feature sources.
|
||||
[Default: all]
|
||||
--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.
|
||||
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
|
||||
sleep). [Default: 60s]
|
||||
|
||||
$ docker run --rm ${NFD_CONTAINER_IMAGE} nfd-worker -help
|
||||
Usage of nfd-worker:
|
||||
-ca-file string
|
||||
Root certificate for verifying connections
|
||||
-cert-file string
|
||||
Certificate used for authenticating connections
|
||||
-config string
|
||||
Config file to use. (default "/etc/kubernetes/node-feature-discovery/nfd-worker.conf")
|
||||
-key-file string
|
||||
Private key matching -cert-file
|
||||
-label-whitelist value
|
||||
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 '/'. DEPRECATED: This parameter should be set via the config file.
|
||||
-no-publish
|
||||
Do not publish discovered features, disable connection to nfd-master.
|
||||
-oneshot
|
||||
Do not publish feature labels
|
||||
-options string
|
||||
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
|
||||
-server string
|
||||
NFD server address to connecto to. (default "localhost:8080")
|
||||
-server-name-override string
|
||||
Hostname expected from server certificate, useful in testing
|
||||
-sleep-interval duration
|
||||
Time to sleep between re-labeling. Non-positive value implies no re-labeling (i.e. infinite sleep). DEPRECATED: This parameter should be set via the config file
|
||||
-sources value
|
||||
Comma separated list of feature sources. Special value 'all' enables all feature sources. DEPRECATED: This parameter should be set via the config file
|
||||
-version
|
||||
Print version and exit.
|
||||
```
|
||||
|
||||
**NOTE** Some feature sources need certain directories and/or files from the
|
||||
|
|
1
go.mod
1
go.mod
|
@ -3,7 +3,6 @@ module sigs.k8s.io/node-feature-discovery
|
|||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/klauspost/cpuid/v2 v2.0.2
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Copyright 2019-2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -26,12 +26,12 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
//"github.com/onsi/gomega"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/vektra/errors"
|
||||
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/labeler"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/cpu"
|
||||
"sigs.k8s.io/node-feature-discovery/source/fake"
|
||||
|
@ -53,13 +53,13 @@ func TestDiscoveryWithMockSources(t *testing.T) {
|
|||
|
||||
fakeFeatureSource := source.FeatureSource(mockFeatureSource)
|
||||
|
||||
labelWhiteList := regex{*regexp.MustCompile("^test")}
|
||||
labelWhiteList := utils.RegexpVal{Regexp: *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, labelWhiteList)
|
||||
returnedLabels, err := getFeatureLabels(fakeFeatureSource, labelWhiteList.Regexp)
|
||||
Convey("Proper label is returned", func() {
|
||||
So(returnedLabels, ShouldResemble, fakeFeatureLabels)
|
||||
})
|
||||
|
@ -72,7 +72,7 @@ func TestDiscoveryWithMockSources(t *testing.T) {
|
|||
expectedError := errors.New("fake error")
|
||||
mockFeatureSource.On("Discover").Return(nil, expectedError)
|
||||
|
||||
returnedLabels, err := getFeatureLabels(fakeFeatureSource, labelWhiteList)
|
||||
returnedLabels, err := getFeatureLabels(fakeFeatureSource, labelWhiteList.Regexp)
|
||||
Convey("No label is returned", func() {
|
||||
So(returnedLabels, ShouldBeNil)
|
||||
})
|
||||
|
@ -109,7 +109,7 @@ func (w *nfdWorker) getSource(name string) source.FeatureSource {
|
|||
|
||||
func TestConfigParse(t *testing.T) {
|
||||
Convey("When parsing configuration", t, func() {
|
||||
w, err := NewNfdWorker(Args{})
|
||||
w, err := NewNfdWorker(&Args{})
|
||||
So(err, ShouldBeNil)
|
||||
worker := w.(*nfdWorker)
|
||||
overrides := `{"core": {"sources": ["fake"],"noPublish": true},"sources": {"cpu": {"cpuid": {"attributeBlacklist": ["foo","bar"]}}}}`
|
||||
|
@ -123,7 +123,7 @@ func TestConfigParse(t *testing.T) {
|
|||
})
|
||||
})
|
||||
Convey("and a non-accessible file, but core cmdline flags and some overrides are specified", func() {
|
||||
worker.args = Args{Sources: &[]string{"cpu", "kernel", "pci"}}
|
||||
worker.args = Args{Overrides: ConfigOverrideArgs{Sources: &utils.StringSliceVal{"cpu", "kernel", "pci"}}}
|
||||
So(worker.configure("non-existing-file", overrides), ShouldBeNil)
|
||||
|
||||
Convey("core cmdline flags should be in effect instead overrides", func() {
|
||||
|
@ -157,7 +157,7 @@ sources:
|
|||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("and a proper config file is specified", func() {
|
||||
worker.args = Args{Sources: &[]string{"cpu", "kernel", "pci"}}
|
||||
worker.args = Args{Overrides: ConfigOverrideArgs{Sources: &utils.StringSliceVal{"cpu", "kernel", "pci"}}}
|
||||
So(worker.configure(f.Name(), ""), ShouldBeNil)
|
||||
|
||||
Convey("specified configuration should take effect", func() {
|
||||
|
@ -178,7 +178,7 @@ sources:
|
|||
|
||||
Convey("and a proper config file and overrides are given", func() {
|
||||
sleepIntervalArg := 15 * time.Second
|
||||
worker.args = Args{SleepInterval: &sleepIntervalArg}
|
||||
worker.args = Args{Overrides: ConfigOverrideArgs{SleepInterval: &sleepIntervalArg}}
|
||||
overrides := `{"core": {"sources": ["fake"],"noPublish": true},"sources": {"pci": {"deviceClassWhitelist": ["03"]}}}`
|
||||
So(worker.configure(f.Name(), overrides), ShouldBeNil)
|
||||
|
||||
|
@ -229,11 +229,11 @@ core:
|
|||
`)
|
||||
|
||||
noPublish := true
|
||||
w, err := NewNfdWorker(Args{
|
||||
ConfigFile: configFile,
|
||||
Sources: &[]string{"fake"},
|
||||
NoPublish: &noPublish,
|
||||
SleepInterval: new(time.Duration),
|
||||
w, err := NewNfdWorker(&Args{
|
||||
ConfigFile: configFile,
|
||||
Overrides: ConfigOverrideArgs{
|
||||
Sources: &utils.StringSliceVal{"fake"},
|
||||
NoPublish: &noPublish},
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
worker := w.(*nfdWorker)
|
||||
|
@ -309,10 +309,10 @@ func withTimeout(actual interface{}, expected ...interface{}) string {
|
|||
func TestNewNfdWorker(t *testing.T) {
|
||||
Convey("When creating new NfdWorker instance", t, func() {
|
||||
|
||||
emptyRegexp := regex{*regexp.MustCompile("")}
|
||||
emptyRegexp := utils.RegexpVal{Regexp: *regexp.MustCompile("")}
|
||||
|
||||
Convey("without any args specified", func() {
|
||||
args := Args{}
|
||||
args := &Args{}
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
|
@ -326,7 +326,7 @@ func TestNewNfdWorker(t *testing.T) {
|
|||
})
|
||||
|
||||
Convey("with non-empty Sources arg specified", func() {
|
||||
args := Args{Sources: &[]string{"fake"}}
|
||||
args := &Args{Overrides: ConfigOverrideArgs{Sources: &utils.StringSliceVal{"fake"}}}
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
|
@ -341,14 +341,14 @@ func TestNewNfdWorker(t *testing.T) {
|
|||
})
|
||||
|
||||
Convey("with valid LabelWhiteListStr arg specified", func() {
|
||||
args := Args{LabelWhiteList: regexp.MustCompile(".*rdt.*")}
|
||||
args := &Args{Overrides: ConfigOverrideArgs{LabelWhiteList: &utils.RegexpVal{Regexp: *regexp.MustCompile(".*rdt.*")}}}
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
worker := w.(*nfdWorker)
|
||||
So(worker.configure("", ""), ShouldBeNil)
|
||||
expectRegexp := regex{*regexp.MustCompile(".*rdt.*")}
|
||||
expectRegexp := utils.RegexpVal{Regexp: *regexp.MustCompile(".*rdt.*")}
|
||||
Convey("proper labelWhiteList regexp should be produced", func() {
|
||||
So(worker.config.Core.LabelWhiteList, ShouldResemble, expectRegexp)
|
||||
})
|
||||
|
@ -359,12 +359,12 @@ func TestNewNfdWorker(t *testing.T) {
|
|||
func TestCreateFeatureLabels(t *testing.T) {
|
||||
Convey("When creating feature labels from the configured sources", t, func() {
|
||||
Convey("When fake feature source is configured", func() {
|
||||
emptyLabelWL := regex{*regexp.MustCompile("")}
|
||||
emptyLabelWL := regexp.MustCompile("")
|
||||
fakeFeatureSource := source.FeatureSource(new(fake.Source))
|
||||
fakeFeatureSource.SetConfig(fakeFeatureSource.NewConfig())
|
||||
sources := []source.FeatureSource{}
|
||||
sources = append(sources, fakeFeatureSource)
|
||||
labels := createFeatureLabels(sources, emptyLabelWL)
|
||||
labels := createFeatureLabels(sources, *emptyLabelWL)
|
||||
|
||||
Convey("Proper fake labels are returned", func() {
|
||||
So(len(labels), ShouldEqual, 3)
|
||||
|
@ -374,11 +374,10 @@ func TestCreateFeatureLabels(t *testing.T) {
|
|||
})
|
||||
})
|
||||
Convey("When fake feature source is configured with a whitelist that doesn't match", func() {
|
||||
labelWL := regex{*regexp.MustCompile(".*rdt.*")}
|
||||
fakeFeatureSource := source.FeatureSource(new(fake.Source))
|
||||
sources := []source.FeatureSource{}
|
||||
sources = append(sources, fakeFeatureSource)
|
||||
labels := createFeatureLabels(sources, labelWL)
|
||||
labels := createFeatureLabels(sources, *regexp.MustCompile(".*rdt.*"))
|
||||
|
||||
Convey("fake labels are not returned", func() {
|
||||
So(len(labels), ShouldEqual, 0)
|
||||
|
@ -394,7 +393,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, regex{*regexp.MustCompile("")})
|
||||
returnedLabels, err := getFeatureLabels(fakePanicFeatureSource, *regexp.MustCompile(""))
|
||||
Convey("No label is returned", func() {
|
||||
So(len(returnedLabels), ShouldEqual, 0)
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Copyright 2019-2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -34,7 +34,10 @@ import (
|
|||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
pb "sigs.k8s.io/node-feature-discovery/pkg/labeler"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/version"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/cpu"
|
||||
|
@ -50,7 +53,6 @@ import (
|
|||
"sigs.k8s.io/node-feature-discovery/source/storage"
|
||||
"sigs.k8s.io/node-feature-discovery/source/system"
|
||||
"sigs.k8s.io/node-feature-discovery/source/usb"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -66,7 +68,7 @@ type NFDConfig struct {
|
|||
}
|
||||
|
||||
type coreConfig struct {
|
||||
LabelWhiteList regex
|
||||
LabelWhiteList utils.RegexpVal
|
||||
NoPublish bool
|
||||
Sources []string
|
||||
SleepInterval duration
|
||||
|
@ -87,11 +89,18 @@ type Args struct {
|
|||
Oneshot bool
|
||||
Server string
|
||||
ServerNameOverride string
|
||||
// Deprecated options that should be set via the config file
|
||||
LabelWhiteList *regexp.Regexp
|
||||
NoPublish *bool
|
||||
|
||||
Overrides ConfigOverrideArgs
|
||||
}
|
||||
|
||||
// ConfigOverrideArgs are args that override config file options
|
||||
type ConfigOverrideArgs struct {
|
||||
NoPublish *bool
|
||||
|
||||
// Deprecated
|
||||
LabelWhiteList *utils.RegexpVal
|
||||
SleepInterval *time.Duration
|
||||
Sources *[]string
|
||||
Sources *utils.StringSliceVal
|
||||
}
|
||||
|
||||
type NfdWorker interface {
|
||||
|
@ -111,18 +120,14 @@ type nfdWorker struct {
|
|||
enabledSources []source.FeatureSource
|
||||
}
|
||||
|
||||
type regex struct {
|
||||
regexp.Regexp
|
||||
}
|
||||
|
||||
type duration struct {
|
||||
time.Duration
|
||||
}
|
||||
|
||||
// Create new NfdWorker instance.
|
||||
func NewNfdWorker(args Args) (NfdWorker, error) {
|
||||
func NewNfdWorker(args *Args) (NfdWorker, error) {
|
||||
nfd := &nfdWorker{
|
||||
args: args,
|
||||
args: *args,
|
||||
config: &NFDConfig{},
|
||||
realSources: []source.FeatureSource{
|
||||
&cpu.Source{},
|
||||
|
@ -203,7 +208,7 @@ func addConfigWatch(path string) (*fsnotify.Watcher, map[string]struct{}, error)
|
|||
func newDefaultConfig() *NFDConfig {
|
||||
return &NFDConfig{
|
||||
Core: coreConfig{
|
||||
LabelWhiteList: regex{*regexp.MustCompile("")},
|
||||
LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")},
|
||||
SleepInterval: duration{60 * time.Second},
|
||||
Sources: []string{"all"},
|
||||
},
|
||||
|
@ -238,7 +243,7 @@ func (w *nfdWorker) Run() error {
|
|||
select {
|
||||
case <-labelTrigger:
|
||||
// Get the set of feature labels.
|
||||
labels := createFeatureLabels(w.enabledSources, w.config.Core.LabelWhiteList)
|
||||
labels := createFeatureLabels(w.enabledSources, w.config.Core.LabelWhiteList.Regexp)
|
||||
|
||||
// Update the node with the feature labels.
|
||||
if w.client != nil {
|
||||
|
@ -448,17 +453,17 @@ func (w *nfdWorker) configure(filepath string, overrides string) error {
|
|||
return fmt.Errorf("Failed to parse --options: %s", err)
|
||||
}
|
||||
|
||||
if w.args.LabelWhiteList != nil {
|
||||
c.Core.LabelWhiteList = regex{*w.args.LabelWhiteList}
|
||||
if w.args.Overrides.LabelWhiteList != nil {
|
||||
c.Core.LabelWhiteList = *w.args.Overrides.LabelWhiteList
|
||||
}
|
||||
if w.args.NoPublish != nil {
|
||||
c.Core.NoPublish = *w.args.NoPublish
|
||||
if w.args.Overrides.NoPublish != nil {
|
||||
c.Core.NoPublish = *w.args.Overrides.NoPublish
|
||||
}
|
||||
if w.args.SleepInterval != nil {
|
||||
c.Core.SleepInterval = duration{*w.args.SleepInterval}
|
||||
if w.args.Overrides.SleepInterval != nil {
|
||||
c.Core.SleepInterval = duration{*w.args.Overrides.SleepInterval}
|
||||
}
|
||||
if w.args.Sources != nil {
|
||||
c.Core.Sources = *w.args.Sources
|
||||
if w.args.Overrides.Sources != nil {
|
||||
c.Core.Sources = *w.args.Overrides.Sources
|
||||
}
|
||||
|
||||
c.Core.sanitize()
|
||||
|
@ -477,7 +482,7 @@ func (w *nfdWorker) configure(filepath string, overrides string) error {
|
|||
|
||||
// createFeatureLabels returns the set of feature labels from the enabled
|
||||
// sources and the whitelist argument.
|
||||
func createFeatureLabels(sources []source.FeatureSource, labelWhiteList regex) (labels Labels) {
|
||||
func createFeatureLabels(sources []source.FeatureSource, labelWhiteList regexp.Regexp) (labels Labels) {
|
||||
labels = Labels{}
|
||||
|
||||
// Do feature discovery from all configured sources.
|
||||
|
@ -500,7 +505,7 @@ func createFeatureLabels(sources []source.FeatureSource, labelWhiteList regex) (
|
|||
|
||||
// getFeatureLabels returns node labels for features discovered by the
|
||||
// supplied source.
|
||||
func getFeatureLabels(source source.FeatureSource, labelWhiteList regex) (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)
|
||||
|
@ -584,25 +589,6 @@ func advertiseFeatureLabels(client pb.LabelerClient, labels Labels) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaler interface from "encoding/json"
|
||||
func (r *regex) UnmarshalJSON(data []byte) error {
|
||||
var v interface{}
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
if rr, err := regexp.Compile(string(val)); err != nil {
|
||||
return err
|
||||
} else {
|
||||
*r = regex{*rr}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid regexp %s", data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaler interface from "encoding/json"
|
||||
func (d *duration) UnmarshalJSON(data []byte) error {
|
||||
var v interface{}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Copyright 2019-2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -24,22 +24,24 @@ import (
|
|||
"time"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
nfdmaster "sigs.k8s.io/node-feature-discovery/pkg/nfd-master"
|
||||
w "sigs.k8s.io/node-feature-discovery/pkg/nfd-worker"
|
||||
|
||||
master "sigs.k8s.io/node-feature-discovery/pkg/nfd-master"
|
||||
worker "sigs.k8s.io/node-feature-discovery/pkg/nfd-worker"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/utils"
|
||||
"sigs.k8s.io/node-feature-discovery/test/data"
|
||||
)
|
||||
|
||||
type testContext struct {
|
||||
master nfdmaster.NfdMaster
|
||||
master master.NfdMaster
|
||||
errs chan error
|
||||
}
|
||||
|
||||
func setupTest(args *nfdmaster.Args) testContext {
|
||||
func setupTest(args *master.Args) testContext {
|
||||
// Fixed port and no-publish, for convenience
|
||||
args.NoPublish = true
|
||||
args.Port = 8192
|
||||
args.LabelWhiteList.Regexp = *regexp.MustCompile("")
|
||||
m, err := nfdmaster.NewNfdMaster(args)
|
||||
m, err := master.NewNfdMaster(args)
|
||||
if err != nil {
|
||||
fmt.Printf("Test setup failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
|
@ -73,9 +75,9 @@ func teardownTest(ctx testContext) {
|
|||
func TestNewNfdWorker(t *testing.T) {
|
||||
Convey("When initializing new NfdWorker instance", t, func() {
|
||||
Convey("When one of --cert-file, --key-file or --ca-file is missing", func() {
|
||||
_, err := w.NewNfdWorker(w.Args{CertFile: "crt", KeyFile: "key"})
|
||||
_, err2 := w.NewNfdWorker(w.Args{KeyFile: "key", CaFile: "ca"})
|
||||
_, err3 := w.NewNfdWorker(w.Args{CertFile: "crt", CaFile: "ca"})
|
||||
_, err := worker.NewNfdWorker(&worker.Args{CertFile: "crt", KeyFile: "key"})
|
||||
_, err2 := worker.NewNfdWorker(&worker.Args{KeyFile: "key", CaFile: "ca"})
|
||||
_, err3 := worker.NewNfdWorker(&worker.Args{CertFile: "crt", CaFile: "ca"})
|
||||
Convey("An error should be returned", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err2, ShouldNotBeNil)
|
||||
|
@ -86,12 +88,17 @@ func TestNewNfdWorker(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
ctx := setupTest(&nfdmaster.Args{})
|
||||
ctx := setupTest(&master.Args{})
|
||||
defer teardownTest(ctx)
|
||||
Convey("When running nfd-worker against nfd-master", t, func() {
|
||||
Convey("When publishing features from fake source", func() {
|
||||
worker, _ := w.NewNfdWorker(w.Args{Oneshot: true, Sources: &[]string{"fake"}, Server: "localhost:8192"})
|
||||
err := worker.Run()
|
||||
args := &worker.Args{
|
||||
Oneshot: true,
|
||||
Server: "localhost:8192",
|
||||
Overrides: worker.ConfigOverrideArgs{Sources: &utils.StringSliceVal{"fake"}},
|
||||
}
|
||||
fooasdf, _ := worker.NewNfdWorker(args)
|
||||
err := fooasdf.Run()
|
||||
Convey("No error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
@ -100,7 +107,7 @@ func TestRun(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRunTls(t *testing.T) {
|
||||
masterArgs := &nfdmaster.Args{
|
||||
masterArgs := &master.Args{
|
||||
CaFile: data.FilePath("ca.crt"),
|
||||
CertFile: data.FilePath("nfd-test-master.crt"),
|
||||
KeyFile: data.FilePath("nfd-test-master.key"),
|
||||
|
@ -110,16 +117,17 @@ func TestRunTls(t *testing.T) {
|
|||
defer teardownTest(ctx)
|
||||
Convey("When running nfd-worker against nfd-master with mutual TLS auth enabled", t, func() {
|
||||
Convey("When publishing features from fake source", func() {
|
||||
workerArgs := w.Args{
|
||||
workerArgs := worker.Args{
|
||||
CaFile: data.FilePath("ca.crt"),
|
||||
CertFile: data.FilePath("nfd-test-worker.crt"),
|
||||
KeyFile: data.FilePath("nfd-test-worker.key"),
|
||||
Oneshot: true,
|
||||
Sources: &[]string{"fake"},
|
||||
Server: "localhost:8192",
|
||||
ServerNameOverride: "nfd-test-master"}
|
||||
worker, _ := w.NewNfdWorker(workerArgs)
|
||||
err := worker.Run()
|
||||
ServerNameOverride: "nfd-test-master",
|
||||
Overrides: worker.ConfigOverrideArgs{Sources: &utils.StringSliceVal{"fake"}},
|
||||
}
|
||||
w, _ := worker.NewNfdWorker(&workerArgs)
|
||||
err := w.Run()
|
||||
Convey("No error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -34,6 +36,25 @@ func (a *RegexpVal) Set(val string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaler interface from "encoding/json"
|
||||
func (a *RegexpVal) UnmarshalJSON(data []byte) error {
|
||||
var v interface{}
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
if r, err := regexp.Compile(string(val)); err != nil {
|
||||
return err
|
||||
} else {
|
||||
*a = RegexpVal{*r}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid regexp %s", data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringSetVal is a Value encapsulating a set of comma-separated strings
|
||||
type StringSetVal map[string]struct{}
|
||||
|
||||
|
@ -60,3 +81,20 @@ func (a *StringSetVal) String() string {
|
|||
sort.Strings(vals)
|
||||
return strings.Join(vals, ",")
|
||||
}
|
||||
|
||||
// StringSliceVal is a Value encapsulating a slice of comma-separated strings
|
||||
type StringSliceVal []string
|
||||
|
||||
// Set implements the regexp.Value interface
|
||||
func (a *StringSliceVal) Set(val string) error {
|
||||
*a = strings.Split(val, ",")
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the regexp.Value interface
|
||||
func (a *StringSliceVal) String() string {
|
||||
if *a == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(*a, ",")
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue