From 56c2ab3d5807df12d7f64c0f2c37cab2ea4dc063 Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Thu, 21 Jun 2018 19:02:30 +0300 Subject: [PATCH] Support for non-binary labels Make it possible to advertise also other than simple 'true' values for feature labels. --- README.md | 1 + main.go | 4 ++-- main_test.go | 6 ++++-- mockfeaturesource.go | 13 ++++++++----- source/cpuid/cpuid.go | 13 ++++++++++--- source/fake/fake.go | 12 ++++++++---- source/iommu/iommu.go | 8 +++++--- source/memory/memory.go | 8 +++++--- source/network/network.go | 10 ++++++---- source/panic_fake/fake_panic.go | 4 +++- source/pci/pci.go | 13 +++++-------- source/pstate/pstate.go | 8 +++++--- source/rdt/rdt.go | 17 +++++++++-------- source/selinux/selinux.go | 10 ++++++---- source/source.go | 18 +++++++++++++++++- source/storage/storage.go | 8 +++++--- 16 files changed, 99 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index f83ec903d..95ce83d5d 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ The published node labels encode a few pieces of information: - The source for each label (e.g. `cpuid`). - The name of the discovered feature as it appears in the underlying source, (e.g. `AESNI` from cpuid). +- The value of the discovered feature. Feature label names adhere to the following pattern: ``` diff --git a/main.go b/main.go index 2500e8928..41c98bf1a 100644 --- a/main.go +++ b/main.go @@ -332,8 +332,8 @@ func getFeatureLabels(source source.FeatureSource) (labels Labels, err error) { if err != nil { return nil, err } - for _, f := range features { - labels[fmt.Sprintf("%s-%s-%s", prefix, source.Name(), f)] = "true" + for k := range features { + labels[fmt.Sprintf("%s-%s-%s", prefix, source.Name(), k)] = fmt.Sprintf("%v", features[k]) } return labels, nil } diff --git a/main_test.go b/main_test.go index 9d179533a..063f2704a 100644 --- a/main_test.go +++ b/main_test.go @@ -22,9 +22,11 @@ func TestDiscoveryWithMockSources(t *testing.T) { Convey("When I discover features from fake source and update the node using fake client", t, func() { mockFeatureSource := new(MockFeatureSource) fakeFeatureSourceName := string("testSource") - fakeFeatures := []string{"testfeature1", "testfeature2", "testfeature3"} + fakeFeatureNames := []string{"testfeature1", "testfeature2", "testfeature3"} + fakeFeatures := source.Features{} fakeFeatureLabels := Labels{} - for _, f := range fakeFeatures { + for _, f := range fakeFeatureNames { + fakeFeatures[f] = true fakeFeatureLabels[fmt.Sprintf("%s-testSource-%s", prefix, f)] = "true" } fakeFeatureSource := source.FeatureSource(mockFeatureSource) diff --git a/mockfeaturesource.go b/mockfeaturesource.go index 765266398..042ceb317 100644 --- a/mockfeaturesource.go +++ b/mockfeaturesource.go @@ -1,6 +1,9 @@ package main -import "github.com/stretchr/testify/mock" +import ( + "github.com/kubernetes-incubator/node-feature-discovery/source" + "github.com/stretchr/testify/mock" +) type MockFeatureSource struct { mock.Mock @@ -23,15 +26,15 @@ func (_m *MockFeatureSource) Name() string { // Discover provides a mock function with no input arguments // and []string and error as the return values -func (_m *MockFeatureSource) Discover() ([]string, error) { +func (_m *MockFeatureSource) Discover() (source.Features, error) { ret := _m.Called() - var r0 []string - if rf, ok := ret.Get(0).(func() []string); ok { + var r0 source.Features + if rf, ok := ret.Get(0).(func() source.Features); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) + r0 = ret.Get(0).(source.Features) } } diff --git a/source/cpuid/cpuid.go b/source/cpuid/cpuid.go index 6f290192a..eb6504790 100644 --- a/source/cpuid/cpuid.go +++ b/source/cpuid/cpuid.go @@ -16,7 +16,10 @@ limitations under the License. package cpuid -import "github.com/klauspost/cpuid" +import ( + "github.com/klauspost/cpuid" + "github.com/kubernetes-incubator/node-feature-discovery/source" +) // Source implements FeatureSource. type Source struct{} @@ -25,7 +28,11 @@ type Source struct{} func (s Source) Name() string { return "cpuid" } // Discover returns feature names for all the supported CPU features. -func (s Source) Discover() ([]string, error) { +func (s Source) Discover() (source.Features, error) { // Get the cpu features as strings - return cpuid.CPU.Features.Strings(), nil + features := source.Features{} + for _, f := range cpuid.CPU.Features.Strings() { + features[f] = true + } + return features, nil } diff --git a/source/fake/fake.go b/source/fake/fake.go index ee6056dbb..bbfddcca9 100644 --- a/source/fake/fake.go +++ b/source/fake/fake.go @@ -16,6 +16,8 @@ limitations under the License. package fake +import "github.com/kubernetes-incubator/node-feature-discovery/source" + // Source implements FeatureSource. type Source struct{} @@ -23,11 +25,13 @@ type Source struct{} func (s Source) Name() string { return "fake" } // Discover returns feature names for some fake features. -func (s Source) Discover() ([]string, error) { - features := []string{} - +func (s Source) Discover() (source.Features, error) { // Adding three fake features. - features = append(features, "fakefeature1", "fakefeature2", "fakefeature3") + features := source.Features{ + "fakefeature1": true, + "fakefeature2": true, + "fakefeature3": true, + } return features, nil } diff --git a/source/iommu/iommu.go b/source/iommu/iommu.go index a23937412..16a2e9925 100644 --- a/source/iommu/iommu.go +++ b/source/iommu/iommu.go @@ -19,6 +19,8 @@ package iommu import ( "fmt" "io/ioutil" + + "github.com/kubernetes-incubator/node-feature-discovery/source" ) // Implement FeatureSource interface @@ -26,8 +28,8 @@ type Source struct{} func (s Source) Name() string { return "iommu" } -func (s Source) Discover() ([]string, error) { - features := []string{} +func (s Source) Discover() (source.Features, error) { + features := source.Features{} // Check if any iommu devices are available devices, err := ioutil.ReadDir("/sys/class/iommu/") @@ -36,7 +38,7 @@ func (s Source) Discover() ([]string, error) { } if len(devices) > 0 { - features = append(features, "enabled") + features["enabled"] = true } return features, nil diff --git a/source/memory/memory.go b/source/memory/memory.go index c4122f29a..490315be1 100644 --- a/source/memory/memory.go +++ b/source/memory/memory.go @@ -20,6 +20,8 @@ import ( "fmt" "io/ioutil" "strings" + + "github.com/kubernetes-incubator/node-feature-discovery/source" ) // Source implements FeatureSource. @@ -29,8 +31,8 @@ type Source struct{} func (s Source) Name() string { return "memory" } // Discover returns feature names for memory: numa if more than one memory node is present. -func (s Source) Discover() ([]string, error) { - features := []string{} +func (s Source) Discover() (source.Features, error) { + features := source.Features{} // Find out how many nodes are online // Multiple nodes is a sign of NUMA @@ -44,7 +46,7 @@ func (s Source) Discover() ([]string, error) { // presence of newline requires TrimSpace if strings.TrimSpace(string(bytes)) != "0" { // more than one node means NUMA - features = append(features, "numa") + features["numa"] = true } return features, nil diff --git a/source/network/network.go b/source/network/network.go index 3374927b2..30c25aea3 100644 --- a/source/network/network.go +++ b/source/network/network.go @@ -24,6 +24,8 @@ import ( "net" "strconv" "strings" + + "github.com/kubernetes-incubator/node-feature-discovery/source" ) // Source implements FeatureSource. @@ -33,8 +35,8 @@ type Source struct{} func (s Source) Name() string { return "network" } // 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() ([]string, error) { - features := []string{} +func (s Source) Discover() (source.Features, error) { + features := source.Features{} netInterfaces, err := net.Interfaces() if err != nil { return nil, fmt.Errorf("can't obtain the network interfaces details: %s", err.Error()) @@ -57,7 +59,7 @@ func (s Source) Discover() ([]string, error) { if t > 0 { glog.Infof("SR-IOV capability is detected on the network interface: %s", netInterface.Name) glog.Infof("%d maximum supported number of virtual functions on network interface: %s", t, netInterface.Name) - features = append(features, "sriov.capable") + features["sriov.capable"] = true numVfsPath := "/sys/class/net/" + netInterface.Name + "/device/sriov_numvfs" numBytes, err := ioutil.ReadFile(numVfsPath) if err != nil { @@ -72,7 +74,7 @@ func (s Source) Discover() ([]string, error) { } if n > 0 { glog.Infof("%d virtual functions configured on network interface: %s", n, netInterface.Name) - features = append(features, "sriov.configured") + features["sriov.configured"] = true break } else if n == 0 { glog.Errorf("SR-IOV not configured on network interface: %s", netInterface.Name) diff --git a/source/panic_fake/fake_panic.go b/source/panic_fake/fake_panic.go index d410cc9e7..4704f3bb5 100644 --- a/source/panic_fake/fake_panic.go +++ b/source/panic_fake/fake_panic.go @@ -16,6 +16,8 @@ limitations under the License. package panic_fake +import "github.com/kubernetes-incubator/node-feature-discovery/source" + // Source implements FeatureSource. type Source struct{} @@ -23,6 +25,6 @@ type Source struct{} func (s Source) Name() string { return "panic_fake" } // Discover calls panic(). -func (s Source) Discover() ([]string, error) { +func (s Source) Discover() (source.Features, error) { panic("fake panic error") } diff --git a/source/pci/pci.go b/source/pci/pci.go index bd1e4ad4a..b05771866 100644 --- a/source/pci/pci.go +++ b/source/pci/pci.go @@ -23,6 +23,8 @@ import ( "os" "path" "strings" + + "github.com/kubernetes-incubator/node-feature-discovery/source" ) type pciDeviceInfo map[string]string @@ -48,8 +50,8 @@ type Source struct{} func (s Source) Name() string { return "pci" } // Discover features -func (s Source) Discover() ([]string, error) { - features := map[string]bool{} +func (s Source) Discover() (source.Features, error) { + features := source.Features{} devs, err := detectPci() if err != nil { @@ -100,12 +102,7 @@ func (s Source) Discover() ([]string, error) { } } - feature_list := []string{} - for feature := range features { - feature_list = append(feature_list, feature) - } - - return feature_list, nil + return features, nil } // Read information of one PCI device diff --git a/source/pstate/pstate.go b/source/pstate/pstate.go index bc5c69a9a..0d1fd2821 100644 --- a/source/pstate/pstate.go +++ b/source/pstate/pstate.go @@ -20,6 +20,8 @@ import ( "fmt" "io/ioutil" "runtime" + + "github.com/kubernetes-incubator/node-feature-discovery/source" ) // Source implements FeatureSource. @@ -29,8 +31,8 @@ type Source struct{} func (s Source) Name() string { return "pstate" } // Discover returns feature names for p-state related features such as turbo boost. -func (s Source) Discover() ([]string, error) { - features := []string{} +func (s Source) Discover() (source.Features, error) { + features := source.Features{} // On Arm platform, the frequency boost mechanism is software-based. // So skip pstate detection on Arm. @@ -46,7 +48,7 @@ func (s Source) Discover() ([]string, error) { } if bytes[0] == byte('0') { // Turbo boost is enabled. - features = append(features, "turbo") + features["turbo"] = true } return features, nil diff --git a/source/rdt/rdt.go b/source/rdt/rdt.go index 4e9b8cf35..f9805bd9d 100644 --- a/source/rdt/rdt.go +++ b/source/rdt/rdt.go @@ -20,6 +20,7 @@ import ( "os/exec" "github.com/golang/glog" + "github.com/kubernetes-incubator/node-feature-discovery/source" ) // Source implements FeatureSource. @@ -29,15 +30,15 @@ type Source struct{} func (s Source) Name() string { return "rdt" } // Discover returns feature names for CMT and CAT if supported. -func (s Source) Discover() ([]string, error) { - features := []string{} +func (s Source) Discover() (source.Features, error) { + features := source.Features{} cmd := exec.Command("bash", "-c", "mon-discovery") if err := cmd.Run(); err != nil { glog.Errorf("support for RDT monitoring was not detected: %v", err) } else { // RDT monitoring detected. - features = append(features, "RDTMON") + features["RDTMON"] = true } cmd = exec.Command("bash", "-c", "mon-cmt-discovery") @@ -45,7 +46,7 @@ func (s Source) Discover() ([]string, error) { glog.Errorf("support for RDT CMT monitoring was not detected: %v", err) } else { // RDT CMT monitoring detected. - features = append(features, "RDTCMT") + features["RDTCMT"] = true } cmd = exec.Command("bash", "-c", "mon-mbm-discovery") @@ -53,7 +54,7 @@ func (s Source) Discover() ([]string, error) { glog.Errorf("support for RDT MBM monitoring was not detected: %v", err) } else { // RDT MBM monitoring detected. - features = append(features, "RDTMBM") + features["RDTMBM"] = true } cmd = exec.Command("bash", "-c", "l3-alloc-discovery") @@ -61,7 +62,7 @@ func (s Source) Discover() ([]string, error) { glog.Errorf("support for RDT L3 allocation was not detected: %v", err) } else { // RDT L3 cache allocation detected. - features = append(features, "RDTL3CA") + features["RDTL3CA"] = true } cmd = exec.Command("bash", "-c", "l2-alloc-discovery") @@ -69,7 +70,7 @@ func (s Source) Discover() ([]string, error) { glog.Errorf("support for RDT L2 allocation was not detected: %v", err) } else { // RDT L2 cache allocation detected. - features = append(features, "RDTL2CA") + features["RDTL2CA"] = true } cmd = exec.Command("bash", "-c", "mem-bandwidth-alloc-discovery") @@ -77,7 +78,7 @@ func (s Source) Discover() ([]string, error) { glog.Errorf("support for RDT Memory bandwidth allocation was not detected: %v", err) } else { // RDT Memory bandwidth allocation detected. - features = append(features, "RDTMBA") + features["RDTMBA"] = true } return features, nil diff --git a/source/selinux/selinux.go b/source/selinux/selinux.go index 86a8c9026..9a0ad8efb 100644 --- a/source/selinux/selinux.go +++ b/source/selinux/selinux.go @@ -17,23 +17,25 @@ limitations under the License. package selinux import ( - "io/ioutil" "fmt" + "io/ioutil" + + "github.com/kubernetes-incubator/node-feature-discovery/source" ) type Source struct{} func (s Source) Name() string { return "selinux" } -func (s Source) Discover() ([]string, error) { - features := []string{} +func (s Source) Discover() (source.Features, error) { + features := source.Features{} status, err := ioutil.ReadFile("/host-sys/fs/selinux/enforce") if err != nil { return nil, fmt.Errorf("Failed to detect the status of selinux, please check if the system supports selinux and make sure /sys on the host is mounted into the container: %s", err.Error()) } if status[0] == byte('1') { // selinux is enabled. - features = append(features, "enabled") + features["enabled"] = true } return features, nil } diff --git a/source/source.go b/source/source.go index 4066d7623..be0ae486e 100644 --- a/source/source.go +++ b/source/source.go @@ -16,11 +16,27 @@ limitations under the License. package source +// Value of a feature +type FeatureValue interface { +} + +// Boolean feature value +type BoolFeatureValue bool + +func (b BoolFeatureValue) String() string { + if b == true { + return "true" + } + return "false" +} + +type Features map[string]FeatureValue + // FeatureSource represents a source of a discovered node feature. type FeatureSource interface { // Name returns a friendly name for this source of node feature. Name() string // Discover returns discovered features for this node. - Discover() ([]string, error) + Discover() (Features, error) } diff --git a/source/storage/storage.go b/source/storage/storage.go index b25ad5486..6e9a095a1 100644 --- a/source/storage/storage.go +++ b/source/storage/storage.go @@ -19,6 +19,8 @@ package storage import ( "fmt" "io/ioutil" + + "github.com/kubernetes-incubator/node-feature-discovery/source" ) // Source implements FeatureSource. @@ -28,8 +30,8 @@ type Source struct{} func (s Source) Name() string { return "storage" } // Discover returns feature names for storage: nonrotationaldisk if any SSD drive present. -func (s Source) Discover() ([]string, error) { - features := []string{} +func (s Source) Discover() (source.Features, error) { + features := source.Features{} // Check if there is any non-rotational block devices attached to the node blockdevices, err := ioutil.ReadDir("/sys/block/") @@ -42,7 +44,7 @@ func (s Source) Discover() ([]string, error) { } if bytes[0] == byte('0') { // Non-rotational storage is present, add label. - features = append(features, "nonrotationaldisk") + features["nonrotationaldisk"] = true break } }