mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
nfd-worker: rework configuration handling
Extend the FeatureSource interface with new methods for configuration handling. This enables easier on-the fly reconfiguration of the feature sources. Further, it simplifies adding config support to feature sources in the future. Stub methods are added to sources that do not currently have any configurability. The patch fixes some (corner) cases with the overrides (--options) handling, too: - Overrides were not applied if config file was missing or its parsing failed - Overrides for a certain source did not have effect if an empty config for the source was specified in the config file. This was caused by the first pass of parsing (config file) setting a nil pointer to the source-specific config, effectively detaching it from the main config. The second pass would then create a new instance of the source specific config, but, this was not visible in the feature source, of course.
This commit is contained in:
parent
c95ad3198c
commit
a2b9df5cd3
19 changed files with 441 additions and 156 deletions
|
@ -4,9 +4,12 @@
|
|||
|
||||
package apihelper
|
||||
|
||||
import kubernetes "k8s.io/client-go/kubernetes"
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import v1 "k8s.io/api/core/v1"
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
kubernetes "k8s.io/client-go/kubernetes"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// MockAPIHelpers is an autogenerated mock type for the APIHelpers type
|
||||
type MockAPIHelpers struct {
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
|
||||
package labeler
|
||||
|
||||
import context "context"
|
||||
import grpc "google.golang.org/grpc"
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockLabelerClient is an autogenerated mock type for the LabelerClient type
|
||||
type MockLabelerClient struct {
|
||||
|
|
|
@ -29,8 +29,11 @@ import (
|
|||
"github.com/vektra/errors"
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/labeler"
|
||||
"sigs.k8s.io/node-feature-discovery/source"
|
||||
"sigs.k8s.io/node-feature-discovery/source/cpu"
|
||||
"sigs.k8s.io/node-feature-discovery/source/fake"
|
||||
"sigs.k8s.io/node-feature-discovery/source/kernel"
|
||||
"sigs.k8s.io/node-feature-discovery/source/panic_fake"
|
||||
"sigs.k8s.io/node-feature-discovery/source/pci"
|
||||
)
|
||||
|
||||
const fakeFeatureSourceName string = "testSource"
|
||||
|
@ -91,13 +94,27 @@ func makeFakeFeatures(names []string) (source.Features, Labels) {
|
|||
return features, labels
|
||||
}
|
||||
|
||||
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", "")
|
||||
func (w *nfdWorker) getSource(name string) source.FeatureSource {
|
||||
for _, s := range w.sources {
|
||||
if s.Name() == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Convey("Should return error", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
func TestConfigParse(t *testing.T) {
|
||||
Convey("When parsing configuration", t, func() {
|
||||
w, err := NewNfdWorker(Args{Sources: []string{"cpu", "kernel", "pci"}})
|
||||
So(err, ShouldBeNil)
|
||||
worker := w.(*nfdWorker)
|
||||
Convey("and a non-accessible file and some overrides are specified", func() {
|
||||
overrides := `{"sources": {"cpu": {"cpuid": {"attributeBlacklist": ["foo","bar"]}}}}`
|
||||
worker.configure("non-existing-file", overrides)
|
||||
|
||||
Convey("overrides should take effect", func() {
|
||||
c := worker.getSource("cpu").GetConfig().(*cpu.Config)
|
||||
So(c.Cpuid.AttributeBlacklist, ShouldResemble, []string{"foo", "bar"})
|
||||
})
|
||||
})
|
||||
// Create a temporary config file
|
||||
|
@ -114,13 +131,28 @@ func TestConfigParse(t *testing.T) {
|
|||
f.Close()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("When proper config file is given", func() {
|
||||
err := configParse(f.Name(), "")
|
||||
Convey("and a proper config file is specified", func() {
|
||||
worker.configure(f.Name(), "")
|
||||
|
||||
Convey("Should return error", func() {
|
||||
Convey("specified configuration should take effect", func() {
|
||||
So(err, ShouldBeNil)
|
||||
So(config.Sources.Kernel.ConfigOpts, ShouldResemble, []string{"DMI"})
|
||||
So(config.Sources.Pci.DeviceClassWhitelist, ShouldResemble, []string{"ff"})
|
||||
c := worker.getSource("kernel").GetConfig()
|
||||
So(c.(*kernel.Config).ConfigOpts, ShouldResemble, []string{"DMI"})
|
||||
c = worker.getSource("pci").GetConfig()
|
||||
So(c.(*pci.Config).DeviceClassWhitelist, ShouldResemble, []string{"ff"})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("and a proper config file and overrides are given", func() {
|
||||
overrides := `{"sources": {"pci": {"deviceClassWhitelist": ["03"]}}}`
|
||||
worker.configure(f.Name(), overrides)
|
||||
|
||||
Convey("overrides should take precedence over the config file", func() {
|
||||
So(err, ShouldBeNil)
|
||||
c := worker.getSource("kernel").GetConfig()
|
||||
So(c.(*kernel.Config).ConfigOpts, ShouldResemble, []string{"DMI"})
|
||||
c = worker.getSource("pci").GetConfig()
|
||||
So(c.(*pci.Config).DeviceClassWhitelist, ShouldResemble, []string{"03"})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -132,11 +164,11 @@ func TestNewNfdWorker(t *testing.T) {
|
|||
Convey("without any args specified", func() {
|
||||
args := Args{}
|
||||
emptyRegexp, _ := regexp.Compile("")
|
||||
worker, err := NewNfdWorker(args)
|
||||
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
worker := w.(*nfdWorker)
|
||||
Convey("no sources should be enabled and the whitelist regexp should be empty", func() {
|
||||
So(len(worker.sources), ShouldEqual, 0)
|
||||
So(worker.labelWhiteList, ShouldResemble, emptyRegexp)
|
||||
|
@ -146,22 +178,22 @@ func TestNewNfdWorker(t *testing.T) {
|
|||
Convey("with non-empty Sources arg specified", func() {
|
||||
args := Args{Sources: []string{"fake"}}
|
||||
emptyRegexp, _ := regexp.Compile("")
|
||||
worker, err := NewNfdWorker(args)
|
||||
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
worker := w.(*nfdWorker)
|
||||
Convey("proper sources should be enabled", func() {
|
||||
So(len(worker.sources), ShouldEqual, 1)
|
||||
So(worker.sources[0], ShouldHaveSameTypeAs, fake.Source{})
|
||||
So(worker.sources[0], ShouldHaveSameTypeAs, &fake.Source{})
|
||||
So(worker.labelWhiteList, ShouldResemble, emptyRegexp)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("with invalid LabelWhiteList arg specified", func() {
|
||||
args := Args{LabelWhiteList: "*"}
|
||||
worker, err := NewNfdWorker(args)
|
||||
|
||||
w, err := NewNfdWorker(args)
|
||||
worker := w.(*nfdWorker)
|
||||
Convey("an error should be returned", func() {
|
||||
So(len(worker.sources), ShouldEqual, 0)
|
||||
So(worker.labelWhiteList, ShouldBeNil)
|
||||
|
@ -171,12 +203,12 @@ func TestNewNfdWorker(t *testing.T) {
|
|||
|
||||
Convey("with valid LabelWhiteListStr arg specified", func() {
|
||||
args := Args{LabelWhiteList: ".*rdt.*"}
|
||||
worker, err := NewNfdWorker(args)
|
||||
expectRegexp, err := regexp.Compile(".*rdt.*")
|
||||
|
||||
w, err := NewNfdWorker(args)
|
||||
Convey("no error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
worker := w.(*nfdWorker)
|
||||
expectRegexp := regexp.MustCompile(".*rdt.*")
|
||||
Convey("proper labelWhiteList regexp should be produced", func() {
|
||||
So(len(worker.sources), ShouldEqual, 0)
|
||||
So(worker.labelWhiteList, ShouldResemble, expectRegexp)
|
||||
|
|
|
@ -19,6 +19,7 @@ package nfdworker
|
|||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -50,7 +51,6 @@ import (
|
|||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// package loggers
|
||||
var (
|
||||
stdoutLogger = log.New(os.Stdout, "", log.LstdFlags)
|
||||
stderrLogger = log.New(os.Stderr, "", log.LstdFlags)
|
||||
|
@ -59,16 +59,10 @@ var (
|
|||
|
||||
// Global config
|
||||
type NFDConfig struct {
|
||||
Sources struct {
|
||||
CPU *cpu.NFDConfig `json:"cpu,omitempty"`
|
||||
Kernel *kernel.NFDConfig `json:"kernel,omitempty"`
|
||||
Pci *pci.NFDConfig `json:"pci,omitempty"`
|
||||
Usb *usb.NFDConfig `json:"usb,omitempty"`
|
||||
Custom *custom.NFDConfig `json:"custom,omitempty"`
|
||||
} `json:"sources,omitempty"`
|
||||
Sources sourcesConfig
|
||||
}
|
||||
|
||||
var config = NFDConfig{}
|
||||
type sourcesConfig map[string]source.Config
|
||||
|
||||
// Labels are a Kubernetes representation of discovered features.
|
||||
type Labels map[string]string
|
||||
|
@ -97,6 +91,7 @@ type nfdWorker struct {
|
|||
args Args
|
||||
clientConn *grpc.ClientConn
|
||||
client pb.LabelerClient
|
||||
config NFDConfig
|
||||
sources []source.FeatureSource
|
||||
labelWhiteList *regexp.Regexp
|
||||
}
|
||||
|
@ -128,21 +123,21 @@ func NewNfdWorker(args Args) (NfdWorker, error) {
|
|||
|
||||
// Figure out active sources
|
||||
allSources := []source.FeatureSource{
|
||||
cpu.Source{},
|
||||
fake.Source{},
|
||||
iommu.Source{},
|
||||
kernel.Source{},
|
||||
memory.Source{},
|
||||
network.Source{},
|
||||
panicfake.Source{},
|
||||
pci.Source{},
|
||||
storage.Source{},
|
||||
system.Source{},
|
||||
usb.Source{},
|
||||
custom.Source{},
|
||||
&cpu.Source{},
|
||||
&fake.Source{},
|
||||
&iommu.Source{},
|
||||
&kernel.Source{},
|
||||
&memory.Source{},
|
||||
&network.Source{},
|
||||
&panicfake.Source{},
|
||||
&pci.Source{},
|
||||
&storage.Source{},
|
||||
&system.Source{},
|
||||
&usb.Source{},
|
||||
&custom.Source{},
|
||||
// local needs to be the last source so that it is able to override
|
||||
// labels from other sources
|
||||
local.Source{},
|
||||
&local.Source{},
|
||||
}
|
||||
|
||||
sourceWhiteList := map[string]struct{}{}
|
||||
|
@ -173,14 +168,11 @@ func (w *nfdWorker) Run() error {
|
|||
stdoutLogger.Printf("Node Feature Discovery Worker %s", version.Get())
|
||||
stdoutLogger.Printf("NodeName: '%s'", nodeName)
|
||||
|
||||
// Parse config
|
||||
err := configParse(w.args.ConfigFile, w.args.Options)
|
||||
if err != nil {
|
||||
stderrLogger.Print(err)
|
||||
}
|
||||
// Parse and apply configuration
|
||||
w.configure(w.args.ConfigFile, w.args.Options)
|
||||
|
||||
// Connect to NFD master
|
||||
err = w.connect()
|
||||
err := w.connect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
|
@ -274,31 +266,38 @@ func (w *nfdWorker) disconnect() {
|
|||
}
|
||||
|
||||
// Parse configuration options
|
||||
func configParse(filepath string, overrides string) error {
|
||||
config.Sources.CPU = &cpu.Config
|
||||
config.Sources.Kernel = &kernel.Config
|
||||
config.Sources.Pci = &pci.Config
|
||||
config.Sources.Usb = &usb.Config
|
||||
config.Sources.Custom = &custom.Config
|
||||
|
||||
data, err := ioutil.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read config file: %s", err)
|
||||
func (w *nfdWorker) configure(filepath string, overrides string) {
|
||||
// Create a new default config
|
||||
c := NFDConfig{Sources: make(map[string]source.Config, len(w.sources))}
|
||||
for _, s := range w.sources {
|
||||
c.Sources[s.Name()] = s.NewConfig()
|
||||
}
|
||||
|
||||
// Read config file
|
||||
err = yaml.Unmarshal(data, &config)
|
||||
// Try to read and parse config file
|
||||
data, err := ioutil.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse config file: %s", err)
|
||||
stderrLogger.Printf("Failed to read config file: %s", err)
|
||||
} else {
|
||||
err = yaml.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
stderrLogger.Printf("Failed to parse config file: %s", err)
|
||||
} else {
|
||||
stdoutLogger.Printf("Configuration successfully loaded from %q", filepath)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse config overrides
|
||||
err = yaml.Unmarshal([]byte(overrides), &config)
|
||||
err = yaml.Unmarshal([]byte(overrides), &c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse --options: %s", err)
|
||||
stderrLogger.Printf("Failed to parse --options: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
w.config = c
|
||||
|
||||
// (Re-)configure all sources
|
||||
for _, s := range w.sources {
|
||||
s.SetConfig(c.Sources[s.Name()])
|
||||
}
|
||||
}
|
||||
|
||||
// createFeatureLabels returns the set of feature labels from the enabled
|
||||
|
@ -343,7 +342,7 @@ func getFeatureLabels(source source.FeatureSource, labelWhiteList *regexp.Regexp
|
|||
// Prefix for labels in the default namespace
|
||||
prefix := source.Name() + "-"
|
||||
switch source.(type) {
|
||||
case local.Source:
|
||||
case *local.Source:
|
||||
// Do not prefix labels from the hooks
|
||||
prefix = ""
|
||||
}
|
||||
|
@ -409,3 +408,27 @@ func advertiseFeatureLabels(client pb.LabelerClient, labels Labels) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the Unmarshaler interface from "encoding/json"
|
||||
func (c *sourcesConfig) UnmarshalJSON(data []byte) error {
|
||||
// First do a raw parse to get the per-source data
|
||||
raw := map[string]json.RawMessage{}
|
||||
err := yaml.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Then parse each source-specific data structure
|
||||
// NOTE: we expect 'c' to be pre-populated with correct per-source data
|
||||
// types. Non-pre-populated keys are ignored.
|
||||
for k, rawv := range raw {
|
||||
if v, ok := (*c)[k]; ok {
|
||||
err := yaml.Unmarshal(rawv, &v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse %q source config: %v", k, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -29,42 +29,43 @@ type cpuidConfig struct {
|
|||
AttributeWhitelist []string `json:"attributeWhitelist,omitempty"`
|
||||
}
|
||||
|
||||
// NFDConfig is the type holding configuration of the cpuid feature source
|
||||
type NFDConfig struct {
|
||||
type Config struct {
|
||||
Cpuid cpuidConfig `json:"cpuid,omitempty"`
|
||||
}
|
||||
|
||||
// Config contains the configuration of the cpuid source
|
||||
var Config = NFDConfig{
|
||||
cpuidConfig{
|
||||
AttributeBlacklist: []string{
|
||||
"BMI1",
|
||||
"BMI2",
|
||||
"CLMUL",
|
||||
"CMOV",
|
||||
"CX16",
|
||||
"ERMS",
|
||||
"F16C",
|
||||
"HTT",
|
||||
"LZCNT",
|
||||
"MMX",
|
||||
"MMXEXT",
|
||||
"NX",
|
||||
"POPCNT",
|
||||
"RDRAND",
|
||||
"RDSEED",
|
||||
"RDTSCP",
|
||||
"SGX",
|
||||
"SGXLC",
|
||||
"SSE",
|
||||
"SSE2",
|
||||
"SSE3",
|
||||
"SSE4.1",
|
||||
"SSE4.2",
|
||||
"SSSE3",
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
cpuidConfig{
|
||||
AttributeBlacklist: []string{
|
||||
"BMI1",
|
||||
"BMI2",
|
||||
"CLMUL",
|
||||
"CMOV",
|
||||
"CX16",
|
||||
"ERMS",
|
||||
"F16C",
|
||||
"HTT",
|
||||
"LZCNT",
|
||||
"MMX",
|
||||
"MMXEXT",
|
||||
"NX",
|
||||
"POPCNT",
|
||||
"RDRAND",
|
||||
"RDSEED",
|
||||
"RDTSCP",
|
||||
"SGX",
|
||||
"SGXLC",
|
||||
"SSE",
|
||||
"SSE2",
|
||||
"SSE3",
|
||||
"SSE4.1",
|
||||
"SSE4.2",
|
||||
"SSSE3",
|
||||
},
|
||||
AttributeWhitelist: []string{},
|
||||
},
|
||||
AttributeWhitelist: []string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Filter for cpuid labels
|
||||
|
@ -73,19 +74,33 @@ type keyFilter struct {
|
|||
whitelist bool
|
||||
}
|
||||
|
||||
var cpuidFilter *keyFilter
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *Config
|
||||
cpuidFilter *keyFilter
|
||||
}
|
||||
|
||||
func (s Source) Name() string { return "cpu" }
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
if cpuidFilter == nil {
|
||||
initCpuidFilter()
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *Config:
|
||||
s.config = v
|
||||
s.initCpuidFilter()
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
||||
// Check if hyper-threading seems to be enabled
|
||||
found, err := haveThreadSiblings()
|
||||
|
@ -106,7 +121,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
// Detect CPUID
|
||||
cpuidFlags := getCpuidFlags()
|
||||
for _, f := range cpuidFlags {
|
||||
if cpuidFilter.unmask(f) {
|
||||
if s.cpuidFilter.unmask(f) {
|
||||
features["cpuid."+f] = true
|
||||
}
|
||||
}
|
||||
|
@ -155,20 +170,20 @@ func haveThreadSiblings() (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func initCpuidFilter() {
|
||||
func (s *Source) initCpuidFilter() {
|
||||
newFilter := keyFilter{keys: map[string]struct{}{}}
|
||||
if len(Config.Cpuid.AttributeWhitelist) > 0 {
|
||||
for _, k := range Config.Cpuid.AttributeWhitelist {
|
||||
if len(s.config.Cpuid.AttributeWhitelist) > 0 {
|
||||
for _, k := range s.config.Cpuid.AttributeWhitelist {
|
||||
newFilter.keys[k] = struct{}{}
|
||||
}
|
||||
newFilter.whitelist = true
|
||||
} else {
|
||||
for _, k := range Config.Cpuid.AttributeBlacklist {
|
||||
for _, k := range s.config.Cpuid.AttributeBlacklist {
|
||||
newFilter.keys[k] = struct{}{}
|
||||
}
|
||||
newFilter.whitelist = false
|
||||
}
|
||||
cpuidFilter = &newFilter
|
||||
s.cpuidFilter = &newFilter
|
||||
}
|
||||
|
||||
func (f keyFilter) unmask(k string) bool {
|
||||
|
|
|
@ -36,20 +36,41 @@ type FeatureSpec struct {
|
|||
MatchOn []MatchRule `json:"matchOn"`
|
||||
}
|
||||
|
||||
type NFDConfig []FeatureSpec
|
||||
type config []FeatureSpec
|
||||
|
||||
var Config = NFDConfig{}
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *config {
|
||||
return &config{}
|
||||
}
|
||||
|
||||
// Implements FeatureSource Interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *config
|
||||
}
|
||||
|
||||
// Return name of the feature source
|
||||
func (s Source) Name() string { return "custom" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *config:
|
||||
s.config = v
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
// Discover features
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
allFeatureConfig := append(getStaticFeatureConfig(), Config...)
|
||||
allFeatureConfig := append(getStaticFeatureConfig(), *s.config...)
|
||||
log.Printf("INFO: Custom features: %+v", allFeatureConfig)
|
||||
// Iterate over features
|
||||
for _, customFeature := range allFeatureConfig {
|
||||
|
|
|
@ -24,6 +24,18 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "fake" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Configure method of the FeatureSource interface
|
||||
func (s Source) Configure([]byte) error { return nil }
|
||||
|
||||
// Discover returns feature names for some fake features.
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
// Adding three fake features.
|
||||
|
|
|
@ -28,6 +28,15 @@ type Source struct{}
|
|||
|
||||
func (s Source) Name() string { return "iommu" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
||||
|
|
|
@ -30,27 +30,48 @@ import (
|
|||
)
|
||||
|
||||
// Configuration file options
|
||||
type NFDConfig struct {
|
||||
type Config struct {
|
||||
KconfigFile string
|
||||
ConfigOpts []string `json:"configOpts,omitempty"`
|
||||
}
|
||||
|
||||
var Config = NFDConfig{
|
||||
KconfigFile: "",
|
||||
ConfigOpts: []string{
|
||||
"NO_HZ",
|
||||
"NO_HZ_IDLE",
|
||||
"NO_HZ_FULL",
|
||||
"PREEMPT",
|
||||
},
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
KconfigFile: "",
|
||||
ConfigOpts: []string{
|
||||
"NO_HZ",
|
||||
"NO_HZ_IDLE",
|
||||
"NO_HZ_FULL",
|
||||
"PREEMPT",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
func (s Source) Name() string { return "kernel" }
|
||||
func (s *Source) Name() string { return "kernel" }
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *Config:
|
||||
s.config = v
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
||||
// Read kernel version
|
||||
|
@ -64,13 +85,13 @@ func (s Source) Discover() (source.Features, error) {
|
|||
}
|
||||
|
||||
// Read kconfig
|
||||
kconfig, err := parseKconfig()
|
||||
kconfig, err := s.parseKconfig()
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Failed to read kconfig: %s", err)
|
||||
}
|
||||
|
||||
// Check flags
|
||||
for _, opt := range Config.ConfigOpts {
|
||||
for _, opt := range s.config.ConfigOpts {
|
||||
if val, ok := kconfig[opt]; ok {
|
||||
features["config."+opt] = val
|
||||
}
|
||||
|
@ -137,16 +158,16 @@ func readKconfigGzip(filename string) ([]byte, error) {
|
|||
}
|
||||
|
||||
// Read kconfig into a map
|
||||
func parseKconfig() (map[string]string, error) {
|
||||
func (s *Source) parseKconfig() (map[string]string, error) {
|
||||
kconfig := map[string]string{}
|
||||
raw := []byte(nil)
|
||||
var err error
|
||||
|
||||
// First, try kconfig specified in the config file
|
||||
if len(Config.KconfigFile) > 0 {
|
||||
raw, err = ioutil.ReadFile(Config.KconfigFile)
|
||||
if len(s.config.KconfigFile) > 0 {
|
||||
raw, err = ioutil.ReadFile(s.config.KconfigFile)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Failed to read kernel config from %s: %s", Config.KconfigFile, err)
|
||||
log.Printf("ERROR: Failed to read kernel config from %s: %s", s.config.KconfigFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,15 @@ type Source struct{}
|
|||
|
||||
func (s Source) Name() string { return "local" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
featuresFromHooks, err := getFeaturesFromHooks()
|
||||
if err != nil {
|
||||
|
|
|
@ -31,6 +31,15 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "memory" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Discover returns feature names for memory: numa if more than one memory node is present.
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
|
|
@ -34,6 +34,22 @@ func (_m *MockFeatureSource) Discover() (Features, error) {
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// GetConfig provides a mock function with given fields:
|
||||
func (_m *MockFeatureSource) GetConfig() Config {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 Config
|
||||
if rf, ok := ret.Get(0).(func() Config); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(Config)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Name provides a mock function with given fields:
|
||||
func (_m *MockFeatureSource) Name() string {
|
||||
ret := _m.Called()
|
||||
|
@ -47,3 +63,24 @@ func (_m *MockFeatureSource) Name() string {
|
|||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewConfig provides a mock function with given fields:
|
||||
func (_m *MockFeatureSource) NewConfig() Config {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 Config
|
||||
if rf, ok := ret.Get(0).(func() Config); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(Config)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetConfig provides a mock function with given fields: _a0
|
||||
func (_m *MockFeatureSource) SetConfig(_a0 Config) {
|
||||
_m.Called(_a0)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,15 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "network" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Discover returns feature names sriov-configured and sriov if SR-IOV capable NICs are present and/or SR-IOV virtual functions are configured on the node
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
|
|
@ -24,6 +24,15 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "panic_fake" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Discover calls panic().
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
panic("fake panic error")
|
||||
|
|
|
@ -25,22 +25,43 @@ import (
|
|||
pciutils "sigs.k8s.io/node-feature-discovery/source/internal"
|
||||
)
|
||||
|
||||
type NFDConfig struct {
|
||||
type Config struct {
|
||||
DeviceClassWhitelist []string `json:"deviceClassWhitelist,omitempty"`
|
||||
DeviceLabelFields []string `json:"deviceLabelFields,omitempty"`
|
||||
}
|
||||
|
||||
var Config = NFDConfig{
|
||||
DeviceClassWhitelist: []string{"03", "0b40", "12"},
|
||||
DeviceLabelFields: []string{"class", "vendor"},
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
DeviceClassWhitelist: []string{"03", "0b40", "12"},
|
||||
DeviceLabelFields: []string{"class", "vendor"},
|
||||
}
|
||||
}
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
// Return name of the feature source
|
||||
func (s Source) Name() string { return "pci" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *Config:
|
||||
s.config = v
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
// Discover features
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
@ -48,7 +69,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
// Construct a device label format, a sorted list of valid attributes
|
||||
deviceLabelFields := []string{}
|
||||
configLabelFields := map[string]bool{}
|
||||
for _, field := range Config.DeviceLabelFields {
|
||||
for _, field := range s.config.DeviceLabelFields {
|
||||
configLabelFields[field] = true
|
||||
}
|
||||
|
||||
|
@ -87,7 +108,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
|
||||
// Iterate over all device classes
|
||||
for class, classDevs := range devs {
|
||||
for _, white := range Config.DeviceClassWhitelist {
|
||||
for _, white := range s.config.DeviceClassWhitelist {
|
||||
if strings.HasPrefix(class, strings.ToLower(white)) {
|
||||
for _, dev := range classDevs {
|
||||
devLabel := ""
|
||||
|
|
|
@ -39,4 +39,16 @@ type FeatureSource interface {
|
|||
|
||||
// Discover returns discovered features for this node.
|
||||
Discover() (Features, error)
|
||||
|
||||
// NewConfig returns a new default config of the source
|
||||
NewConfig() Config
|
||||
|
||||
// GetConfig returns the effective configuration of the source
|
||||
GetConfig() Config
|
||||
|
||||
// SetConfig changes the effective configuration of the source
|
||||
SetConfig(Config)
|
||||
}
|
||||
|
||||
type Config interface {
|
||||
}
|
||||
|
|
|
@ -29,6 +29,15 @@ type Source struct{}
|
|||
// Name returns an identifier string for this feature source.
|
||||
func (s Source) Name() string { return "storage" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
// Discover returns feature names for storage: nonrotationaldisk if any SSD drive present.
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
|
|
@ -36,6 +36,15 @@ type Source struct{}
|
|||
|
||||
func (s Source) Name() string { return "system" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return nil }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return nil }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(source.Config) {}
|
||||
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
||||
|
|
|
@ -25,25 +25,46 @@ import (
|
|||
usbutils "sigs.k8s.io/node-feature-discovery/source/internal"
|
||||
)
|
||||
|
||||
type NFDConfig struct {
|
||||
type Config struct {
|
||||
DeviceClassWhitelist []string `json:"deviceClassWhitelist,omitempty"`
|
||||
DeviceLabelFields []string `json:"deviceLabelFields,omitempty"`
|
||||
}
|
||||
|
||||
var Config = NFDConfig{
|
||||
// Whitelist specific USB classes: https://www.usb.org/defined-class-codes
|
||||
// By default these include classes where different accelerators are typically mapped:
|
||||
// Video (0e), Miscellaneous (ef), Application Specific (fe), and Vendor Specific (ff).
|
||||
DeviceClassWhitelist: []string{"0e", "ef", "fe", "ff"},
|
||||
DeviceLabelFields: []string{"class", "vendor", "device"},
|
||||
// newDefaultConfig returns a new config with pre-populated defaults
|
||||
func newDefaultConfig() *Config {
|
||||
return &Config{
|
||||
// Whitelist specific USB classes: https://www.usb.org/defined-class-codes
|
||||
// By default these include classes where different accelerators are typically mapped:
|
||||
// Video (0e), Miscellaneous (ef), Application Specific (fe), and Vendor Specific (ff).
|
||||
DeviceClassWhitelist: []string{"0e", "ef", "fe", "ff"},
|
||||
DeviceLabelFields: []string{"class", "vendor", "device"},
|
||||
}
|
||||
}
|
||||
|
||||
// Implement FeatureSource interface
|
||||
type Source struct{}
|
||||
type Source struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
// Return name of the feature source
|
||||
func (s Source) Name() string { return "usb" }
|
||||
|
||||
// NewConfig method of the FeatureSource interface
|
||||
func (s *Source) NewConfig() source.Config { return newDefaultConfig() }
|
||||
|
||||
// GetConfig method of the FeatureSource interface
|
||||
func (s *Source) GetConfig() source.Config { return s.config }
|
||||
|
||||
// SetConfig method of the FeatureSource interface
|
||||
func (s *Source) SetConfig(conf source.Config) {
|
||||
switch v := conf.(type) {
|
||||
case *Config:
|
||||
s.config = v
|
||||
default:
|
||||
log.Printf("PANIC: invalid config type: %T", conf)
|
||||
}
|
||||
}
|
||||
|
||||
// Discover features
|
||||
func (s Source) Discover() (source.Features, error) {
|
||||
features := source.Features{}
|
||||
|
@ -51,7 +72,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
// Construct a device label format, a sorted list of valid attributes
|
||||
deviceLabelFields := []string{}
|
||||
configLabelFields := map[string]bool{}
|
||||
for _, field := range Config.DeviceLabelFields {
|
||||
for _, field := range s.config.DeviceLabelFields {
|
||||
configLabelFields[field] = true
|
||||
}
|
||||
|
||||
|
@ -86,7 +107,7 @@ func (s Source) Discover() (source.Features, error) {
|
|||
|
||||
// Iterate over all device classes
|
||||
for class, classDevs := range devs {
|
||||
for _, white := range Config.DeviceClassWhitelist {
|
||||
for _, white := range s.config.DeviceClassWhitelist {
|
||||
if strings.HasPrefix(class, strings.ToLower(white)) {
|
||||
for _, dev := range classDevs {
|
||||
devLabel := ""
|
||||
|
|
Loading…
Reference in a new issue