1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2024-12-14 11:57:51 +00:00

source/custom: use internal api for config parsing

Change the custom feature source of nfd-worker to use the newly added
internal config API for its own configuration. It now uses the internal
types for json/yaml unmarshalling but converts them to external
nfdv1alpha1 API to do the actual rule matching as the internal API does
not duplicate that functionality.
This commit is contained in:
Markus Lehtonen 2023-11-30 10:47:03 +02:00
parent 185b406ee7
commit a8092927fc
3 changed files with 61 additions and 45 deletions

View file

@ -18,22 +18,21 @@ package custom
import (
"fmt"
"os"
"k8s.io/klog/v2"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
"sigs.k8s.io/node-feature-discovery/source"
api "sigs.k8s.io/node-feature-discovery/source/custom/api"
)
// Name of this feature source
const Name = "custom"
type CustomRule struct {
nfdv1alpha1.Rule
}
type config []CustomRule
// The config files use the internal API type.
type config []api.Rule
// newDefaultConfig returns a new config with pre-populated defaults
func newDefaultConfig() *config {
@ -43,13 +42,19 @@ func newDefaultConfig() *config {
// customSource implements the LabelSource and ConfigurableSource interfaces.
type customSource struct {
config *config
// The rules are stored in the NFD API format that is a superset of our
// internal API and provides the functions for rule matching.
rules []nfdv1alpha1.Rule
}
// Singleton source instance
var (
src = customSource{config: newDefaultConfig()}
_ source.LabelSource = &src
_ source.ConfigurableSource = &src
src = customSource{
config: &config{},
rules: []nfdv1alpha1.Rule{},
}
_ source.LabelSource = &src
_ source.ConfigurableSource = &src
)
// Name returns the name of the feature source
@ -65,6 +70,8 @@ func (s *customSource) GetConfig() source.Config { return s.config }
func (s *customSource) SetConfig(conf source.Config) {
switch v := conf.(type) {
case *config:
r := []api.Rule(*v)
s.rules = convertInternalRulesToNfdApi(&r)
s.config = v
default:
panic(fmt.Sprintf("invalid config type: %T", conf))
@ -80,8 +87,8 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) {
features := source.GetAllFeatures()
labels := source.FeatureLabels{}
allFeatureConfig := append(getStaticFeatureConfig(), *s.config...)
allFeatureConfig = append(allFeatureConfig, getDirectoryFeatureConfig()...)
allFeatureConfig := append(getStaticRules(), s.rules...)
allFeatureConfig = append(allFeatureConfig, getDropinDirRules()...)
klog.V(2).InfoS("resolving custom features", "configuration", utils.DelayedDumper(allFeatureConfig))
// Iterate over features
for _, rule := range allFeatureConfig {
@ -102,6 +109,17 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) {
return labels, nil
}
func convertInternalRulesToNfdApi(in *[]api.Rule) []nfdv1alpha1.Rule {
out := make([]nfdv1alpha1.Rule, len(*in))
for i := range *in {
if err := api.ConvertRuleToV1alpha1(&(*in)[i], &out[i]); err != nil {
klog.ErrorS(err, "FATAL: API conversion failed")
os.Exit(255)
}
}
return out
}
func init() {
source.Register(&src)
}

View file

@ -22,22 +22,24 @@ import (
"strings"
"k8s.io/klog/v2"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
api "sigs.k8s.io/node-feature-discovery/source/custom/api"
"sigs.k8s.io/yaml"
)
// Directory stores the full path for the custom sources folder
const Directory = "/etc/kubernetes/node-feature-discovery/custom.d"
// getDirectoryFeatureConfig returns features configured in the "/etc/kubernetes/node-feature-discovery/custom.d"
// getDropinDirRules returns features configured in the "/etc/kubernetes/node-feature-discovery/custom.d"
// host directory and its 1st level subdirectories, which can be populated e.g. by ConfigMaps
func getDirectoryFeatureConfig() []CustomRule {
func getDropinDirRules() []nfdv1alpha1.Rule {
features := readDir(Directory, true)
klog.V(3).InfoS("all custom feature specs from config dir", "featureSpecs", features)
return features
}
func readDir(dirName string, recursive bool) []CustomRule {
features := make([]CustomRule, 0)
func readDir(dirName string, recursive bool) []nfdv1alpha1.Rule {
features := make([]nfdv1alpha1.Rule, 0)
klog.V(4).InfoS("reading directory", "path", dirName)
files, err := os.ReadDir(dirName)
@ -74,14 +76,14 @@ func readDir(dirName string, recursive bool) []CustomRule {
continue
}
config := &[]CustomRule{}
config := &[]api.Rule{}
err = yaml.UnmarshalStrict(bytes, config)
if err != nil {
klog.ErrorS(err, "could not parse file", "path", fileName)
continue
}
features = append(features, *config...)
features = append(features, convertInternalRulesToNfdApi(config)...)
}
return features
}

View file

@ -20,40 +20,36 @@ import (
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
)
// getStaticFeatures returns statically configured custom features to discover
// getStaticRules returns statically configured custom features to discover
// e.g RMDA related features. NFD configuration file may extend these custom features by adding rules.
func getStaticFeatureConfig() []CustomRule {
return []CustomRule{
{
nfdv1alpha1.Rule{
Name: "RDMA capable static rule",
Labels: map[string]string{"rdma.capable": "true"},
MatchFeatures: nfdv1alpha1.FeatureMatcher{
nfdv1alpha1.FeatureMatcherTerm{
Feature: "pci.device",
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{
"vendor": &nfdv1alpha1.MatchExpression{
Op: nfdv1alpha1.MatchIn,
Value: nfdv1alpha1.MatchValue{"15b3"}},
},
func getStaticRules() []nfdv1alpha1.Rule {
return []nfdv1alpha1.Rule{
nfdv1alpha1.Rule{
Name: "RDMA capable static rule",
Labels: map[string]string{"rdma.capable": "true"},
MatchFeatures: nfdv1alpha1.FeatureMatcher{
nfdv1alpha1.FeatureMatcherTerm{
Feature: "pci.device",
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{
"vendor": &nfdv1alpha1.MatchExpression{
Op: nfdv1alpha1.MatchIn,
Value: nfdv1alpha1.MatchValue{"15b3"}},
},
},
},
},
{
nfdv1alpha1.Rule{
Name: "RDMA available static rule",
Labels: map[string]string{"rdma.available": "true"},
MatchFeatures: nfdv1alpha1.FeatureMatcher{
nfdv1alpha1.FeatureMatcherTerm{
Feature: "kernel.loadedmodule",
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{
"ib_uverbs": &nfdv1alpha1.MatchExpression{
Op: nfdv1alpha1.MatchExists,
},
"rdma_ucm": &nfdv1alpha1.MatchExpression{
Op: nfdv1alpha1.MatchExists,
},
nfdv1alpha1.Rule{
Name: "RDMA available static rule",
Labels: map[string]string{"rdma.available": "true"},
MatchFeatures: nfdv1alpha1.FeatureMatcher{
nfdv1alpha1.FeatureMatcherTerm{
Feature: "kernel.loadedmodule",
MatchExpressions: &nfdv1alpha1.MatchExpressionSet{
"ib_uverbs": &nfdv1alpha1.MatchExpression{
Op: nfdv1alpha1.MatchExists,
},
"rdma_ucm": &nfdv1alpha1.MatchExpression{
Op: nfdv1alpha1.MatchExists,
},
},
},