1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-05 08:17:04 +00:00

Merge pull request #601 from marquiz/devel/feature-source-interface

source: introduce FeatureSource interface
This commit is contained in:
Kubernetes Prow Robot 2021-09-21 05:48:25 -07:00 committed by GitHub
commit 064391f310
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 177 additions and 29 deletions

View file

@ -0,0 +1,36 @@
/*
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 feature
// NewDomainFeatures creates a new instance of Features, initializing specified
// features to empty values
func NewDomainFeatures() *DomainFeatures {
return &DomainFeatures{
Keys: make(map[string]KeyFeatureSet),
Values: make(map[string]ValueFeatureSet),
Instances: make(map[string]InstanceFeatureSet)}
}
func NewKeyFeatures() *KeyFeatureSet { return &KeyFeatureSet{Elements: make(map[string]Nil)} }
func NewValueFeatures() *ValueFeatureSet { return &ValueFeatureSet{Elements: make(map[string]string)} }
func NewInstanceFeatures() *InstanceFeatureSet { return &InstanceFeatureSet{} }
func NewInstanceFeature() *InstanceFeature {
return &InstanceFeature{Attributes: make(map[string]string)}
}

51
pkg/api/feature/types.go Normal file
View file

@ -0,0 +1,51 @@
/*
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 feature
// Features is a collection of all features of the system, arranged by domain.
// +protobuf
type Features map[string]*DomainFeatures
// DomainFeatures is the collection of all discovered features of one domain.
type DomainFeatures struct {
Keys map[string]KeyFeatureSet `protobuf:"bytes,1,rep,name=keys"`
Values map[string]ValueFeatureSet `protobuf:"bytes,2,rep,name=values"`
Instances map[string]InstanceFeatureSet `protobuf:"bytes,3,rep,name=instances"`
}
// KeyFeatureSet is a set of simple features only containing names without values.
type KeyFeatureSet struct {
Elements map[string]Nil `protobuf:"bytes,1,rep,name=elements"`
}
// ValueFeatureSet is a set of features having string value.
type ValueFeatureSet struct {
Elements map[string]string `protobuf:"bytes,1,rep,name=elements"`
}
// InstanceFeatureSet is a set of features each of which is an instance having multiple attributes.
type InstanceFeatureSet struct {
Elements []InstanceFeature `protobuf:"bytes,1,rep,name=elements"`
}
// InstanceFeature represents one instance of a complex features, e.g. a device.
type InstanceFeature struct {
Attributes map[string]string `protobuf:"bytes,1,rep,name=attributes"`
}
// Nil is a dummy empty struct for protobuf compatibility
type Nil struct{}

View file

@ -39,7 +39,7 @@ import (
const fakeLabelSourceName string = "testSource"
func TestDiscoveryWithMockSources(t *testing.T) {
func TestGetLabelsWithMockSources(t *testing.T) {
Convey("When I discover features from fake source and update the node using fake client", t, func() {
mockLabelSource := new(source.MockLabelSource)
allFeatureNames := []string{"testfeature1", "testfeature2", "test.ns/test", "test.ns/foo", "/no-ns-label", "invalid/test/feature"}
@ -54,7 +54,7 @@ func TestDiscoveryWithMockSources(t *testing.T) {
Convey("When I successfully get the labels from the mock source", func() {
mockLabelSource.On("Name").Return(fakeLabelSourceName)
mockLabelSource.On("Discover").Return(fakeFeatures, nil)
mockLabelSource.On("GetLabels").Return(fakeFeatures, nil)
returnedLabels, err := getFeatureLabels(fakeLabelSource, labelWhiteList.Regexp)
Convey("Proper label is returned", func() {
@ -67,7 +67,7 @@ func TestDiscoveryWithMockSources(t *testing.T) {
Convey("When I fail to get the labels from the mock source", func() {
expectedError := errors.New("fake error")
mockLabelSource.On("Discover").Return(nil, expectedError)
mockLabelSource.On("GetLabels").Return(nil, expectedError)
returnedLabels, err := getFeatureLabels(fakeLabelSource, labelWhiteList.Regexp)
Convey("No label is returned", func() {

View file

@ -175,6 +175,14 @@ func (w *nfdWorker) Run() error {
for {
select {
case <-labelTrigger:
// Run feature discovery
for n, s := range source.GetAllFeatureSources() {
klog.V(2).Infof("running discovery for %q source", n)
if err := s.Discover(); err != nil {
klog.Errorf("feature discovery of %q source failed: %v", n, err)
}
}
// Get the set of feature labels.
labels := createFeatureLabels(w.enabledSources, w.config.Core.LabelWhiteList.Regexp)
@ -394,7 +402,7 @@ func (w *nfdWorker) configure(filepath string, overrides string) error {
func createFeatureLabels(sources []source.LabelSource, labelWhiteList regexp.Regexp) (labels Labels) {
labels = Labels{}
// Do feature discovery from all configured sources.
// Get labels from all enabled label sources
klog.Info("starting feature discovery...")
for _, source := range sources {
labelsFromSource, err := getFeatureLabels(source, labelWhiteList)
@ -416,7 +424,7 @@ func createFeatureLabels(sources []source.LabelSource, labelWhiteList regexp.Reg
// supplied source.
func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) (labels Labels, err error) {
labels = Labels{}
features, err := source.Discover()
features, err := source.GetLabels()
if err != nil {
return nil, err
}

View file

@ -113,7 +113,8 @@ func (s *cpuSource) SetConfig(conf source.Config) {
// Priority method of the LabelSource interface
func (s *cpuSource) Priority() int { return 0 }
func (s *cpuSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *cpuSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
// Check if hyper-threading seems to be enabled

View file

@ -85,8 +85,8 @@ func (s *customSource) SetConfig(conf source.Config) {
// Priority method of the LabelSource interface
func (s *customSource) Priority() int { return 10 }
// Discover features
func (s *customSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *customSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
allFeatureConfig := append(getStaticFeatureConfig(), *s.config...)
allFeatureConfig = append(allFeatureConfig, getDirectoryFeatureConfig()...)

View file

@ -77,8 +77,8 @@ 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 *fakeSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *fakeSource) GetLabels() (source.FeatureLabels, error) {
// Adding three fake features.
features := make(source.FeatureLabels, len(s.config.Labels))
for k, v := range s.config.Labels {

View file

@ -39,7 +39,8 @@ var (
// Priority method of the LabelSource interface
func (s *iommuSource) Priority() int { return 0 }
func (s *iommuSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *iommuSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
// Check if any iommu devices are available

View file

@ -80,7 +80,8 @@ func (s *kernelSource) SetConfig(conf source.Config) {
// Priority method of the LabelSource interface
func (s *kernelSource) Priority() int { return 0 }
func (s *kernelSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *kernelSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
// Read kernel version

View file

@ -53,8 +53,8 @@ func (s *localSource) Name() string { return Name }
// Priority method of the LabelSource interface
func (s *localSource) Priority() int { return 20 }
// Discover method of the LabelSource interface
func (s *localSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *localSource) GetLabels() (source.FeatureLabels, error) {
featuresFromHooks, err := getFeaturesFromHooks()
if err != nil {
klog.Error(err)

View file

@ -43,8 +43,8 @@ func (s *memorySource) Name() string { return Name }
// 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 *memorySource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *memorySource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
// Detect NUMA

View file

@ -11,8 +11,8 @@ type MockLabelSource struct {
mock.Mock
}
// Discover provides a mock function with given fields:
func (_m *MockLabelSource) Discover() (FeatureLabels, error) {
// GetLabels provides a mock function with given fields:
func (_m *MockLabelSource) GetLabels() (FeatureLabels, error) {
ret := _m.Called()
var r0 FeatureLabels

View file

@ -56,8 +56,8 @@ func (s *networkSource) Name() string { return Name }
// 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 *networkSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *networkSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
netInterfaces, err := ioutil.ReadDir(source.SysfsDir.Path(sysfsBaseDir))

View file

@ -75,8 +75,8 @@ func (s *pciSource) SetConfig(conf source.Config) {
// Priority method of the LabelSource interface
func (s *pciSource) Priority() int { return 0 }
// Discover features
func (s *pciSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *pciSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
// Construct a device label format, a sorted list of valid attributes

View file

@ -18,6 +18,8 @@ package source
import (
"fmt"
"sigs.k8s.io/node-feature-discovery/pkg/api/feature"
)
// Source is the base interface for all other source interfaces
@ -26,12 +28,23 @@ type Source interface {
Name() string
}
// FeatureSource is an interface for discovering node features
type FeatureSource interface {
Source
// Discover does feature discovery
Discover() error
// GetFeatures returns discovered features in raw form
GetFeatures() *feature.DomainFeatures
}
// LabelSource represents a source of node feature labels
type LabelSource interface {
Source
// Discover returns discovered feature labels
Discover() (FeatureLabels, error)
// GetLabels returns discovered feature labels
GetLabels() (FeatureLabels, error)
// Priority returns the priority of the source
Priority() int
@ -80,6 +93,25 @@ func Register(s Source) {
sources[s.Name()] = s
}
// GetFeatureSource returns a registered FeatureSource interface
func GetFeatureSource(name string) FeatureSource {
if s, ok := sources[name].(FeatureSource); ok {
return s
}
return nil
}
// GetAllFeatureSources returns all registered label sources
func GetAllFeatureSources() map[string]FeatureSource {
all := make(map[string]FeatureSource)
for k, v := range sources {
if s, ok := v.(FeatureSource); ok {
all[k] = s
}
}
return all
}
// GetLabelSource a registered label source
func GetLabelSource(name string) LabelSource {
if s, ok := sources[name].(LabelSource); ok {

View file

@ -17,6 +17,7 @@ limitations under the License.
package source_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@ -61,3 +62,19 @@ func TestConfigurableSources(t *testing.T) {
assert.Equalf(t, c, rc, "testing ConfigurableSource %q failed", n)
}
}
func TestFeatureSources(t *testing.T) {
sources := source.GetAllFeatureSources()
for n, s := range sources {
msg := fmt.Sprintf("testing FeatureSource %q failed", n)
assert.Equal(t, n, s.Name(), msg)
f := s.GetFeatures()
assert.NotNil(t, f, msg)
assert.Empty(t, (*f).Keys, msg)
assert.Empty(t, (*f).Values, msg)
assert.Empty(t, (*f).Instances, msg)
}
}

View file

@ -40,8 +40,8 @@ func (s *storageSource) Name() string { return Name }
// 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 *storageSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *storageSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
// Check if there is any non-rotational block devices attached to the node

View file

@ -48,7 +48,8 @@ func (s *systemSource) Name() string { return Name }
// Priority method of the LabelSource interface
func (s *systemSource) Priority() int { return 0 }
func (s *systemSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *systemSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
release, err := parseOSRelease()

View file

@ -78,8 +78,8 @@ func (s *usbSource) SetConfig(conf source.Config) {
// Priority method of the LabelSource interface
func (s *usbSource) Priority() int { return 0 }
// Discover features
func (s *usbSource) Discover() (source.FeatureLabels, error) {
// GetLabels method of the LabelSource interface
func (s *usbSource) GetLabels() (source.FeatureLabels, error) {
features := source.FeatureLabels{}
// Construct a device label format, a sorted list of valid attributes