From a8092927fc0f33a096aed59958c4e75e64aa45d4 Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Thu, 30 Nov 2023 10:47:03 +0200 Subject: [PATCH] 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. --- source/custom/custom.go | 38 ++++++++++++++------ source/custom/directory_features.go | 14 ++++---- source/custom/static_features.go | 54 +++++++++++++---------------- 3 files changed, 61 insertions(+), 45 deletions(-) diff --git a/source/custom/custom.go b/source/custom/custom.go index f3782e372..17ef9fff9 100644 --- a/source/custom/custom.go +++ b/source/custom/custom.go @@ -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) } diff --git a/source/custom/directory_features.go b/source/custom/directory_features.go index 951c0e791..5fc92e845 100644 --- a/source/custom/directory_features.go +++ b/source/custom/directory_features.go @@ -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 } diff --git a/source/custom/static_features.go b/source/custom/static_features.go index 2c2d1ff05..45e77cd6c 100644 --- a/source/custom/static_features.go +++ b/source/custom/static_features.go @@ -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, }, }, },