diff --git a/Makefile b/Makefile index b50d97b97..f5bfb38ff 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,7 @@ helm-lint: helm lint --strict deployment/helm/node-feature-discovery/ test: - $(GO_CMD) test ./cmd/... ./pkg/... + $(GO_CMD) test ./cmd/... ./pkg/... ./source/... e2e-test: @if [ -z ${KUBECONFIG} ]; then echo "[ERR] KUBECONFIG missing, must be defined"; exit 1; fi diff --git a/pkg/nfd-client/worker/nfd-worker-internal_test.go b/pkg/nfd-client/worker/nfd-worker-internal_test.go index d2255cb9b..9070c0969 100644 --- a/pkg/nfd-client/worker/nfd-worker-internal_test.go +++ b/pkg/nfd-client/worker/nfd-worker-internal_test.go @@ -33,7 +33,6 @@ import ( "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" "sigs.k8s.io/node-feature-discovery/source/kernel" "sigs.k8s.io/node-feature-discovery/source/pci" ) @@ -96,15 +95,6 @@ func makeFakeFeatures(names []string) (source.FeatureLabels, Labels) { return features, labels } -func (w *nfdWorker) getSource(name string) source.LabelSource { - for _, s := range w.realSources { - if s.Name() == name { - return s - } - } - return nil -} - func TestConfigParse(t *testing.T) { Convey("When parsing configuration", t, func() { w, err := NewNfdWorker(&Args{}) @@ -130,7 +120,7 @@ func TestConfigParse(t *testing.T) { Convey("overrides should take effect", func() { So(worker.config.Core.NoPublish, ShouldBeTrue) - c := worker.getSource("cpu").GetConfig().(*cpu.Config) + c := source.GetConfigurableSource("cpu").GetConfig().(*cpu.Config) So(c.Cpuid.AttributeBlacklist, ShouldResemble, []string{"foo", "bar"}) }) }) @@ -167,9 +157,9 @@ sources: // Verify feature source config So(err, ShouldBeNil) - c := worker.getSource("kernel").GetConfig() + c := source.GetConfigurableSource("kernel").GetConfig() So(c.(*kernel.Config).ConfigOpts, ShouldResemble, []string{"DMI"}) - c = worker.getSource("pci").GetConfig() + c = source.GetConfigurableSource("pci").GetConfig() So(c.(*pci.Config).DeviceClassWhitelist, ShouldResemble, []string{"ff"}) }) }) @@ -189,9 +179,9 @@ sources: // Verify feature source config So(err, ShouldBeNil) - c := worker.getSource("kernel").GetConfig() + c := source.GetConfigurableSource("kernel").GetConfig() So(c.(*kernel.Config).ConfigOpts, ShouldResemble, []string{"DMI"}) - c = worker.getSource("pci").GetConfig() + c = source.GetConfigurableSource("pci").GetConfig() So(c.(*pci.Config).DeviceClassWhitelist, ShouldResemble, []string{"03"}) }) }) @@ -318,7 +308,7 @@ func TestNewNfdWorker(t *testing.T) { worker := w.(*nfdWorker) So(worker.configure("", ""), ShouldBeNil) Convey("all sources should be enabled and the whitelist regexp should be empty", func() { - So(len(worker.enabledSources), ShouldEqual, len(worker.realSources)) + So(len(worker.enabledSources), ShouldEqual, len(source.GetAllLabelSources())-1) So(worker.config.Core.LabelWhiteList, ShouldResemble, emptyRegexp) }) }) @@ -333,7 +323,7 @@ func TestNewNfdWorker(t *testing.T) { So(worker.configure("", ""), ShouldBeNil) Convey("proper sources should be enabled", func() { So(len(worker.enabledSources), ShouldEqual, 1) - So(worker.enabledSources[0], ShouldHaveSameTypeAs, &fake.Source{}) + So(worker.enabledSources[0].Name(), ShouldEqual, "fake") So(worker.config.Core.LabelWhiteList, ShouldResemble, emptyRegexp) }) }) @@ -356,9 +346,9 @@ func TestNewNfdWorker(t *testing.T) { func TestCreateFeatureLabels(t *testing.T) { Convey("When creating feature labels from the configured sources", t, func() { - fakeLabelSource := source.LabelSource(new(fake.Source)) - fakeLabelSource.SetConfig(fakeLabelSource.NewConfig()) - sources := []source.LabelSource{fakeLabelSource} + cs := source.GetConfigurableSource("fake") + cs.SetConfig(cs.NewConfig()) + sources := []source.LabelSource{source.GetLabelSource("fake")} Convey("When fake feature source is configured", func() { emptyLabelWL := regexp.MustCompile("") diff --git a/pkg/nfd-client/worker/nfd-worker.go b/pkg/nfd-client/worker/nfd-worker.go index d34467c70..3d8c47770 100644 --- a/pkg/nfd-client/worker/nfd-worker.go +++ b/pkg/nfd-client/worker/nfd-worker.go @@ -23,6 +23,7 @@ import ( "os" "path/filepath" "regexp" + "sort" "strings" "time" @@ -36,18 +37,20 @@ import ( "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" - "sigs.k8s.io/node-feature-discovery/source/custom" - "sigs.k8s.io/node-feature-discovery/source/fake" - "sigs.k8s.io/node-feature-discovery/source/iommu" - "sigs.k8s.io/node-feature-discovery/source/kernel" - "sigs.k8s.io/node-feature-discovery/source/local" - "sigs.k8s.io/node-feature-discovery/source/memory" - "sigs.k8s.io/node-feature-discovery/source/network" - "sigs.k8s.io/node-feature-discovery/source/pci" - "sigs.k8s.io/node-feature-discovery/source/storage" - "sigs.k8s.io/node-feature-discovery/source/system" - "sigs.k8s.io/node-feature-discovery/source/usb" + + // Register all source packages + _ "sigs.k8s.io/node-feature-discovery/source/cpu" + _ "sigs.k8s.io/node-feature-discovery/source/custom" + _ "sigs.k8s.io/node-feature-discovery/source/fake" + _ "sigs.k8s.io/node-feature-discovery/source/iommu" + _ "sigs.k8s.io/node-feature-discovery/source/kernel" + _ "sigs.k8s.io/node-feature-discovery/source/local" + _ "sigs.k8s.io/node-feature-discovery/source/memory" + _ "sigs.k8s.io/node-feature-discovery/source/network" + _ "sigs.k8s.io/node-feature-discovery/source/pci" + _ "sigs.k8s.io/node-feature-discovery/source/storage" + _ "sigs.k8s.io/node-feature-discovery/source/system" + _ "sigs.k8s.io/node-feature-discovery/source/usb" ) // Global config @@ -99,9 +102,7 @@ type nfdWorker struct { client pb.LabelerClient configFilePath string config *NFDConfig - realSources []source.LabelSource stop chan struct{} // channel for signaling stop - testSources []source.LabelSource enabledSources []source.LabelSource } @@ -121,25 +122,7 @@ func NewNfdWorker(args *Args) (nfdclient.NfdClient, error) { args: *args, config: &NFDConfig{}, - realSources: []source.LabelSource{ - &cpu.Source{}, - &iommu.Source{}, - &kernel.Source{}, - &memory.Source{}, - &network.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{}, - }, - testSources: []source.LabelSource{ - &fake.Source{}, - }, - stop: make(chan struct{}, 1), + stop: make(chan struct{}, 1), } if args.ConfigFile != "" { @@ -301,36 +284,44 @@ func (w *nfdWorker) configureCore(c coreConfig) error { } // Determine enabled feature sources - sourceList := map[string]struct{}{} - all := false - for _, s := range c.Sources { - if s == "all" { - all = true - continue + enabled := make(map[string]source.LabelSource) + for _, name := range c.Sources { + if name == "all" { + for n, s := range source.GetAllLabelSources() { + if ts, ok := s.(source.TestSource); !ok || !ts.IsTestSource() { + enabled[n] = s + } + } + } else { + if s := source.GetLabelSource(name); s != nil { + enabled[name] = s + } else { + klog.Warningf("skipping unknown source %q specified in core.sources (or --sources)", name) + } } - sourceList[strings.TrimSpace(s)] = struct{}{} } - w.enabledSources = []source.LabelSource{} - for _, s := range w.realSources { - if _, enabled := sourceList[s.Name()]; all || enabled { - w.enabledSources = append(w.enabledSources, s) - delete(sourceList, s.Name()) - } + w.enabledSources = make([]source.LabelSource, 0, len(enabled)) + for _, s := range enabled { + w.enabledSources = append(w.enabledSources, s) } - for _, s := range w.testSources { - if _, enabled := sourceList[s.Name()]; enabled { - w.enabledSources = append(w.enabledSources, s) - delete(sourceList, s.Name()) + + sort.Slice(w.enabledSources, func(i, j int) bool { + iP, jP := w.enabledSources[i].Priority(), w.enabledSources[j].Priority() + if iP != jP { + return iP < jP } - } - if len(sourceList) > 0 { - names := make([]string, 0, len(sourceList)) - for n := range sourceList { - names = append(names, n) + return w.enabledSources[i].Name() < w.enabledSources[j].Name() + }) + + if klog.V(1).Enabled() { + n := make([]string, len(w.enabledSources)) + for i, s := range w.enabledSources { + n[i] = s.Name() } - klog.Warningf("skipping unknown source(s) %q specified in core.sources (or --sources)", strings.Join(names, ", ")) + klog.Infof("enabled label sources: %s", strings.Join(n, ", ")) } + return nil } @@ -338,9 +329,9 @@ func (w *nfdWorker) configureCore(c coreConfig) error { func (w *nfdWorker) configure(filepath string, overrides string) error { // Create a new default config c := newDefaultConfig() - allSources := append(w.realSources, w.testSources...) - c.Sources = make(map[string]source.Config, len(allSources)) - for _, s := range allSources { + confSources := source.GetAllConfigurableSources() + c.Sources = make(map[string]source.Config, len(confSources)) + for _, s := range confSources { c.Sources[s.Name()] = s.NewConfig() } @@ -388,8 +379,8 @@ func (w *nfdWorker) configure(filepath string, overrides string) error { return err } - // (Re-)configure all "real" sources, test sources are not configurable - for _, s := range allSources { + // (Re-)configure sources + for _, s := range confSources { s.SetConfig(c.Sources[s.Name()]) } @@ -432,8 +423,8 @@ func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) ( // Prefix for labels in the default namespace prefix := source.Name() + "-" - switch source.(type) { - case *local.Source: + switch source.Name() { + case "local": // Do not prefix labels from the hooks prefix = "" } diff --git a/source/cpu/cpu.go b/source/cpu/cpu.go index 1563b9235..c23fff4f3 100644 --- a/source/cpu/cpu.go +++ b/source/cpu/cpu.go @@ -78,22 +78,29 @@ type keyFilter struct { whitelist bool } -// Source implements LabelSource. -type Source struct { +// cpuSource implements the LabelSource and ConfigurableSource interfaces. +type cpuSource struct { config *Config cpuidFilter *keyFilter } -func (s Source) Name() string { return Name } +// Singleton source instance +var ( + src cpuSource + _ source.LabelSource = &src + _ source.ConfigurableSource = &src +) + +func (s *cpuSource) Name() string { return Name } // NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return newDefaultConfig() } +func (s *cpuSource) NewConfig() source.Config { return newDefaultConfig() } // GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return s.config } +func (s *cpuSource) GetConfig() source.Config { return s.config } // SetConfig method of the LabelSource interface -func (s *Source) SetConfig(conf source.Config) { +func (s *cpuSource) SetConfig(conf source.Config) { switch v := conf.(type) { case *Config: s.config = v @@ -103,7 +110,10 @@ func (s *Source) SetConfig(conf source.Config) { } } -func (s *Source) Discover() (source.FeatureLabels, error) { +// Priority method of the LabelSource interface +func (s *cpuSource) Priority() int { return 0 } + +func (s *cpuSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} // Check if hyper-threading seems to be enabled @@ -182,7 +192,7 @@ func haveThreadSiblings() (bool, error) { return false, nil } -func (s *Source) initCpuidFilter() { +func (s *cpuSource) initCpuidFilter() { newFilter := keyFilter{keys: map[string]struct{}{}} if len(s.config.Cpuid.AttributeWhitelist) > 0 { for _, k := range s.config.Cpuid.AttributeWhitelist { @@ -210,3 +220,7 @@ func (f keyFilter) unmask(k string) bool { } return false } + +func init() { + source.Register(&src) +} diff --git a/source/custom/custom.go b/source/custom/custom.go index b1bca0d94..6bfaf582a 100644 --- a/source/custom/custom.go +++ b/source/custom/custom.go @@ -51,22 +51,29 @@ func newDefaultConfig() *config { return &config{} } -// Source implements LabelSource. -type Source struct { +// customSource implements the LabelSource and ConfigurableSource interfaces. +type customSource struct { config *config } +// Singleton source instance +var ( + src customSource + _ source.LabelSource = &src + _ source.ConfigurableSource = &src +) + // Name returns the name of the feature source -func (s Source) Name() string { return Name } +func (s *customSource) Name() string { return Name } // NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return newDefaultConfig() } +func (s *customSource) NewConfig() source.Config { return newDefaultConfig() } // GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return s.config } +func (s *customSource) GetConfig() source.Config { return s.config } // SetConfig method of the LabelSource interface -func (s *Source) SetConfig(conf source.Config) { +func (s *customSource) SetConfig(conf source.Config) { switch v := conf.(type) { case *config: s.config = v @@ -75,8 +82,11 @@ func (s *Source) SetConfig(conf source.Config) { } } +// Priority method of the LabelSource interface +func (s *customSource) Priority() int { return 10 } + // Discover features -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *customSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} allFeatureConfig := append(getStaticFeatureConfig(), *s.config...) allFeatureConfig = append(allFeatureConfig, getDirectoryFeatureConfig()...) @@ -101,7 +111,7 @@ func (s Source) Discover() (source.FeatureLabels, error) { // Process a single feature by Matching on the defined rules. // A feature is present if all defined Rules in a MatchRule return a match. -func (s Source) discoverFeature(feature FeatureSpec) (bool, error) { +func (s *customSource) discoverFeature(feature FeatureSpec) (bool, error) { for _, matchRules := range feature.MatchOn { allRules := []rules.Rule{ @@ -136,3 +146,7 @@ func (s Source) discoverFeature(feature FeatureSpec) (bool, error) { } return false, nil } + +func init() { + source.Register(&src) +} diff --git a/source/fake/fake.go b/source/fake/fake.go index 43e676b02..9d9f8d2aa 100644 --- a/source/fake/fake.go +++ b/source/fake/fake.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors. +Copyright 2017-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. @@ -40,22 +40,29 @@ func newDefaultConfig() *Config { } } -// Source implements LabelSource. -type Source struct { +// fakeSource implements the LabelSource and ConfigurableSource interfaces. +type fakeSource struct { config *Config } +// Singleton source instance +var ( + src fakeSource + _ source.LabelSource = &src + _ source.ConfigurableSource = &src +) + // Name returns an identifier string for this feature source. -func (s Source) Name() string { return Name } +func (s *fakeSource) Name() string { return Name } // NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return newDefaultConfig() } +func (s *fakeSource) NewConfig() source.Config { return newDefaultConfig() } // GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return s.config } +func (s *fakeSource) GetConfig() source.Config { return s.config } // SetConfig method of the LabelSource interface -func (s *Source) SetConfig(conf source.Config) { +func (s *fakeSource) SetConfig(conf source.Config) { switch v := conf.(type) { case *Config: s.config = v @@ -65,10 +72,13 @@ func (s *Source) SetConfig(conf source.Config) { } // Configure method of the LabelSource interface -func (s Source) Configure([]byte) error { return nil } +func (s *fakeSource) Configure([]byte) error { return nil } + +// Priority method of the LabelSource interface +func (s *fakeSource) Priority() int { return 0 } // Discover returns feature names for some fake features. -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *fakeSource) Discover() (source.FeatureLabels, error) { // Adding three fake features. features := make(source.FeatureLabels, len(s.config.Labels)) for k, v := range s.config.Labels { @@ -77,3 +87,10 @@ func (s Source) Discover() (source.FeatureLabels, error) { return features, nil } + +// IsTestSource method of the LabelSource interface +func (s *fakeSource) IsTestSource() bool { return true } + +func init() { + source.Register(&src) +} diff --git a/source/iommu/iommu.go b/source/iommu/iommu.go index 36849a847..f731bf934 100644 --- a/source/iommu/iommu.go +++ b/source/iommu/iommu.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2018-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. @@ -25,21 +25,21 @@ import ( const Name = "iommu" -// Source implements LabelSource. -type Source struct{} +// iommuSource implements the LabelSource interface. +type iommuSource struct{} -func (s Source) Name() string { return Name } +func (s *iommuSource) Name() string { return Name } -// NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return nil } +// Singleton source instance +var ( + src iommuSource + _ source.LabelSource = &src +) -// GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return nil } +// Priority method of the LabelSource interface +func (s *iommuSource) Priority() int { return 0 } -// SetConfig method of the LabelSource interface -func (s *Source) SetConfig(source.Config) {} - -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *iommuSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} // Check if any iommu devices are available @@ -54,3 +54,7 @@ func (s Source) Discover() (source.FeatureLabels, error) { return features, nil } + +func init() { + source.Register(&src) +} diff --git a/source/kernel/kernel.go b/source/kernel/kernel.go index 88e8be782..045809135 100644 --- a/source/kernel/kernel.go +++ b/source/kernel/kernel.go @@ -47,21 +47,28 @@ func newDefaultConfig() *Config { } } -// Source implements LabelSource. -type Source struct { +// kernelSource implements the LabelSource and ConfigurableSource interfaces. +type kernelSource struct { config *Config } -func (s *Source) Name() string { return Name } +// Singleton source instance +var ( + src kernelSource + _ source.LabelSource = &src + _ source.ConfigurableSource = &src +) + +func (s *kernelSource) Name() string { return Name } // NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return newDefaultConfig() } +func (s *kernelSource) NewConfig() source.Config { return newDefaultConfig() } // GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return s.config } +func (s *kernelSource) GetConfig() source.Config { return s.config } // SetConfig method of the LabelSource interface -func (s *Source) SetConfig(conf source.Config) { +func (s *kernelSource) SetConfig(conf source.Config) { switch v := conf.(type) { case *Config: s.config = v @@ -70,7 +77,10 @@ func (s *Source) SetConfig(conf source.Config) { } } -func (s *Source) Discover() (source.FeatureLabels, error) { +// Priority method of the LabelSource interface +func (s *kernelSource) Priority() int { return 0 } + +func (s *kernelSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} // Read kernel version @@ -135,3 +145,7 @@ func parseVersion() (map[string]string, error) { return version, nil } + +func init() { + source.Register(&src) +} diff --git a/source/local/local.go b/source/local/local.go index 8fc9331f7..dc3427fe1 100644 --- a/source/local/local.go +++ b/source/local/local.go @@ -38,23 +38,23 @@ var ( hookDir = "/etc/kubernetes/node-feature-discovery/source.d/" ) -// Source implements LabelSource. -type Source struct{} +// localSource implements the LabelSource interface. +type localSource struct{} + +// Singleton source instance +var ( + src localSource + _ source.LabelSource = &src +) // Name method of the LabelSource interface -func (s Source) Name() string { return Name } +func (s *localSource) Name() string { return Name } -// NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return nil } - -// GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return nil } - -// SetConfig method of the LabelSource interface -func (s *Source) SetConfig(source.Config) {} +// Priority method of the LabelSource interface +func (s *localSource) Priority() int { return 20 } // Discover method of the LabelSource interface -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *localSource) Discover() (source.FeatureLabels, error) { featuresFromHooks, err := getFeaturesFromHooks() if err != nil { klog.Error(err) @@ -240,3 +240,7 @@ func getFileContent(fileName string) ([][]byte, error) { return lines, nil } + +func init() { + source.Register(&src) +} diff --git a/source/memory/memory.go b/source/memory/memory.go index 4311171ae..9fde92010 100644 --- a/source/memory/memory.go +++ b/source/memory/memory.go @@ -28,23 +28,23 @@ import ( const Name = "memory" -// Source implements LabelSource. -type Source struct{} +// memorySource implements the LabelSource interface. +type memorySource struct{} + +// Singleton source instance +var ( + src memorySource + _ source.LabelSource = &src +) // Name returns an identifier string for this feature source. -func (s Source) Name() string { return Name } +func (s *memorySource) Name() string { return Name } -// NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return nil } - -// GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return nil } - -// SetConfig method of the LabelSource interface -func (s *Source) SetConfig(source.Config) {} +// Priority method of the LabelSource interface +func (s *memorySource) Priority() int { return 0 } // Discover returns feature names for memory: numa if more than one memory node is present. -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *memorySource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} // Detect NUMA @@ -118,3 +118,7 @@ func detectNvdimm() (map[string]bool, error) { return features, nil } + +func init() { + source.Register(&src) +} diff --git a/source/mock_LabelSource.go b/source/mock_LabelSource.go index 82d55fdcc..96865c6fb 100644 --- a/source/mock_LabelSource.go +++ b/source/mock_LabelSource.go @@ -34,22 +34,6 @@ func (_m *MockLabelSource) Discover() (FeatureLabels, error) { return r0, r1 } -// GetConfig provides a mock function with given fields: -func (_m *MockLabelSource) 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 *MockLabelSource) Name() string { ret := _m.Called() @@ -64,23 +48,16 @@ func (_m *MockLabelSource) Name() string { return r0 } -// NewConfig provides a mock function with given fields: -func (_m *MockLabelSource) NewConfig() Config { +// Priority provides a mock function with given fields: +func (_m *MockLabelSource) Priority() int { ret := _m.Called() - var r0 Config - if rf, ok := ret.Get(0).(func() Config); ok { + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { r0 = rf() } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(Config) - } + r0 = ret.Get(0).(int) } return r0 } - -// SetConfig provides a mock function with given fields: _a0 -func (_m *MockLabelSource) SetConfig(_a0 Config) { - _m.Called(_a0) -} diff --git a/source/network/network.go b/source/network/network.go index 9a1748722..2ebf26778 100644 --- a/source/network/network.go +++ b/source/network/network.go @@ -41,23 +41,23 @@ const ( const sysfsBaseDir = "class/net" -// Source implements LabelSource. -type Source struct{} +// networkSource implements the LabelSource interface. +type networkSource struct{} + +// Singleton source instance +var ( + src networkSource + _ source.LabelSource = &src +) // Name returns an identifier string for this feature source. -func (s Source) Name() string { return Name } +func (s *networkSource) Name() string { return Name } -// NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return nil } - -// GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return nil } - -// SetConfig method of the LabelSource interface -func (s *Source) SetConfig(source.Config) {} +// Priority method of the LabelSource interface +func (s *networkSource) Priority() int { return 0 } // 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.FeatureLabels, error) { +func (s *networkSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} netInterfaces, err := ioutil.ReadDir(source.SysfsDir.Path(sysfsBaseDir)) @@ -128,3 +128,7 @@ func readIfFlags(name string) (uint64, error) { return flags, nil } + +func init() { + source.Register(&src) +} diff --git a/source/pci/pci.go b/source/pci/pci.go index a68f270ae..b100f0fbe 100644 --- a/source/pci/pci.go +++ b/source/pci/pci.go @@ -41,22 +41,29 @@ func newDefaultConfig() *Config { } } -// Source implements LabelSource. -type Source struct { +// pciSource implements the LabelSource and ConfigurableSource interfaces. +type pciSource struct { config *Config } +// Singleton source instance +var ( + src pciSource + _ source.LabelSource = &src + _ source.ConfigurableSource = &src +) + // Name returns the name of the feature source -func (s Source) Name() string { return Name } +func (s *pciSource) Name() string { return Name } // NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return newDefaultConfig() } +func (s *pciSource) NewConfig() source.Config { return newDefaultConfig() } // GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return s.config } +func (s *pciSource) GetConfig() source.Config { return s.config } // SetConfig method of the LabelSource interface -func (s *Source) SetConfig(conf source.Config) { +func (s *pciSource) SetConfig(conf source.Config) { switch v := conf.(type) { case *Config: s.config = v @@ -65,8 +72,11 @@ func (s *Source) SetConfig(conf source.Config) { } } +// Priority method of the LabelSource interface +func (s *pciSource) Priority() int { return 0 } + // Discover features -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *pciSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} // Construct a device label format, a sorted list of valid attributes @@ -132,3 +142,7 @@ func (s Source) Discover() (source.FeatureLabels, error) { } return features, nil } + +func init() { + source.Register(&src) +} diff --git a/source/source.go b/source/source.go index d70e10983..e490857f5 100644 --- a/source/source.go +++ b/source/source.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors. +Copyright 2017-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. @@ -16,20 +16,31 @@ limitations under the License. package source -// FeatureLabelValue represents the value of one feature label -type FeatureLabelValue interface{} +import ( + "fmt" +) -// FeatureLabels is a collection of feature labels -type FeatureLabels map[string]FeatureLabelValue +// Source is the base interface for all other source interfaces +type Source interface { + // Name returns a friendly name for this source + Name() string +} // LabelSource represents a source of node feature labels type LabelSource interface { - // Name returns a friendly name for this source - Name() string + Source // Discover returns discovered feature labels Discover() (FeatureLabels, error) + // Priority returns the priority of the source + Priority() int +} + +// ConfigurableSource is an interface for a source that can be configured +type ConfigurableSource interface { + Source + // NewConfig returns a new default config of the source NewConfig() Config @@ -40,6 +51,69 @@ type LabelSource interface { SetConfig(Config) } +// TestSource represents a source purposed for testing only +type TestSource interface { + Source + + // IsTestSource returns true if the source is not for production + IsTestSource() bool +} + +// FeatureLabelValue represents the value of one feature label +type FeatureLabelValue interface{} + +// FeatureLabels is a collection of feature labels +type FeatureLabels map[string]FeatureLabelValue + // Config is the generic interface for source configuration data type Config interface { } + +// sources contain all registered sources +var sources = make(map[string]Source) + +// RegisterSource registers a source +func Register(s Source) { + if name, ok := sources[s.Name()]; ok { + panic(fmt.Sprintf("source %q already registered", name)) + } + sources[s.Name()] = s +} + +// GetLabelSource a registered label source +func GetLabelSource(name string) LabelSource { + if s, ok := sources[name].(LabelSource); ok { + return s + } + return nil +} + +// GetAllLabelSources returns all registered label sources +func GetAllLabelSources() map[string]LabelSource { + all := make(map[string]LabelSource) + for k, v := range sources { + if s, ok := v.(LabelSource); ok { + all[k] = s + } + } + return all +} + +// GetConfigurableSource a registered configurable source +func GetConfigurableSource(name string) ConfigurableSource { + if s, ok := sources[name].(ConfigurableSource); ok { + return s + } + return nil +} + +// GetAllConfigurableSources returns all registered configurable sources +func GetAllConfigurableSources() map[string]ConfigurableSource { + all := make(map[string]ConfigurableSource) + for k, v := range sources { + if s, ok := v.(ConfigurableSource); ok { + all[k] = s + } + } + return all +} diff --git a/source/source_test.go b/source/source_test.go new file mode 100644 index 000000000..c8a6980ae --- /dev/null +++ b/source/source_test.go @@ -0,0 +1,63 @@ +/* +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 source_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + source "sigs.k8s.io/node-feature-discovery/source" + + // Register all source packages + _ "sigs.k8s.io/node-feature-discovery/source/cpu" + _ "sigs.k8s.io/node-feature-discovery/source/custom" + _ "sigs.k8s.io/node-feature-discovery/source/fake" + _ "sigs.k8s.io/node-feature-discovery/source/iommu" + _ "sigs.k8s.io/node-feature-discovery/source/kernel" + _ "sigs.k8s.io/node-feature-discovery/source/local" + _ "sigs.k8s.io/node-feature-discovery/source/memory" + _ "sigs.k8s.io/node-feature-discovery/source/network" + _ "sigs.k8s.io/node-feature-discovery/source/pci" + _ "sigs.k8s.io/node-feature-discovery/source/storage" + _ "sigs.k8s.io/node-feature-discovery/source/system" + _ "sigs.k8s.io/node-feature-discovery/source/usb" +) + +func TestLabelSources(t *testing.T) { + sources := source.GetAllLabelSources() + assert.NotZero(t, len(sources)) + + for n, s := range sources { + assert.Equalf(t, n, s.Name(), "testing labelsource %q failed", n) + } +} + +func TestConfigurableSources(t *testing.T) { + sources := source.GetAllConfigurableSources() + assert.NotZero(t, len(sources)) + + for n, s := range sources { + assert.Equalf(t, n, s.Name(), "testing ConfigurableSource %q failed", n) + + c := s.NewConfig() + s.SetConfig(c) + rc := s.GetConfig() + + assert.Equalf(t, c, rc, "testing ConfigurableSource %q failed", n) + } +} diff --git a/source/storage/storage.go b/source/storage/storage.go index a2ae032fc..34383340a 100644 --- a/source/storage/storage.go +++ b/source/storage/storage.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2018-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. @@ -25,23 +25,23 @@ import ( const Name = "storage" -// Source implements LabelSource. -type Source struct{} +// storageSource implements the LabelSource interface. +type storageSource struct{} + +// Singleton source instance +var ( + src storageSource + _ source.LabelSource = &src +) // Name returns an identifier string for this feature source. -func (s Source) Name() string { return Name } +func (s *storageSource) Name() string { return Name } -// NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return nil } - -// GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return nil } - -// SetConfig method of the LabelSource interface -func (s *Source) SetConfig(source.Config) {} +// Priority method of the LabelSource interface +func (s *storageSource) Priority() int { return 0 } // Discover returns feature names for storage: nonrotationaldisk if any SSD drive present. -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *storageSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} // Check if there is any non-rotational block devices attached to the node @@ -62,3 +62,7 @@ func (s Source) Discover() (source.FeatureLabels, error) { } return features, nil } + +func init() { + source.Register(&src) +} diff --git a/source/system/system.go b/source/system/system.go index 889aa8ceb..2ee4ac029 100644 --- a/source/system/system.go +++ b/source/system/system.go @@ -34,21 +34,21 @@ var osReleaseFields = [...]string{ const Name = "system" -// Source implements LabelSource. -type Source struct{} +// systemSource implements the LabelSource interface. +type systemSource struct{} -func (s Source) Name() string { return Name } +// Singleton source instance +var ( + src systemSource + _ source.LabelSource = &src +) -// NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return nil } +func (s *systemSource) Name() string { return Name } -// GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return nil } +// Priority method of the LabelSource interface +func (s *systemSource) Priority() int { return 0 } -// SetConfig method of the LabelSource interface -func (s *Source) SetConfig(source.Config) {} - -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *systemSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} release, err := parseOSRelease() @@ -112,3 +112,7 @@ func splitVersion(version string) map[string]string { } return components } + +func init() { + source.Register(&src) +} diff --git a/source/usb/usb.go b/source/usb/usb.go index 81b8003ef..80774fbec 100644 --- a/source/usb/usb.go +++ b/source/usb/usb.go @@ -44,22 +44,29 @@ func newDefaultConfig() *Config { } } -// Source implements LabelSource. -type Source struct { +// usbSource implements the LabelSource and ConfigurableSource interfaces. +type usbSource struct { config *Config } +// Singleton source instance +var ( + src usbSource + _ source.LabelSource = &src + _ source.ConfigurableSource = &src +) + // Name returns the name of the feature source -func (s Source) Name() string { return Name } +func (s *usbSource) Name() string { return Name } // NewConfig method of the LabelSource interface -func (s *Source) NewConfig() source.Config { return newDefaultConfig() } +func (s *usbSource) NewConfig() source.Config { return newDefaultConfig() } // GetConfig method of the LabelSource interface -func (s *Source) GetConfig() source.Config { return s.config } +func (s *usbSource) GetConfig() source.Config { return s.config } // SetConfig method of the LabelSource interface -func (s *Source) SetConfig(conf source.Config) { +func (s *usbSource) SetConfig(conf source.Config) { switch v := conf.(type) { case *Config: s.config = v @@ -68,8 +75,11 @@ func (s *Source) SetConfig(conf source.Config) { } } +// Priority method of the LabelSource interface +func (s *usbSource) Priority() int { return 0 } + // Discover features -func (s Source) Discover() (source.FeatureLabels, error) { +func (s *usbSource) Discover() (source.FeatureLabels, error) { features := source.FeatureLabels{} // Construct a device label format, a sorted list of valid attributes @@ -127,3 +137,7 @@ func (s Source) Discover() (source.FeatureLabels, error) { } return features, nil } + +func init() { + source.Register(&src) +}