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:
commit
5697bffd2c
13 changed files with 419 additions and 485 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,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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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.*")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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
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.
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.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
100
pkg/utils/flags.go
Normal 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, ",")
|
||||
}
|
Loading…
Add table
Reference in a new issue