1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-28 02:37:11 +00:00

Merge pull request #444 from marquiz/devel/flags

Switch to flags package in command line arg parsing
This commit is contained in:
Kubernetes Prow Robot 2021-02-24 06:42:50 -08:00 committed by GitHub
commit 5697bffd2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 419 additions and 485 deletions

View file

@ -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,14 @@ limitations under the License.
package main
import (
"flag"
"fmt"
"log"
"os"
"regexp"
"strconv"
"strings"
"github.com/docopt/docopt-go"
master "sigs.k8s.io/node-feature-discovery/pkg/nfd-master"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
"sigs.k8s.io/node-feature-discovery/pkg/version"
)
@ -34,17 +34,29 @@ const (
)
func main() {
flags := flag.NewFlagSet(ProgramName, flag.ExitOnError)
printVersion := flags.Bool("version", false, "Print version and exit.")
args := initFlags(flags)
_ = flags.Parse(os.Args[1:])
if len(flags.Args()) > 0 {
fmt.Printf("unknown command line argument: %s\n", flags.Args()[0])
flags.Usage()
os.Exit(2)
}
if *printVersion {
fmt.Println(ProgramName, version.Get())
os.Exit(0)
}
// Assert that the version is known
if version.Undefined() {
log.Print("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 NfdMaster instance
instance, err := master.NewNfdMaster(args)
if err != nil {
@ -56,88 +68,37 @@ 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) (master.Args, error) {
args := master.Args{}
usage := fmt.Sprintf(`%s.
Usage:
%s [--prune] [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
[--kubeconfig=<path>] [--instance=<name>]
%s -h | --help
%s --version
Options:
-h --help Show this screen.
--version Output version and exit.
--prune Prune all NFD related attributes from all nodes
of the cluster and exit.
--instance=<name> Instance name. Used to separate annotation
namespaces for multiple parallel deployments.
[Default: ]
--kubeconfig=<path> Kubeconfig to use [Default: ]
of the cluster and exit.
--port=<port> Port on which to listen for connections.
[Default: 8080]
--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: ]
--verify-node-name Verify worker node name against CN from the TLS
certificate. Only has effect when TLS authentication
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.
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.
[Default: ]`,
ProgramName,
ProgramName,
ProgramName,
ProgramName,
)
arguments, _ := docopt.ParseArgs(usage, argv,
fmt.Sprintf("%s %s", ProgramName, version.Get()))
// Parse argument values as usable types.
var err error
args.Instance = arguments["--instance"].(string)
if ok, _ := regexp.MatchString(`^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$`, args.Instance); args.Instance != "" && !ok {
return args, fmt.Errorf("invalid --instance %q: instance name "+
"must start and end with an alphanumeric character and may only contain "+
"alphanumerics, `-`, `_` or `.`", args.Instance)
func initFlags(flagset *flag.FlagSet) *master.Args {
args := &master.Args{
LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")},
}
args.CaFile = arguments["--ca-file"].(string)
args.CertFile = arguments["--cert-file"].(string)
args.KeyFile = arguments["--key-file"].(string)
args.NoPublish = arguments["--no-publish"].(bool)
args.Port, err = strconv.Atoi(arguments["--port"].(string))
if err != nil {
return args, fmt.Errorf("invalid --port defined: %s", err)
}
args.LabelWhiteList, err = regexp.Compile(arguments["--label-whitelist"].(string))
if err != nil {
return args, fmt.Errorf("error parsing whitelist regex (%s): %s", arguments["--label-whitelist"], err)
}
args.VerifyNodeName = arguments["--verify-node-name"].(bool)
args.ExtraLabelNs = map[string]struct{}{}
for _, n := range strings.Split(arguments["--extra-label-ns"].(string), ",") {
args.ExtraLabelNs[n] = struct{}{}
}
args.ResourceLabels = strings.Split(arguments["--resource-labels"].(string), ",")
args.Prune = arguments["--prune"].(bool)
args.Kubeconfig = arguments["--kubeconfig"].(string)
return args, nil
flagset.StringVar(&args.CaFile, "ca-file", "",
"Root certificate for verifying connections")
flagset.StringVar(&args.CertFile, "cert-file", "",
"Certificate used for authenticating connections")
flagset.Var(&args.ExtraLabelNs, "extra-label-ns",
"Comma separated list of allowed extra label namespaces")
flagset.StringVar(&args.Instance, "instance", "",
"Instance name. Used to separate annotation namespaces for multiple parallel deployments.")
flagset.StringVar(&args.KeyFile, "key-file", "",
"Private key matching -cert-file")
flagset.StringVar(&args.Kubeconfig, "kubeconfig", "",
"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.NoPublish, "no-publish", false,
"Do not publish feature labels")
flagset.IntVar(&args.Port, "port", 8080,
"Port on which to listen for connections.")
flagset.BoolVar(&args.Prune, "prune", false,
"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,
"Verify worker node name against CN from the TLS certificate. "+
"Only takes effect when TLS authentication has been enabled.")
return args
}

View file

@ -1,55 +0,0 @@
/*
Copyright 2019 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.
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.
*/
package main
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestArgsParse(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"})
Convey("noPublish is set and args.sources is set to the default value", func() {
So(args.NoPublish, ShouldBeTrue)
So(len(args.LabelWhiteList.String()), ShouldEqual, 0)
So(err, ShouldBeNil)
})
})
Convey("When valid args are specified", func() {
args, err := argsParse([]string{"--label-whitelist=.*rdt.*", "--port=1234", "--cert-file=crt", "--key-file=key", "--ca-file=ca"})
Convey("Argument parsing should succeed and args set to correct values", func() {
So(args.NoPublish, ShouldBeFalse)
So(args.Port, ShouldEqual, 1234)
So(args.CertFile, ShouldEqual, "crt")
So(args.KeyFile, ShouldEqual, "key")
So(args.CaFile, ShouldEqual, "ca")
So(args.LabelWhiteList.String(), ShouldResemble, ".*rdt.*")
So(err, ShouldBeNil)
})
})
Convey("When invalid --port is defined", func() {
_, err := argsParse([]string{"--port=123a"})
Convey("argsParse should fail", func() {
So(err, ShouldNotBeNil)
})
})
})
}

View file

@ -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
}

View file

@ -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.*")
})
})
})

View file

@ -164,43 +164,34 @@ $ docker run --rm --name=nfd-test ${NFD_CONTAINER_IMAGE} nfd-master --no-publish
Command line flags of nfd-master:
```bash
$ docker run --rm ${NFD_CONTAINER_IMAGE} nfd-master --help
...
Usage:
nfd-master [--prune] [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
[--kubeconfig=<path>]
nfd-master -h | --help
nfd-master --version
Options:
-h --help Show this screen.
--version Output version and exit.
--prune Prune all NFD related attributes from all nodes
of the cluster and exit.
--kubeconfig=<path> Kubeconfig to use [Default: ]
--port=<port> Port on which to listen for connections.
[Default: 8080]
--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: ]
--verify-node-name Verify worker node name against CN from the TLS
certificate. Only has effect when TLS authentication
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.
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.
[Default: ]
$ docker run --rm ${NFD_CONTAINER_IMAGE} nfd-master -help
Usage of nfd-master:
-ca-file string
Root certificate for verifying connections
-cert-file string
Certificate used for authenticating connections
-extra-label-ns value
Comma separated list of allowed extra label namespaces
-instance string
Instance name. Used to separate annotation namespaces for multiple parallel deployments.
-key-file string
Private key matching -cert-file
-kubeconfig string
Kubeconfig to use
-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 '/'.
-no-publish
Do not publish feature labels
-port int
Port on which to listen for connections. (default 8080)
-prune
Prune all NFD related attributes from all nodes of the cluaster and exit.
-resource-labels value
Comma separated list of labels to be exposed as extended resources.
-verify-node-name
Verify worker node name against CN from the TLS certificate. Only takes effect when TLS authentication has been enabled.
-version
Print version and exit.
```
### NFD-Worker
@ -220,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
View file

@ -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

View file

@ -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,6 +34,7 @@ import (
k8sclient "k8s.io/client-go/kubernetes"
"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
"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/yaml"
)
@ -55,7 +56,7 @@ func newMockMaster(apihelper apihelper.APIHelpers) *nfdMaster {
return &nfdMaster{
nodeName: mockNodeName,
annotationNs: AnnotationNsBase,
args: Args{LabelWhiteList: regexp.MustCompile("")},
args: Args{LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")}},
apihelper: apihelper,
}
}
@ -339,7 +340,7 @@ func TestSetLabels(t *testing.T) {
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-2", mockLabels["feature-2"]),
}
mockMaster.args.LabelWhiteList = regexp.MustCompile("^f.*2$")
mockMaster.args.LabelWhiteList.Regexp = *regexp.MustCompile("^f.*2$")
mockHelper.On("GetClient").Return(mockClient, nil)
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
mockHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedPatches))).Return(nil)
@ -390,7 +391,7 @@ func TestSetLabels(t *testing.T) {
apihelper.NewJsonPatch("add", "/status/capacity", LabelNs+"/feature-3", mockLabels["feature-3"]),
}
mockMaster.args.ResourceLabels = []string{"feature-3", "feature-1"}
mockMaster.args.ResourceLabels = map[string]struct{}{"feature-3": struct{}{}, "feature-1": struct{}{}}
mockHelper.On("GetClient").Return(mockClient, nil)
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
mockHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedPatches))).Return(nil)

View file

@ -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.
@ -38,6 +38,7 @@ import (
api "k8s.io/api/core/v1"
"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
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"
)
@ -74,16 +75,16 @@ type Annotations map[string]string
type Args struct {
CaFile string
CertFile string
ExtraLabelNs map[string]struct{}
ExtraLabelNs utils.StringSetVal
Instance string
KeyFile string
Kubeconfig string
LabelWhiteList *regexp.Regexp
LabelWhiteList utils.RegexpVal
NoPublish bool
Port int
Prune bool
VerifyNodeName bool
ResourceLabels []string
ResourceLabels utils.StringSetVal
}
type NfdMaster interface {
@ -102,8 +103,8 @@ type nfdMaster struct {
}
// Create new NfdMaster server instance.
func NewNfdMaster(args Args) (NfdMaster, error) {
nfd := &nfdMaster{args: args,
func NewNfdMaster(args *Args) (NfdMaster, error) {
nfd := &nfdMaster{args: *args,
nodeName: os.Getenv("NODE_NAME"),
ready: make(chan bool, 1),
}
@ -111,6 +112,11 @@ func NewNfdMaster(args Args) (NfdMaster, error) {
if args.Instance == "" {
nfd.annotationNs = AnnotationNsBase
} else {
if ok, _ := regexp.MatchString(`^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$`, args.Instance); !ok {
return nfd, fmt.Errorf("invalid --instance %q: instance name "+
"must start and end with an alphanumeric character and may only contain "+
"alphanumerics, `-`, `_` or `.`", args.Instance)
}
nfd.annotationNs = args.Instance + "." + AnnotationNsBase
}
@ -283,7 +289,7 @@ func (m *nfdMaster) updateMasterNode() error {
// into extended resources. This function also handles proper namespacing of
// labels and ERs, i.e. adds the possibly missing default namespace for labels
// arriving through the gRPC API.
func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelWhiteList *regexp.Regexp, extendedResourceNames []string) (Labels, ExtendedResources) {
func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelWhiteList regexp.Regexp, extendedResourceNames map[string]struct{}) (Labels, ExtendedResources) {
outLabels := Labels{}
for label, value := range labels {
@ -310,7 +316,7 @@ func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelW
// Remove labels which are intended to be extended resources
extendedResources := ExtendedResources{}
for _, extendedResourceName := range extendedResourceNames {
for extendedResourceName := range extendedResourceNames {
// Add possibly missing default ns
extendedResourceName = addNs(extendedResourceName, LabelNs)
if value, ok := outLabels[extendedResourceName]; ok {
@ -354,7 +360,7 @@ func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.Se
}
stdoutLogger.Printf("REQUEST Node: %s NFD-version: %s Labels: %s", r.NodeName, r.NfdVersion, r.Labels)
labels, extendedResources := filterFeatureLabels(r.Labels, m.args.ExtraLabelNs, m.args.LabelWhiteList, m.args.ResourceLabels)
labels, extendedResources := filterFeatureLabels(r.Labels, m.args.ExtraLabelNs, m.args.LabelWhiteList.Regexp, m.args.ResourceLabels)
if !m.args.NoPublish {
// Advertise NFD worker version as an annotation

View file

@ -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,9 +26,9 @@ import (
func TestNewNfdMaster(t *testing.T) {
Convey("When initializing new NfdMaster instance", t, func() {
Convey("When one of --cert-file, --key-file or --ca-file is missing", func() {
_, err := m.NewNfdMaster(m.Args{CertFile: "crt", KeyFile: "key"})
_, err2 := m.NewNfdMaster(m.Args{KeyFile: "key", CaFile: "ca"})
_, err3 := m.NewNfdMaster(m.Args{CertFile: "crt", CaFile: "ca"})
_, err := m.NewNfdMaster(&m.Args{CertFile: "crt", KeyFile: "key"})
_, err2 := m.NewNfdMaster(&m.Args{KeyFile: "key", CaFile: "ca"})
_, err3 := m.NewNfdMaster(&m.Args{CertFile: "crt", CaFile: "ca"})
Convey("An error should be returned", func() {
So(err, ShouldNotBeNil)
So(err2, ShouldNotBeNil)

View file

@ -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)
})

View file

@ -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{}

View file

@ -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.MustCompile("")
m, err := nfdmaster.NewNfdMaster(args)
args.LabelWhiteList.Regexp = *regexp.MustCompile("")
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)
})

100
pkg/utils/flags.go Normal file
View file

@ -0,0 +1,100 @@
/*
Copyright 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.
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.
*/
package utils
import (
"encoding/json"
"fmt"
"regexp"
"sort"
"strings"
)
// RegexpVal is a wrapper for regexp command line flags
type RegexpVal struct {
regexp.Regexp
}
// Set implements the flag.Value interface
func (a *RegexpVal) Set(val string) error {
r, err := regexp.Compile(val)
a.Regexp = *r
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{}
// Set implements the flag.Value interface
func (a *StringSetVal) Set(val string) error {
m := map[string]struct{}{}
for _, n := range strings.Split(val, ",") {
m[n] = struct{}{}
}
*a = m
return nil
}
// String implements the flag.Value interface
func (a *StringSetVal) String() string {
if *a == nil {
return ""
}
vals := make([]string, len(*a), 0)
for val := range *a {
vals = append(vals, val)
}
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, ",")
}