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

apis/nfd: flatten the structure of features data type

Flatten the data structure that stores features, dropping the "domain"
level from the data model. That extra level of hierarchy brought little
benefit but just caused some extra complexity, instead. The new
structure nicely matches what we have in the NodeFeatureRule object (the
matchFeatures field of uses the same flat structure with the "feature"
field having a value <domain>.<feature>, e.g. "kernel.version").

This is pre-work for introducing a new "node feature" CRD that contains
the raw feature data. It makes the life of both users and developers
easier when both CRDs, plus our internal code, handle feature data in a
similar flat structure.
This commit is contained in:
Markus Lehtonen 2022-07-04 14:05:58 +03:00
parent cab617c42e
commit b907d07d7e
22 changed files with 221 additions and 208 deletions

View file

@ -16,10 +16,10 @@ limitations under the License.
package v1alpha1 package v1alpha1
// NewDomainFeatures creates a new instance of Features, initializing specified // NewFeatures creates a new instance of Features, initializing all feature
// features to empty values // types (flags, attributes and instances) to empty values.
func NewDomainFeatures() *DomainFeatures { func NewFeatures() *Features {
return &DomainFeatures{ return &Features{
Flags: make(map[string]FlagFeatureSet), Flags: make(map[string]FlagFeatureSet),
Attributes: make(map[string]AttributeFeatureSet), Attributes: make(map[string]AttributeFeatureSet),
Instances: make(map[string]InstanceFeatureSet)} Instances: make(map[string]InstanceFeatureSet)}
@ -56,16 +56,29 @@ func NewInstanceFeature(attrs map[string]string) *InstanceFeature {
} }
// InsertAttributeFeatures inserts new values into a specific feature. // InsertAttributeFeatures inserts new values into a specific feature.
func InsertAttributeFeatures(f Features, domain, feature string, values map[string]string) { func (f *Features) InsertAttributeFeatures(domain, feature string, values map[string]string) {
if _, ok := f[domain]; !ok { key := domain + "." + feature
f[domain] = NewDomainFeatures() if _, ok := f.Attributes[key]; !ok {
} f.Attributes[key] = NewAttributeFeatures(values)
if _, ok := f[domain].Attributes[feature]; !ok {
f[domain].Attributes[feature] = NewAttributeFeatures(values)
return return
} }
for k, v := range values { for k, v := range values {
f[domain].Attributes[feature].Elements[k] = v f.Attributes[key].Elements[k] = v
} }
} }
// Exists returns a non-empty string if a feature exists. The return value is
// the type of the feautre, i.e. "flag", "attribute" or "instance".
func (f *Features) Exists(name string) string {
if _, ok := f.Flags[name]; ok {
return "flag"
}
if _, ok := f.Attributes[name]; ok {
return "attribute"
}
if _, ok := f.Instances[name]; ok {
return "instance"
}
return ""
}

View file

@ -71,15 +71,15 @@ func (m *AttributeFeatureSet) XXX_DiscardUnknown() {
var xxx_messageInfo_AttributeFeatureSet proto.InternalMessageInfo var xxx_messageInfo_AttributeFeatureSet proto.InternalMessageInfo
func (m *DomainFeatures) Reset() { *m = DomainFeatures{} } func (m *Features) Reset() { *m = Features{} }
func (*DomainFeatures) ProtoMessage() {} func (*Features) ProtoMessage() {}
func (*DomainFeatures) Descriptor() ([]byte, []int) { func (*Features) Descriptor() ([]byte, []int) {
return fileDescriptor_6f67d44e41cfe439, []int{1} return fileDescriptor_6f67d44e41cfe439, []int{1}
} }
func (m *DomainFeatures) XXX_Unmarshal(b []byte) error { func (m *Features) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
} }
func (m *DomainFeatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *Features) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
b = b[:cap(b)] b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b) n, err := m.MarshalToSizedBuffer(b)
if err != nil { if err != nil {
@ -87,13 +87,13 @@ func (m *DomainFeatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, erro
} }
return b[:n], nil return b[:n], nil
} }
func (m *DomainFeatures) XXX_Merge(src proto.Message) { func (m *Features) XXX_Merge(src proto.Message) {
xxx_messageInfo_DomainFeatures.Merge(m, src) xxx_messageInfo_DomainFeatures.Merge(m, src)
} }
func (m *DomainFeatures) XXX_Size() int { func (m *Features) XXX_Size() int {
return m.Size() return m.Size()
} }
func (m *DomainFeatures) XXX_DiscardUnknown() { func (m *Features) XXX_DiscardUnknown() {
xxx_messageInfo_DomainFeatures.DiscardUnknown(m) xxx_messageInfo_DomainFeatures.DiscardUnknown(m)
} }
@ -214,7 +214,7 @@ var xxx_messageInfo_Nil proto.InternalMessageInfo
func init() { func init() {
proto.RegisterType((*AttributeFeatureSet)(nil), "v1alpha1.AttributeFeatureSet") proto.RegisterType((*AttributeFeatureSet)(nil), "v1alpha1.AttributeFeatureSet")
proto.RegisterMapType((map[string]string)(nil), "v1alpha1.AttributeFeatureSet.ElementsEntry") proto.RegisterMapType((map[string]string)(nil), "v1alpha1.AttributeFeatureSet.ElementsEntry")
proto.RegisterType((*DomainFeatures)(nil), "v1alpha1.DomainFeatures") proto.RegisterType((*Features)(nil), "v1alpha1.DomainFeatures")
proto.RegisterMapType((map[string]FlagFeatureSet)(nil), "v1alpha1.DomainFeatures.FlagsEntry") proto.RegisterMapType((map[string]FlagFeatureSet)(nil), "v1alpha1.DomainFeatures.FlagsEntry")
proto.RegisterMapType((map[string]InstanceFeatureSet)(nil), "v1alpha1.DomainFeatures.InstancesEntry") proto.RegisterMapType((map[string]InstanceFeatureSet)(nil), "v1alpha1.DomainFeatures.InstancesEntry")
proto.RegisterMapType((map[string]AttributeFeatureSet)(nil), "v1alpha1.DomainFeatures.VattributesEntry") proto.RegisterMapType((map[string]AttributeFeatureSet)(nil), "v1alpha1.DomainFeatures.VattributesEntry")
@ -316,7 +316,7 @@ func (m *AttributeFeatureSet) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *DomainFeatures) Marshal() (dAtA []byte, err error) { func (m *Features) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size]) n, err := m.MarshalToSizedBuffer(dAtA[:size])
@ -326,12 +326,12 @@ func (m *DomainFeatures) Marshal() (dAtA []byte, err error) {
return dAtA[:n], nil return dAtA[:n], nil
} }
func (m *DomainFeatures) MarshalTo(dAtA []byte) (int, error) { func (m *Features) MarshalTo(dAtA []byte) (int, error) {
size := m.Size() size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size]) return m.MarshalToSizedBuffer(dAtA[:size])
} }
func (m *DomainFeatures) MarshalToSizedBuffer(dAtA []byte) (int, error) { func (m *Features) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA) i := len(dAtA)
_ = i _ = i
var l int var l int
@ -613,7 +613,7 @@ func (m *AttributeFeatureSet) Size() (n int) {
return n return n
} }
func (m *DomainFeatures) Size() (n int) { func (m *Features) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
} }
@ -734,7 +734,7 @@ func (this *AttributeFeatureSet) String() string {
}, "") }, "")
return s return s
} }
func (this *DomainFeatures) String() string { func (this *Features) String() string {
if this == nil { if this == nil {
return "nil" return "nil"
} }
@ -1025,7 +1025,7 @@ func (m *AttributeFeatureSet) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *DomainFeatures) Unmarshal(dAtA []byte) error { func (m *Features) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
for iNdEx < l { for iNdEx < l {

View file

@ -35,7 +35,7 @@ type RuleOutput struct {
} }
// Execute the rule against a set of input features. // Execute the rule against a set of input features.
func (r *Rule) Execute(features Features) (RuleOutput, error) { func (r *Rule) Execute(features *Features) (RuleOutput, error) {
labels := make(map[string]string) labels := make(map[string]string)
vars := make(map[string]string) vars := make(map[string]string)
@ -145,55 +145,39 @@ func (r *Rule) executeVarsTemplate(in matchedFeatures, out map[string]string) er
return nil return nil
} }
type matchedFeatures map[string]domainMatchedFeatures type matchedFeatures map[string]interface{}
type domainMatchedFeatures map[string]interface{} func (e *MatchAnyElem) match(features *Features) (bool, matchedFeatures, error) {
func (e *MatchAnyElem) match(features map[string]*DomainFeatures) (bool, matchedFeatures, error) {
return e.MatchFeatures.match(features) return e.MatchFeatures.match(features)
} }
func (m *FeatureMatcher) match(features map[string]*DomainFeatures) (bool, matchedFeatures, error) { func (m *FeatureMatcher) match(features *Features) (bool, matchedFeatures, error) {
matches := make(matchedFeatures, len(*m)) matches := make(matchedFeatures, len(*m))
// Logical AND over the terms // Logical AND over the terms
for _, term := range *m { for _, term := range *m {
split := strings.SplitN(term.Feature, ".", 2)
if len(split) != 2 {
return false, nil, fmt.Errorf("invalid feature %q: must be <domain>.<feature>", term.Feature)
}
domain := split[0]
// Ignore case // Ignore case
featureName := strings.ToLower(split[1]) featureName := strings.ToLower(term.Feature)
domainFeatures, ok := features[domain]
if !ok {
return false, nil, fmt.Errorf("unknown feature source/domain %q", domain)
}
if _, ok := matches[domain]; !ok {
matches[domain] = make(domainMatchedFeatures)
}
var isMatch bool var isMatch bool
var err error var err error
if f, ok := domainFeatures.Flags[featureName]; ok { if f, ok := features.Flags[featureName]; ok {
m, v, e := term.MatchExpressions.MatchGetKeys(f.Elements) m, v, e := term.MatchExpressions.MatchGetKeys(f.Elements)
isMatch = m isMatch = m
err = e err = e
matches[domain][featureName] = v matches[featureName] = v
} else if f, ok := domainFeatures.Attributes[featureName]; ok { } else if f, ok := features.Attributes[featureName]; ok {
m, v, e := term.MatchExpressions.MatchGetValues(f.Elements) m, v, e := term.MatchExpressions.MatchGetValues(f.Elements)
isMatch = m isMatch = m
err = e err = e
matches[domain][featureName] = v matches[featureName] = v
} else if f, ok := domainFeatures.Instances[featureName]; ok { } else if f, ok := features.Instances[featureName]; ok {
v, e := term.MatchExpressions.MatchGetInstances(f.Elements) v, e := term.MatchExpressions.MatchGetInstances(f.Elements)
isMatch = len(v) > 0 isMatch = len(v) > 0
err = e err = e
matches[domain][featureName] = v matches[featureName] = v
} else { } else {
return false, nil, fmt.Errorf("%q feature of source/domain %q not available", featureName, domain) return false, nil, fmt.Errorf("feature %q not available", featureName)
} }
if err != nil { if err != nil {

View file

@ -23,14 +23,14 @@ import (
) )
func TestRule(t *testing.T) { func TestRule(t *testing.T) {
f := map[string]*DomainFeatures{} f := &Features{}
r1 := Rule{Labels: map[string]string{"label-1": "", "label-2": "true"}} r1 := Rule{Labels: map[string]string{"label-1": "", "label-2": "true"}}
r2 := Rule{ r2 := Rule{
Labels: map[string]string{"label-1": "label-val-1"}, Labels: map[string]string{"label-1": "label-val-1"},
Vars: map[string]string{"var-1": "var-val-1"}, Vars: map[string]string{"var-1": "var-val-1"},
MatchFeatures: FeatureMatcher{ MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain-1.kf-1", Feature: "kf-1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchExists), "key-1": MustCreateMatchExpression(MatchExists),
}, },
@ -44,11 +44,10 @@ func TestRule(t *testing.T) {
assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features") assert.Equal(t, r1.Labels, m.Labels, "empty matcher should have matched empty features")
_, err = r2.Execute(f) _, err = r2.Execute(f)
assert.Error(t, err, "matching against a missing domain should have returned an error") assert.Error(t, err, "matching against a missing feature should have returned an error")
// Test empty domain // Test properly initialized empty features
d := NewDomainFeatures() f = NewFeatures()
f["domain-1"] = d
m, err = r1.Execute(f) m, err = r1.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
@ -59,9 +58,9 @@ func TestRule(t *testing.T) {
assert.Error(t, err, "matching against a missing feature type should have returned an error") assert.Error(t, err, "matching against a missing feature type should have returned an error")
// Test empty feature sets // Test empty feature sets
d.Flags["kf-1"] = NewFlagFeatures() f.Flags["kf-1"] = NewFlagFeatures()
d.Attributes["vf-1"] = NewAttributeFeatures(nil) f.Attributes["vf-1"] = NewAttributeFeatures(nil)
d.Instances["if-1"] = NewInstanceFeatures(nil) f.Instances["if-1"] = NewInstanceFeatures(nil)
m, err = r1.Execute(f) m, err = r1.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
@ -72,9 +71,9 @@ func TestRule(t *testing.T) {
assert.Nil(t, m.Labels, "unexpected match") assert.Nil(t, m.Labels, "unexpected match")
// Test non-empty feature sets // Test non-empty feature sets
d.Flags["kf-1"].Elements["key-x"] = Nil{} f.Flags["kf-1"].Elements["key-x"] = Nil{}
d.Attributes["vf-1"].Elements["key-1"] = "val-x" f.Attributes["vf-1"].Elements["key-1"] = "val-x"
d.Instances["if-1"] = NewInstanceFeatures([]InstanceFeature{ f.Instances["if-1"] = NewInstanceFeatures([]InstanceFeature{
*NewInstanceFeature(map[string]string{"attr-1": "val-x"})}) *NewInstanceFeature(map[string]string{"attr-1": "val-x"})})
m, err = r1.Execute(f) m, err = r1.Execute(f)
@ -84,7 +83,7 @@ func TestRule(t *testing.T) {
// Test empty MatchExpressions // Test empty MatchExpressions
r1.MatchFeatures = FeatureMatcher{ r1.MatchFeatures = FeatureMatcher{
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain-1.kf-1", Feature: "kf-1",
MatchExpressions: MatchExpressionSet{}, MatchExpressions: MatchExpressionSet{},
}, },
} }
@ -97,7 +96,7 @@ func TestRule(t *testing.T) {
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "keys should not have matched") assert.Nil(t, m.Labels, "keys should not have matched")
d.Flags["kf-1"].Elements["key-1"] = Nil{} f.Flags["kf-1"].Elements["key-1"] = Nil{}
m, err = r2.Execute(f) m, err = r2.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r2.Labels, m.Labels, "keys should have matched") assert.Equal(t, r2.Labels, m.Labels, "keys should have matched")
@ -108,7 +107,7 @@ func TestRule(t *testing.T) {
Labels: map[string]string{"label-3": "label-val-3", "empty": ""}, Labels: map[string]string{"label-3": "label-val-3", "empty": ""},
MatchFeatures: FeatureMatcher{ MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain-1.vf-1", Feature: "vf-1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-1"), "key-1": MustCreateMatchExpression(MatchIn, "val-1"),
}, },
@ -119,7 +118,7 @@ func TestRule(t *testing.T) {
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "values should not have matched") assert.Nil(t, m.Labels, "values should not have matched")
d.Attributes["vf-1"].Elements["key-1"] = "val-1" f.Attributes["vf-1"].Elements["key-1"] = "val-1"
m, err = r3.Execute(f) m, err = r3.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r3.Labels, m.Labels, "values should have matched") assert.Equal(t, r3.Labels, m.Labels, "values should have matched")
@ -129,7 +128,7 @@ func TestRule(t *testing.T) {
Labels: map[string]string{"label-4": "label-val-4"}, Labels: map[string]string{"label-4": "label-val-4"},
MatchFeatures: FeatureMatcher{ MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain-1.if-1", Feature: "if-1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchIn, "val-1"), "attr-1": MustCreateMatchExpression(MatchIn, "val-1"),
}, },
@ -140,7 +139,7 @@ func TestRule(t *testing.T) {
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Nil(t, m.Labels, "instances should not have matched") assert.Nil(t, m.Labels, "instances should not have matched")
d.Instances["if-1"].Elements[0].Attributes["attr-1"] = "val-1" f.Instances["if-1"].Elements[0].Attributes["attr-1"] = "val-1"
m, err = r4.Execute(f) m, err = r4.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err) assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, r4.Labels, m.Labels, "instances should have matched") assert.Equal(t, r4.Labels, m.Labels, "instances should have matched")
@ -150,13 +149,13 @@ func TestRule(t *testing.T) {
Labels: map[string]string{"label-5": "label-val-5"}, Labels: map[string]string{"label-5": "label-val-5"},
MatchFeatures: FeatureMatcher{ MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain-1.vf-1", Feature: "vf-1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-x"), "key-1": MustCreateMatchExpression(MatchIn, "val-x"),
}, },
}, },
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain-1.if-1", Feature: "if-1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchIn, "val-1"), "attr-1": MustCreateMatchExpression(MatchIn, "val-1"),
}, },
@ -177,7 +176,7 @@ func TestRule(t *testing.T) {
{ {
MatchFeatures: FeatureMatcher{ MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain-1.kf-1", Feature: "kf-1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"key-na": MustCreateMatchExpression(MatchExists), "key-na": MustCreateMatchExpression(MatchExists),
}, },
@ -193,7 +192,7 @@ func TestRule(t *testing.T) {
MatchAnyElem{ MatchAnyElem{
MatchFeatures: FeatureMatcher{ MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain-1.kf-1", Feature: "kf-1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchExists), "key-1": MustCreateMatchExpression(MatchExists),
}, },
@ -207,10 +206,9 @@ func TestRule(t *testing.T) {
} }
func TestTemplating(t *testing.T) { func TestTemplating(t *testing.T) {
f := map[string]*DomainFeatures{ f := &Features{
"domain_1": &DomainFeatures{
Flags: map[string]FlagFeatureSet{ Flags: map[string]FlagFeatureSet{
"kf_1": FlagFeatureSet{ "kf_1": {
Elements: map[string]Nil{ Elements: map[string]Nil{
"key-a": {}, "key-a": {},
"key-b": {}, "key-b": {},
@ -219,7 +217,7 @@ func TestTemplating(t *testing.T) {
}, },
}, },
Attributes: map[string]AttributeFeatureSet{ Attributes: map[string]AttributeFeatureSet{
"vf_1": AttributeFeatureSet{ "vf_1": {
Elements: map[string]string{ Elements: map[string]string{
"key-1": "val-1", "key-1": "val-1",
"keu-2": "val-2", "keu-2": "val-2",
@ -228,7 +226,7 @@ func TestTemplating(t *testing.T) {
}, },
}, },
Instances: map[string]InstanceFeatureSet{ Instances: map[string]InstanceFeatureSet{
"if_1": InstanceFeatureSet{ "if_1": {
Elements: []InstanceFeature{ Elements: []InstanceFeature{
{ {
Attributes: map[string]string{ Attributes: map[string]string{
@ -251,7 +249,6 @@ func TestTemplating(t *testing.T) {
}, },
}, },
}, },
},
} }
r1 := Rule{ r1 := Rule{
@ -259,21 +256,21 @@ func TestTemplating(t *testing.T) {
LabelsTemplate: ` LabelsTemplate: `
label-1=will-be-overridden label-1=will-be-overridden
label-2= label-2=
{{range .domain_1.kf_1}}kf-{{.Name}}=present {{range .kf_1}}kf-{{.Name}}=present
{{end}} {{end}}
{{range .domain_1.vf_1}}vf-{{.Name}}=vf-{{.Value}} {{range .vf_1}}vf-{{.Name}}=vf-{{.Value}}
{{end}} {{end}}
{{range .domain_1.if_1}}if-{{index . "attr-1"}}_{{index . "attr-2"}}=present {{range .if_1}}if-{{index . "attr-1"}}_{{index . "attr-2"}}=present
{{end}}`, {{end}}`,
Vars: map[string]string{"var-1": "var-val-1"}, Vars: map[string]string{"var-1": "var-val-1"},
VarsTemplate: ` VarsTemplate: `
var-1=value-will-be-overridden-by-vars var-1=value-will-be-overridden-by-vars
var-2= var-2=
{{range .domain_1.kf_1}}kf-{{.Name}}=true {{range .kf_1}}kf-{{.Name}}=true
{{end}}`, {{end}}`,
MatchFeatures: FeatureMatcher{ MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain_1.kf_1", Feature: "kf_1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"key-a": MustCreateMatchExpression(MatchExists), "key-a": MustCreateMatchExpression(MatchExists),
"key-c": MustCreateMatchExpression(MatchExists), "key-c": MustCreateMatchExpression(MatchExists),
@ -281,14 +278,14 @@ var-2=
}, },
}, },
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain_1.vf_1", Feature: "vf_1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-1", "val-2"), "key-1": MustCreateMatchExpression(MatchIn, "val-1", "val-2"),
"bar": MustCreateMatchExpression(MatchDoesNotExist), "bar": MustCreateMatchExpression(MatchDoesNotExist),
}, },
}, },
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain_1.if_1", Feature: "if_1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchLt, "100"), "attr-1": MustCreateMatchExpression(MatchLt, "100"),
}, },
@ -342,7 +339,7 @@ var-2=
// We need at least one matcher to match to execute the template. // We need at least one matcher to match to execute the template.
// Use a simple empty matchexpression set to match anything. // Use a simple empty matchexpression set to match anything.
FeatureMatcherTerm{ FeatureMatcherTerm{
Feature: "domain_1.kf_1", Feature: "kf_1",
MatchExpressions: MatchExpressionSet{ MatchExpressions: MatchExpressionSet{
"key-a": MustCreateMatchExpression(MatchExists), "key-a": MustCreateMatchExpression(MatchExists),
}, },

View file

@ -20,18 +20,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
// Features is a collection of all features of the system, arranged by domain. // Features is the collection of all discovered features.
// //
// +protobuf=true // +protobuf=true
type Features map[string]*DomainFeatures type Features struct {
Flags map[string]FlagFeatureSet `json:"flags" protobuf:"bytes,1,rep,name=flags"`
// DomainFeatures is the collection of all discovered features of one domain. Attributes map[string]AttributeFeatureSet `json:"attributes" protobuf:"bytes,2,rep,name=vattributes"`
// Instances map[string]InstanceFeatureSet `json:"instances" protobuf:"bytes,3,rep,name=instances"`
// +protobuf=true
type DomainFeatures struct {
Flags map[string]FlagFeatureSet `json:"flags" protobuf:"bytes,2,rep,name=flags"`
Attributes map[string]AttributeFeatureSet `json:"attributes" protobuf:"bytes,3,rep,name=vattributes"`
Instances map[string]InstanceFeatureSet `json:"instances" protobuf:"bytes,4,rep,name=instances"`
} }
// FlagFeatureSet is a set of simple features only containing names without values. // FlagFeatureSet is a set of simple features only containing names without values.

View file

@ -32,7 +32,7 @@ func (in *AttributeFeatureSet) DeepCopy() *AttributeFeatureSet {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DomainFeatures) DeepCopyInto(out *DomainFeatures) { func (in *Features) DeepCopyInto(out *Features) {
*out = *in *out = *in
if in.Flags != nil { if in.Flags != nil {
in, out := &in.Flags, &out.Flags in, out := &in.Flags, &out.Flags
@ -58,11 +58,11 @@ func (in *DomainFeatures) DeepCopyInto(out *DomainFeatures) {
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainFeatures. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainFeatures.
func (in *DomainFeatures) DeepCopy() *DomainFeatures { func (in *Features) DeepCopy() *Features {
if in == nil { if in == nil {
return nil return nil
} }
out := new(DomainFeatures) out := new(Features)
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
@ -119,17 +119,17 @@ func (in *FeatureMatcherTerm) DeepCopy() *FeatureMatcherTerm {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Features) DeepCopyInto(out *Features) { func (in FeaturesMap) DeepCopyInto(out *FeaturesMap) {
{ {
in := &in in := &in
*out = make(Features, len(*in)) *out = make(FeaturesMap, len(*in))
for key, val := range *in { for key, val := range *in {
var outVal *DomainFeatures var outVal *Features
if val == nil { if val == nil {
(*out)[key] = nil (*out)[key] = nil
} else { } else {
in, out := &val, &outVal in, out := &val, &outVal
*out = new(DomainFeatures) *out = new(Features)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
(*out)[key] = outVal (*out)[key] = outVal
@ -138,11 +138,11 @@ func (in Features) DeepCopyInto(out *Features) {
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Features. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Features.
func (in Features) DeepCopy() Features { func (in FeaturesMap) DeepCopy() FeaturesMap {
if in == nil { if in == nil {
return nil return nil
} }
out := new(Features) out := new(FeaturesMap)
in.DeepCopyInto(out) in.DeepCopyInto(out)
return *out return *out
} }

View file

@ -53,7 +53,7 @@ type SetLabelsRequest struct {
NfdVersion string `protobuf:"bytes,1,opt,name=nfd_version,json=nfdVersion,proto3" json:"nfd_version,omitempty"` NfdVersion string `protobuf:"bytes,1,opt,name=nfd_version,json=nfdVersion,proto3" json:"nfd_version,omitempty"`
NodeName string `protobuf:"bytes,2,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"` NodeName string `protobuf:"bytes,2,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"`
Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Features map[string]*v1alpha1.DomainFeatures `protobuf:"bytes,4,rep,name=features,proto3" json:"features,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Features map[string]*v1alpha1.Features `protobuf:"bytes,4,rep,name=features,proto3" json:"features,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
} }
func (x *SetLabelsRequest) Reset() { func (x *SetLabelsRequest) Reset() {
@ -109,7 +109,7 @@ func (x *SetLabelsRequest) GetLabels() map[string]string {
return nil return nil
} }
func (x *SetLabelsRequest) GetFeatures() map[string]*v1alpha1.DomainFeatures { func (x *SetLabelsRequest) GetFeatures() map[string]*v1alpha1.Features {
if x != nil { if x != nil {
return x.Features return x.Features
} }
@ -214,7 +214,7 @@ var file_labeler_proto_goTypes = []interface{}{
(*SetLabelsReply)(nil), // 1: labeler.SetLabelsReply (*SetLabelsReply)(nil), // 1: labeler.SetLabelsReply
nil, // 2: labeler.SetLabelsRequest.LabelsEntry nil, // 2: labeler.SetLabelsRequest.LabelsEntry
nil, // 3: labeler.SetLabelsRequest.FeaturesEntry nil, // 3: labeler.SetLabelsRequest.FeaturesEntry
(*v1alpha1.DomainFeatures)(nil), // 4: v1alpha1.DomainFeatures (*v1alpha1.Features)(nil), // 4: v1alpha1.DomainFeatures
} }
var file_labeler_proto_depIdxs = []int32{ var file_labeler_proto_depIdxs = []int32{
2, // 0: labeler.SetLabelsRequest.labels:type_name -> labeler.SetLabelsRequest.LabelsEntry 2, // 0: labeler.SetLabelsRequest.labels:type_name -> labeler.SetLabelsRequest.LabelsEntry

View file

@ -30,7 +30,7 @@ message SetLabelsRequest {
string nfd_version = 1; string nfd_version = 1;
string node_name = 2; string node_name = 2;
map<string, string> labels = 3; map<string, string> labels = 3;
map<string, v1alpha1.DomainFeatures> features = 4; v1alpha1.Features features = 4;
} }
message SetLabelsReply { message SetLabelsReply {

View file

@ -31,7 +31,6 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
pb "sigs.k8s.io/node-feature-discovery/pkg/labeler" pb "sigs.k8s.io/node-feature-discovery/pkg/labeler"
clientcommon "sigs.k8s.io/node-feature-discovery/pkg/nfd-client" clientcommon "sigs.k8s.io/node-feature-discovery/pkg/nfd-client"
"sigs.k8s.io/node-feature-discovery/pkg/utils" "sigs.k8s.io/node-feature-discovery/pkg/utils"
@ -532,17 +531,6 @@ func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) (
return labels, nil return labels, nil
} }
// getFeatures returns raw features from all feature sources
func getFeatures() map[string]*nfdv1alpha1.DomainFeatures {
features := make(map[string]*nfdv1alpha1.DomainFeatures)
for name, src := range source.GetAllFeatureSources() {
features[name] = src.GetFeatures()
}
return features
}
// advertiseFeatureLabels advertises the feature labels to a Kubernetes node // advertiseFeatureLabels advertises the feature labels to a Kubernetes node
// via the NFD server. // via the NFD server.
func (w *nfdWorker) advertiseFeatureLabels(labels Labels) error { func (w *nfdWorker) advertiseFeatureLabels(labels Labels) error {
@ -552,7 +540,7 @@ func (w *nfdWorker) advertiseFeatureLabels(labels Labels) error {
klog.Infof("sending labeling request to nfd-master") klog.Infof("sending labeling request to nfd-master")
labelReq := pb.SetLabelsRequest{Labels: labels, labelReq := pb.SetLabelsRequest{Labels: labels,
Features: getFeatures(), Features: source.GetAllFeatures(),
NfdVersion: version.Get(), NfdVersion: version.Get(),
NodeName: clientcommon.NodeName()} NodeName: clientcommon.NodeName()}

View file

@ -501,6 +501,9 @@ func (m *nfdMaster) crLabels(r *pb.SetLabelsRequest) map[string]string {
return nil return nil
} }
// Helper struct for rule processing
features := r.GetFeatures()
// Process all rule CRs // Process all rule CRs
for _, spec := range ruleSpecs { for _, spec := range ruleSpecs {
switch { switch {
@ -511,7 +514,7 @@ func (m *nfdMaster) crLabels(r *pb.SetLabelsRequest) map[string]string {
klog.Infof("executing NodeFeatureRule %q", spec.ObjectMeta.Name) klog.Infof("executing NodeFeatureRule %q", spec.ObjectMeta.Name)
} }
for _, rule := range spec.Spec.Rules { for _, rule := range spec.Spec.Rules {
ruleOut, err := rule.Execute(r.Features) ruleOut, err := rule.Execute(features)
if err != nil { if err != nil {
klog.Errorf("failed to process Rule %q: %v", rule.Name, err) klog.Errorf("failed to process Rule %q: %v", rule.Name, err)
continue continue
@ -522,8 +525,8 @@ func (m *nfdMaster) crLabels(r *pb.SetLabelsRequest) map[string]string {
} }
// Feed back rule output to features map for subsequent rules to match // Feed back rule output to features map for subsequent rules to match
nfdv1alpha1.InsertAttributeFeatures(r.Features, nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels) features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels)
nfdv1alpha1.InsertAttributeFeatures(r.Features, nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars) features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars)
} }
} }

View file

@ -102,7 +102,7 @@ type keyFilter struct {
type cpuSource struct { type cpuSource struct {
config *Config config *Config
cpuidFilter *keyFilter cpuidFilter *keyFilter
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
} }
// Singleton source instance // Singleton source instance
@ -197,7 +197,7 @@ func (s *cpuSource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource Interface // Discover method of the FeatureSource Interface
func (s *cpuSource) Discover() error { func (s *cpuSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
// Detect CPUID // Detect CPUID
s.features.Flags[CpuidFeature] = nfdv1alpha1.NewFlagFeatures(getCpuidFlags()...) s.features.Flags[CpuidFeature] = nfdv1alpha1.NewFlagFeatures(getCpuidFlags()...)
@ -252,9 +252,9 @@ func (s *cpuSource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface // GetFeatures method of the FeatureSource Interface
func (s *cpuSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *cpuSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -107,10 +107,7 @@ func (s *customSource) Priority() int { return 10 }
// GetLabels method of the LabelSource interface // GetLabels method of the LabelSource interface
func (s *customSource) GetLabels() (source.FeatureLabels, error) { func (s *customSource) GetLabels() (source.FeatureLabels, error) {
// Get raw features from all sources // Get raw features from all sources
domainFeatures := make(map[string]*nfdv1alpha1.DomainFeatures) features := source.GetAllFeatures()
for n, s := range source.GetAllFeatureSources() {
domainFeatures[n] = s.GetFeatures()
}
labels := source.FeatureLabels{} labels := source.FeatureLabels{}
allFeatureConfig := append(getStaticFeatureConfig(), *s.config...) allFeatureConfig := append(getStaticFeatureConfig(), *s.config...)
@ -118,7 +115,7 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) {
utils.KlogDump(2, "custom features configuration:", " ", allFeatureConfig) utils.KlogDump(2, "custom features configuration:", " ", allFeatureConfig)
// Iterate over features // Iterate over features
for _, rule := range allFeatureConfig { for _, rule := range allFeatureConfig {
ruleOut, err := rule.execute(domainFeatures) ruleOut, err := rule.execute(features)
if err != nil { if err != nil {
klog.Error(err) klog.Error(err)
continue continue
@ -128,15 +125,15 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) {
labels[n] = v labels[n] = v
} }
// Feed back rule output to features map for subsequent rules to match // Feed back rule output to features map for subsequent rules to match
nfdv1alpha1.InsertAttributeFeatures(domainFeatures, nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels) features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels)
nfdv1alpha1.InsertAttributeFeatures(domainFeatures, nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars) features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars)
} }
return labels, nil return labels, nil
} }
func (r *CustomRule) execute(features map[string]*nfdv1alpha1.DomainFeatures) (nfdv1alpha1.RuleOutput, error) { func (r *CustomRule) execute(features *nfdv1alpha1.Features) (nfdv1alpha1.RuleOutput, error) {
if r.LegacyRule != nil { if r.LegacyRule != nil {
ruleOut, err := r.LegacyRule.execute(features) ruleOut, err := r.LegacyRule.execute()
if err != nil { if err != nil {
return nfdv1alpha1.RuleOutput{}, fmt.Errorf("failed to execute legacy rule %s: %w", r.LegacyRule.Name, err) return nfdv1alpha1.RuleOutput{}, fmt.Errorf("failed to execute legacy rule %s: %w", r.LegacyRule.Name, err)
} }
@ -154,7 +151,7 @@ func (r *CustomRule) execute(features map[string]*nfdv1alpha1.DomainFeatures) (n
return nfdv1alpha1.RuleOutput{}, fmt.Errorf("BUG: an empty rule, this really should not happen") return nfdv1alpha1.RuleOutput{}, fmt.Errorf("BUG: an empty rule, this really should not happen")
} }
func (r *LegacyRule) execute(features map[string]*nfdv1alpha1.DomainFeatures) (map[string]string, error) { func (r *LegacyRule) execute() (map[string]string, error) {
if len(r.MatchOn) > 0 { if len(r.MatchOn) > 0 {
// Logical OR over the legacy rules // Logical OR over the legacy rules
matched := false matched := false

View file

@ -84,7 +84,7 @@ func newDefaultConfig() *Config {
// fakeSource implements the FeatureSource, LabelSource and ConfigurableSource interfaces. // fakeSource implements the FeatureSource, LabelSource and ConfigurableSource interfaces.
type fakeSource struct { type fakeSource struct {
config *Config config *Config
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
} }
// Singleton source instance // Singleton source instance
@ -116,7 +116,7 @@ func (s *fakeSource) SetConfig(conf source.Config) {
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *fakeSource) Discover() error { func (s *fakeSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
s.features.Flags[FlagFeature] = nfdv1alpha1.NewFlagFeatures(s.config.FlagFeatures...) s.features.Flags[FlagFeature] = nfdv1alpha1.NewFlagFeatures(s.config.FlagFeatures...)
s.features.Attributes[AttributeFeature] = nfdv1alpha1.NewAttributeFeatures(s.config.AttributeFeatures) s.features.Attributes[AttributeFeature] = nfdv1alpha1.NewAttributeFeatures(s.config.AttributeFeatures)
@ -133,9 +133,9 @@ func (s *fakeSource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface. // GetFeatures method of the FeatureSource Interface.
func (s *fakeSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *fakeSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -58,7 +58,7 @@ func newDefaultConfig() *Config {
// kernelSource implements the FeatureSource, LabelSource and ConfigurableSource interfaces. // kernelSource implements the FeatureSource, LabelSource and ConfigurableSource interfaces.
type kernelSource struct { type kernelSource struct {
config *Config config *Config
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
// legacyKconfig contains mangled kconfig values used for // legacyKconfig contains mangled kconfig values used for
// kernel.config-<flag> labels and legacy kConfig custom rules. // kernel.config-<flag> labels and legacy kConfig custom rules.
legacyKconfig map[string]string legacyKconfig map[string]string
@ -117,7 +117,7 @@ func (s *kernelSource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *kernelSource) Discover() error { func (s *kernelSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
// Read kernel version // Read kernel version
if version, err := parseVersion(); err != nil { if version, err := parseVersion(); err != nil {
@ -153,9 +153,9 @@ func (s *kernelSource) Discover() error {
return nil return nil
} }
func (s *kernelSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *kernelSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -45,7 +45,7 @@ var (
// localSource implements the FeatureSource and LabelSource interfaces. // localSource implements the FeatureSource and LabelSource interfaces.
type localSource struct { type localSource struct {
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
config *Config config *Config
} }
@ -103,7 +103,7 @@ func newDefaultConfig() *Config {
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *localSource) Discover() error { func (s *localSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
featuresFromFiles, err := getFeaturesFromFiles() featuresFromFiles, err := getFeaturesFromFiles()
if err != nil { if err != nil {
@ -137,9 +137,9 @@ func (s *localSource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface // GetFeatures method of the FeatureSource Interface
func (s *localSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *localSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -42,7 +42,7 @@ const NumaFeature = "numa"
// memorySource implements the FeatureSource and LabelSource interfaces. // memorySource implements the FeatureSource and LabelSource interfaces.
type memorySource struct { type memorySource struct {
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
} }
// Singleton source instance // Singleton source instance
@ -84,7 +84,7 @@ func (s *memorySource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *memorySource) Discover() error { func (s *memorySource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
// Detect NUMA // Detect NUMA
if numa, err := detectNuma(); err != nil { if numa, err := detectNuma(); err != nil {
@ -106,9 +106,9 @@ func (s *memorySource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface. // GetFeatures method of the FeatureSource Interface.
func (s *memorySource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *memorySource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -40,7 +40,7 @@ const sysfsBaseDir = "class/net"
// networkSource implements the FeatureSource and LabelSource interfaces. // networkSource implements the FeatureSource and LabelSource interfaces.
type networkSource struct { type networkSource struct {
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
} }
// Singleton source instance // Singleton source instance
@ -91,7 +91,7 @@ func (s *networkSource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource interface. // Discover method of the FeatureSource interface.
func (s *networkSource) Discover() error { func (s *networkSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
devs, err := detectNetDevices() devs, err := detectNetDevices()
if err != nil { if err != nil {
@ -105,9 +105,9 @@ func (s *networkSource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface. // GetFeatures method of the FeatureSource Interface.
func (s *networkSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *networkSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -50,7 +50,7 @@ func newDefaultConfig() *Config {
// pciSource implements the FeatureSource, LabelSource and ConfigurableSource interfaces. // pciSource implements the FeatureSource, LabelSource and ConfigurableSource interfaces.
type pciSource struct { type pciSource struct {
config *Config config *Config
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
} }
// Singleton source instance // Singleton source instance
@ -140,7 +140,7 @@ func (s *pciSource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *pciSource) Discover() error { func (s *pciSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
devs, err := detectPci() devs, err := detectPci()
if err != nil { if err != nil {
@ -154,9 +154,9 @@ func (s *pciSource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface // GetFeatures method of the FeatureSource Interface
func (s *pciSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *pciSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -21,6 +21,8 @@ package source
import ( import (
"fmt" "fmt"
"k8s.io/klog/v2"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
) )
@ -38,7 +40,7 @@ type FeatureSource interface {
Discover() error Discover() error
// GetFeatures returns discovered features in raw form // GetFeatures returns discovered features in raw form
GetFeatures() *nfdv1alpha1.DomainFeatures GetFeatures() *nfdv1alpha1.Features
} }
// LabelSource represents a source of node feature labels // LabelSource represents a source of node feature labels
@ -154,3 +156,37 @@ func GetAllConfigurableSources() map[string]ConfigurableSource {
} }
return all return all
} }
// GetAllFeatures returns a combined set of all features from all feature
// sources.
func GetAllFeatures() *nfdv1alpha1.Features {
features := nfdv1alpha1.NewFeatures()
for n, s := range GetAllFeatureSources() {
f := s.GetFeatures()
for k, v := range f.Flags {
// Prefix feature with the name of the source
k = n + "." + k
if typ := features.Exists(k); typ != "" {
klog.Exitf("feature source %q returned flag feature %q which already exists (type %q)", n, k, typ)
}
features.Flags[k] = v
}
for k, v := range f.Attributes {
// Prefix feature with the name of the source
k = n + "." + k
if typ := features.Exists(k); typ != "" {
klog.Exitf("feature source %q returned attribute feature %q which already exists (type %q)", n, k, typ)
}
features.Attributes[k] = v
}
for k, v := range f.Instances {
// Prefix feature with the name of the source
k = n + "." + k
if typ := features.Exists(k); typ != "" {
klog.Exitf("feature source %q returned instance feature %q which already exists (type %q)", n, k, typ)
}
features.Instances[k] = v
}
}
return features
}

View file

@ -37,7 +37,7 @@ const BlockFeature = "block"
// storageSource implements the FeatureSource and LabelSource interfaces. // storageSource implements the FeatureSource and LabelSource interfaces.
type storageSource struct { type storageSource struct {
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
} }
// Singleton source instance // Singleton source instance
@ -73,7 +73,7 @@ func (s *storageSource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *storageSource) Discover() error { func (s *storageSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
devs, err := detectBlock() devs, err := detectBlock()
if err != nil { if err != nil {
@ -87,9 +87,9 @@ func (s *storageSource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface. // GetFeatures method of the FeatureSource Interface.
func (s *storageSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *storageSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -47,7 +47,7 @@ const (
// systemSource implements the FeatureSource and LabelSource interfaces. // systemSource implements the FeatureSource and LabelSource interfaces.
type systemSource struct { type systemSource struct {
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
} }
// Singleton source instance // Singleton source instance
@ -78,7 +78,7 @@ func (s *systemSource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *systemSource) Discover() error { func (s *systemSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
// Get node name // Get node name
s.features.Attributes[NameFeature] = nfdv1alpha1.NewAttributeFeatures(nil) s.features.Attributes[NameFeature] = nfdv1alpha1.NewAttributeFeatures(nil)
@ -107,9 +107,9 @@ func (s *systemSource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface // GetFeatures method of the FeatureSource Interface
func (s *systemSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *systemSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }

View file

@ -53,7 +53,7 @@ func defaultDeviceLabelFields() []string { return []string{"class", "vendor", "d
// usbSource implements the LabelSource and ConfigurableSource interfaces. // usbSource implements the LabelSource and ConfigurableSource interfaces.
type usbSource struct { type usbSource struct {
config *Config config *Config
features *nfdv1alpha1.DomainFeatures features *nfdv1alpha1.Features
} }
// Singleton source instance // Singleton source instance
@ -139,7 +139,7 @@ func (s *usbSource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource interface // Discover method of the FeatureSource interface
func (s *usbSource) Discover() error { func (s *usbSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
devs, err := detectUsb() devs, err := detectUsb()
if err != nil { if err != nil {
@ -153,9 +153,9 @@ func (s *usbSource) Discover() error {
} }
// GetFeatures method of the FeatureSource Interface // GetFeatures method of the FeatureSource Interface
func (s *usbSource) GetFeatures() *nfdv1alpha1.DomainFeatures { func (s *usbSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil { if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures() s.features = nfdv1alpha1.NewFeatures()
} }
return s.features return s.features
} }