diff --git a/Dockerfile b/Dockerfile index 43cc499a9a..4c2702a654 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ RUN glide install --strip-vendor RUN go install \ -ldflags "-s -w -X main.version=$NFD_VERSION" \ github.com/kubernetes-incubator/node-feature-discovery +RUN install -D -m644 node-feature-discovery.conf.example /etc/kubernetes/node-feature-discovery/node-feature-discovery.conf RUN go test . @@ -35,6 +36,7 @@ FROM debian:stretch-slim COPY --from=builder /usr/local/bin /usr/local/bin COPY --from=builder /usr/local/lib /usr/local/lib +COPY --from=builder /etc/kubernetes/node-feature-discovery /etc/kubernetes/node-feature-discovery RUN ldconfig COPY --from=builder /go/bin/node-feature-discovery /usr/bin/node-feature-discovery diff --git a/README.md b/README.md index 7f3b8f4e8d..04f8b5bc35 100644 --- a/README.md +++ b/README.md @@ -38,13 +38,21 @@ node-feature-discovery. Usage: node-feature-discovery [--no-publish] [--sources=] [--label-whitelist=] - [--oneshot | --sleep-interval=] + [--oneshot | --sleep-interval=] [--config=] + [--options=] node-feature-discovery -h | --help node-feature-discovery --version Options: -h --help Show this screen. --version Output version and exit. + --config= Config file to use. + [Default: /etc/kubernetes/node-feature-discovery/node-feature-discovery.conf] + --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 + will override settings read from the config file. + [Default: ] --sources= Comma separated list of feature sources. [Default: cpuid,iommu,memory,network,pstate,rdt,selinux,storage] --no-publish Do not publish discovered features to the @@ -231,6 +239,50 @@ For example, if some node is tainted NoSchedule or fails to start a job for some [![asciicast](https://asciinema.org/a/11wir751y89617oemwnsgli4a.png)](https://asciinema.org/a/11wir751y89617oemwnsgli4a) +### Configuration options + +NFD supports a configuration file. The default location is +`/etc/kubernetes/node-feature-discovery/node-feature-discovery.conf`, but, +this can be changed by specifying the`--config` command line flag. The file is +read inside the Docker image, and thus, Volumes and VolumeMounts are needed to +make your configuration available for NFD. The preferred method is to use a +ConfigMap. +For example, create a config map using the example config as a template: +``` +cp node-feature-discovery.conf.example node-feature-discovery.conf +vim node-feature-discovery.conf # edit the configuration +kubectl create configmap node-feature-discovery-config --from-file=node-feature-discovery.conf +``` +Then, configure Volumes and VolumeMounts in the Pod spec (just the relevant +snippets shown below): +``` +... + containers: + volumeMounts: + - name: node-feature-discovery-config + mountPath: "/etc/kubernetes/node-feature-discovery/" +... + volumes: + - name: node-feature-discovery-config + configMap: + name: node-feature-discovery-config +... +``` +You could also use other types of volumes, of course. That is, hostPath if +different config for different nodes would be required, for example. + +The (empty-by-default) +[example config](https://github.com/kubernetes-incubator/node-feature-discovery/blob/master/node-feature-discovery.conf.example) +is used as a config in the NFD Docker image. Thus, this can be used as a default +configuration in custom-built images. + +Configuration options can also be specified via the `--options` command line +flag, in which case no mounts need to be used. The same format as in the config +file must be used, i.e. JSON (or YAML). + +Configuration options specified from the command line will override those read +from the config file. + ## Building from source Download the source code. diff --git a/main.go b/main.go index 39bdb129b7..c893f7f834 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io/ioutil" "log" "os" "regexp" @@ -9,6 +10,7 @@ import ( "time" "github.com/docopt/docopt-go" + "github.com/ghodss/yaml" "github.com/kubernetes-incubator/node-feature-discovery/source" "github.com/kubernetes-incubator/node-feature-discovery/source/cpuid" "github.com/kubernetes-incubator/node-feature-discovery/source/fake" @@ -48,6 +50,14 @@ var ( stderrLogger = log.New(os.Stderr, "", log.LstdFlags) ) +// Global config +type NFDConfig struct { + Sources struct { + } `json:"sources,omitempty"` +} + +var config = NFDConfig{} + // Labels are a Kubernetes representation of discovered features. type Labels map[string]string @@ -76,7 +86,9 @@ type APIHelpers interface { // Command line arguments type Args struct { labelWhiteList string + configFile string noPublish bool + options string oneshot bool sleepInterval time.Duration sources []string @@ -91,6 +103,12 @@ func main() { // Parse command-line arguments. args := argsParse(nil) + // Parse config + err := configParse(args.configFile, args.options) + if err != nil { + stderrLogger.Print(err) + } + // Configure the parameters for feature discovery. enabledSources, labelWhiteList, err := configureParameters(args.sources, args.labelWhiteList) if err != nil { @@ -129,13 +147,21 @@ func argsParse(argv []string) (args Args) { Usage: %s [--no-publish] [--sources=] [--label-whitelist=] - [--oneshot | --sleep-interval=] + [--oneshot | --sleep-interval=] [--config=] + [--options=] %s -h | --help %s --version Options: -h --help Show this screen. --version Output version and exit. + --config= Config file to use. + [Default: /etc/kubernetes/node-feature-discovery/node-feature-discovery.conf] + --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 + will override settings read from the config file. + [Default: ] --sources= Comma separated list of feature sources. [Default: cpuid,iommu,memory,network,pstate,rdt,selinux,storage] --no-publish Do not publish discovered features to the @@ -157,7 +183,9 @@ func argsParse(argv []string) (args Args) { // Parse argument values as usable types. var err error + args.configFile = arguments["--config"].(string) args.noPublish = arguments["--no-publish"].(bool) + args.options = arguments["--options"].(string) args.sources = strings.Split(arguments["--sources"].(string), ",") args.labelWhiteList = arguments["--label-whitelist"].(string) args.oneshot = arguments["--oneshot"].(bool) @@ -175,6 +203,28 @@ func argsParse(argv []string) (args Args) { return args } +// Parse configuration options +func configParse(filepath string, overrides string) error { + data, err := ioutil.ReadFile(filepath) + if err != nil { + return fmt.Errorf("Failed to read config file: %s", err) + } + + // Read config file + err = yaml.Unmarshal(data, &config) + if err != nil { + return fmt.Errorf("Failed to parse config file: %s", err) + } + + // Parse config overrides + err = yaml.Unmarshal([]byte(overrides), &config) + if err != nil { + return fmt.Errorf("Failed to parse --options: %s", err) + } + + return nil +} + // configureParameters returns all the variables required to perform feature // discovery based on command line arguments. func configureParameters(sourcesWhiteList []string, labelWhiteListStr string) (enabledSources []source.FeatureSource, labelWhiteList *regexp.Regexp, err error) { diff --git a/main_test.go b/main_test.go index 137ef60fa4..2fb3f83a10 100644 --- a/main_test.go +++ b/main_test.go @@ -2,6 +2,8 @@ package main import ( "fmt" + "io/ioutil" + "os" "regexp" "testing" "time" @@ -174,6 +176,32 @@ func TestArgsParse(t *testing.T) { }) } +func TestConfigParse(t *testing.T) { + Convey("When parsing configuration file", t, func() { + Convey("When non-accessible file is given", func() { + err := configParse("non-existing-file", "") + + Convey("Should return error", func() { + So(err, ShouldNotBeNil) + }) + }) + // Create a temporary config file + f, err := ioutil.TempFile("", "nfd-test-") + defer os.Remove(f.Name()) + So(err, ShouldBeNil) + f.WriteString(`sources:`) + f.Close() + + Convey("When proper config file is given", func() { + err := configParse(f.Name(), "") + + Convey("Should return error", func() { + So(err, ShouldBeNil) + }) + }) + }) +} + func TestConfigureParameters(t *testing.T) { Convey("When configuring parameters for node feature discovery", t, func() { diff --git a/node-feature-discovery.conf.example b/node-feature-discovery.conf.example new file mode 100644 index 0000000000..a4ede8e4d3 --- /dev/null +++ b/node-feature-discovery.conf.example @@ -0,0 +1 @@ +#sources: