Skip to content

Commit

Permalink
Label aggregation into profile
Browse files Browse the repository at this point in the history
* Additional 'profile' source for nfd-worker config
* Re-org some existing funcs to validate profile labels
  • Loading branch information
nolancon committed Feb 25, 2021
1 parent 14dc2b0 commit 4b04d27
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 35 deletions.
132 changes: 97 additions & 35 deletions pkg/nfd-worker/nfd-worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"sigs.k8s.io/node-feature-discovery/source/network"
"sigs.k8s.io/node-feature-discovery/source/panic_fake"
"sigs.k8s.io/node-feature-discovery/source/pci"
"sigs.k8s.io/node-feature-discovery/source/profile"
"sigs.k8s.io/node-feature-discovery/source/storage"
"sigs.k8s.io/node-feature-discovery/source/system"
"sigs.k8s.io/node-feature-discovery/source/usb"
Expand Down Expand Up @@ -140,9 +141,12 @@ func NewNfdWorker(args *Args) (NfdWorker, error) {
&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 needs to be the last source for feature discovery so that it is
// able to override labels from other sources
&local.Source{},
// profile needs to be the last source because it aggregates all previously
// discovered features
&profile.Source{},
},
testSources: []source.FeatureSource{
&fake.Source{},
Expand Down Expand Up @@ -246,6 +250,9 @@ func (w *nfdWorker) Run() error {
// Get the set of feature labels.
labels := createFeatureLabels(w.enabledSources, w.config.Core.LabelWhiteList.Regexp)

// Add profile labels to list of labels
labels = createProfileLabels(w.config.Sources["profile"], labels, w.config.Core.LabelWhiteList.Regexp)

// Update the node with the feature labels.
if w.client != nil {
err := advertiseFeatureLabels(w.client, labels)
Expand Down Expand Up @@ -509,6 +516,12 @@ func createFeatureLabels(sources []source.FeatureSource, labelWhiteList regexp.R

// Do feature discovery from all configured sources.
for _, source := range sources {
switch source.(type) {
case *profile.Source:
// Skip profile source
continue
}

labelsFromSource, err := getFeatureLabels(source, labelWhiteList)
if err != nil {
klog.Errorf("discovery failed for source %q: %v", source.Name(), err)
Expand All @@ -524,6 +537,45 @@ func createFeatureLabels(sources []source.FeatureSource, labelWhiteList regexp.R
return labels
}

// createProfileLabels checks the discovered labels against the config profile features and
// adds overall profile labels where necessary.
func createProfileLabels(profileCfg source.Config, labels Labels, labelWhiteList regexp.Regexp) Labels {
profileObjects, ok := profileCfg.(*profile.Config)
if !ok {
return labels
}
for _, profileObject := range *profileObjects {
if isSubset(Labels(profileObject.Features), labels) {
labelValue := "true"
label := getValidLabel("profile-", profileObject.Name, labelValue, labelWhiteList)
if label != "" {
klog.Infof("INFO: Adding profile label %v=%v to labels", label, labelValue)
labels[label] = labelValue

}
}
}
return labels
}

// isSubset returns true if all labels in labelsSubset are present in labelsSet.
func isSubset(labelsSubset, labelsSet Labels) bool {
if len(labelsSubset) == 0 {
return true
}

for k, v := range labelsSubset {
value, ok := labelsSet[k]
if !ok {
return false
}
if value != v {
return false
}
}
return true
}

// getFeatureLabels returns node labels for features discovered by the
// supplied source.
func getFeatureLabels(source source.FeatureSource, labelWhiteList regexp.Regexp) (labels Labels, err error) {
Expand All @@ -548,46 +600,56 @@ func getFeatureLabels(source source.FeatureSource, labelWhiteList regexp.Regexp)
prefix = ""
}

for k, v := range features {
// Split label name into namespace and name compoents. Use dummy 'ns'
// default namespace because there is no function to validate just
// the name part
split := strings.SplitN(k, "/", 2)
for key, value := range features {
labelValue := fmt.Sprintf("%v", value)
label := getValidLabel(prefix, key, labelValue, labelWhiteList)
if label == "" {
continue
}

label := prefix + split[0]
nameForValidation := "ns/" + label
nameForWhiteListing := label
labels[label] = labelValue
}
return labels, nil
}

if len(split) == 2 {
label = k
nameForValidation = label
nameForWhiteListing = split[1]
}
// getValidLabel returns a label after checking validity of label name & value and ensuring label
// exists in whitelist.
func getValidLabel(prefix, labelKey, labelValue string, labelWhiteList regexp.Regexp) string {
// Split label name into namespace and name components. Use dummy 'ns'
// default namespace because there is no function to validate just
// the name part
split := strings.SplitN(labelKey, "/", 2)

// Validate label name.
errs := validation.IsQualifiedName(nameForValidation)
if len(errs) > 0 {
klog.Warningf("Ignoring invalid feature name '%s': %s", label, errs)
continue
}
label := prefix + split[0]
nameForValidation := "ns/" + label
nameForWhiteListing := label

value := fmt.Sprintf("%v", v)
// Validate label value
errs = validation.IsValidLabelValue(value)
if len(errs) > 0 {
klog.Warningf("Ignoring invalid feature value %s=%s: %s", label, value, errs)
continue
}
if len(split) == 2 {
label = labelKey
nameForValidation = label
nameForWhiteListing = split[1]
}
// Validate label name.
errs := validation.IsQualifiedName(nameForValidation)
if len(errs) > 0 {
klog.Warningf("Ignoring invalid feature name '%s': %s", label, errs)
return ""
}

// Skip if label doesn't match labelWhiteList
if !labelWhiteList.MatchString(nameForWhiteListing) {
klog.Infof("%q does not match the whitelist (%s) and will not be published.", nameForWhiteListing, labelWhiteList.String())
continue
}
// Validate label value
errs = validation.IsValidLabelValue(labelValue)
if len(errs) > 0 {
klog.Warningf("Ignoring invalid feature value %s=%s: %s", label, labelValue, errs)
return ""
}

labels[label] = value
// Skip if label doesn't match labelWhiteList
if !labelWhiteList.MatchString(nameForWhiteListing) {
klog.Warningf("%q does not match the whitelist (%s) and will not be published.", nameForWhiteListing, labelWhiteList.String())
return ""
}
return labels, nil
return label

}

// advertiseFeatureLabels advertises the feature labels to a Kubernetes node
Expand Down
65 changes: 65 additions & 0 deletions source/profile/profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
Copyright 2020 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 profile

import (
"log"

"sigs.k8s.io/node-feature-discovery/source"
)

type Profile struct {
Name string `json:"name"`
Features map[string]string `json:"features"`
}

type Config []Profile

// newDefaultConfig returns a new config with pre-populated defaults
func newDefaultConfig() *Config {
return &Config{}
}

// Implements FeatureSource Interface
type Source struct {
Config *Config
}

// Return name of the feature source
func (s Source) Name() string { return "profile" }

// 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{}
return features, nil
}

0 comments on commit 4b04d27

Please sign in to comment.