1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-14 20:56:42 +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
// NewDomainFeatures creates a new instance of Features, initializing specified
// features to empty values
func NewDomainFeatures() *DomainFeatures {
return &DomainFeatures{
// NewFeatures creates a new instance of Features, initializing all feature
// types (flags, attributes and instances) to empty values.
func NewFeatures() *Features {
return &Features{
Flags: make(map[string]FlagFeatureSet),
Attributes: make(map[string]AttributeFeatureSet),
Instances: make(map[string]InstanceFeatureSet)}
@ -56,16 +56,29 @@ func NewInstanceFeature(attrs map[string]string) *InstanceFeature {
}
// InsertAttributeFeatures inserts new values into a specific feature.
func InsertAttributeFeatures(f Features, domain, feature string, values map[string]string) {
if _, ok := f[domain]; !ok {
f[domain] = NewDomainFeatures()
}
if _, ok := f[domain].Attributes[feature]; !ok {
f[domain].Attributes[feature] = NewAttributeFeatures(values)
func (f *Features) InsertAttributeFeatures(domain, feature string, values map[string]string) {
key := domain + "." + feature
if _, ok := f.Attributes[key]; !ok {
f.Attributes[key] = NewAttributeFeatures(values)
return
}
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
func (m *DomainFeatures) Reset() { *m = DomainFeatures{} }
func (*DomainFeatures) ProtoMessage() {}
func (*DomainFeatures) Descriptor() ([]byte, []int) {
func (m *Features) Reset() { *m = Features{} }
func (*Features) ProtoMessage() {}
func (*Features) Descriptor() ([]byte, []int) {
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)
}
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)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
@ -87,13 +87,13 @@ func (m *DomainFeatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, erro
}
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)
}
func (m *DomainFeatures) XXX_Size() int {
func (m *Features) XXX_Size() int {
return m.Size()
}
func (m *DomainFeatures) XXX_DiscardUnknown() {
func (m *Features) XXX_DiscardUnknown() {
xxx_messageInfo_DomainFeatures.DiscardUnknown(m)
}
@ -214,7 +214,7 @@ var xxx_messageInfo_Nil proto.InternalMessageInfo
func init() {
proto.RegisterType((*AttributeFeatureSet)(nil), "v1alpha1.AttributeFeatureSet")
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]InstanceFeatureSet)(nil), "v1alpha1.DomainFeatures.InstancesEntry")
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
}
func (m *DomainFeatures) Marshal() (dAtA []byte, err error) {
func (m *Features) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
@ -326,12 +326,12 @@ func (m *DomainFeatures) Marshal() (dAtA []byte, err error) {
return dAtA[:n], nil
}
func (m *DomainFeatures) MarshalTo(dAtA []byte) (int, error) {
func (m *Features) MarshalTo(dAtA []byte) (int, error) {
size := m.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
var l int
@ -613,7 +613,7 @@ func (m *AttributeFeatureSet) Size() (n int) {
return n
}
func (m *DomainFeatures) Size() (n int) {
func (m *Features) Size() (n int) {
if m == nil {
return 0
}
@ -734,7 +734,7 @@ func (this *AttributeFeatureSet) String() string {
}, "")
return s
}
func (this *DomainFeatures) String() string {
func (this *Features) String() string {
if this == nil {
return "nil"
}
@ -1025,7 +1025,7 @@ func (m *AttributeFeatureSet) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *DomainFeatures) Unmarshal(dAtA []byte) error {
func (m *Features) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {

View file

@ -35,7 +35,7 @@ type RuleOutput struct {
}
// 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)
vars := make(map[string]string)
@ -145,55 +145,39 @@ func (r *Rule) executeVarsTemplate(in matchedFeatures, out map[string]string) er
return nil
}
type matchedFeatures map[string]domainMatchedFeatures
type matchedFeatures map[string]interface{}
type domainMatchedFeatures map[string]interface{}
func (e *MatchAnyElem) match(features map[string]*DomainFeatures) (bool, matchedFeatures, error) {
func (e *MatchAnyElem) match(features *Features) (bool, matchedFeatures, error) {
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))
// Logical AND over the terms
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
featureName := strings.ToLower(split[1])
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)
}
featureName := strings.ToLower(term.Feature)
var isMatch bool
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)
isMatch = m
err = e
matches[domain][featureName] = v
} else if f, ok := domainFeatures.Attributes[featureName]; ok {
matches[featureName] = v
} else if f, ok := features.Attributes[featureName]; ok {
m, v, e := term.MatchExpressions.MatchGetValues(f.Elements)
isMatch = m
err = e
matches[domain][featureName] = v
} else if f, ok := domainFeatures.Instances[featureName]; ok {
matches[featureName] = v
} else if f, ok := features.Instances[featureName]; ok {
v, e := term.MatchExpressions.MatchGetInstances(f.Elements)
isMatch = len(v) > 0
err = e
matches[domain][featureName] = v
matches[featureName] = v
} 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 {

View file

@ -23,14 +23,14 @@ import (
)
func TestRule(t *testing.T) {
f := map[string]*DomainFeatures{}
f := &Features{}
r1 := Rule{Labels: map[string]string{"label-1": "", "label-2": "true"}}
r2 := Rule{
Labels: map[string]string{"label-1": "label-val-1"},
Vars: map[string]string{"var-1": "var-val-1"},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.kf-1",
Feature: "kf-1",
MatchExpressions: MatchExpressionSet{
"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")
_, 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
d := NewDomainFeatures()
f["domain-1"] = d
// Test properly initialized empty features
f = NewFeatures()
m, err = r1.Execute(f)
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")
// Test empty feature sets
d.Flags["kf-1"] = NewFlagFeatures()
d.Attributes["vf-1"] = NewAttributeFeatures(nil)
d.Instances["if-1"] = NewInstanceFeatures(nil)
f.Flags["kf-1"] = NewFlagFeatures()
f.Attributes["vf-1"] = NewAttributeFeatures(nil)
f.Instances["if-1"] = NewInstanceFeatures(nil)
m, err = r1.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err)
@ -72,9 +71,9 @@ func TestRule(t *testing.T) {
assert.Nil(t, m.Labels, "unexpected match")
// Test non-empty feature sets
d.Flags["kf-1"].Elements["key-x"] = Nil{}
d.Attributes["vf-1"].Elements["key-1"] = "val-x"
d.Instances["if-1"] = NewInstanceFeatures([]InstanceFeature{
f.Flags["kf-1"].Elements["key-x"] = Nil{}
f.Attributes["vf-1"].Elements["key-1"] = "val-x"
f.Instances["if-1"] = NewInstanceFeatures([]InstanceFeature{
*NewInstanceFeature(map[string]string{"attr-1": "val-x"})})
m, err = r1.Execute(f)
@ -84,7 +83,7 @@ func TestRule(t *testing.T) {
// Test empty MatchExpressions
r1.MatchFeatures = FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.kf-1",
Feature: "kf-1",
MatchExpressions: MatchExpressionSet{},
},
}
@ -97,7 +96,7 @@ func TestRule(t *testing.T) {
assert.Nilf(t, err, "unexpected error: %v", err)
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)
assert.Nilf(t, err, "unexpected error: %v", err)
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": ""},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.vf-1",
Feature: "vf-1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-1"),
},
@ -119,7 +118,7 @@ func TestRule(t *testing.T) {
assert.Nilf(t, err, "unexpected error: %v", err)
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)
assert.Nilf(t, err, "unexpected error: %v", err)
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"},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.if-1",
Feature: "if-1",
MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchIn, "val-1"),
},
@ -140,7 +139,7 @@ func TestRule(t *testing.T) {
assert.Nilf(t, err, "unexpected error: %v", err)
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)
assert.Nilf(t, err, "unexpected error: %v", err)
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"},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.vf-1",
Feature: "vf-1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-x"),
},
},
FeatureMatcherTerm{
Feature: "domain-1.if-1",
Feature: "if-1",
MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchIn, "val-1"),
},
@ -177,7 +176,7 @@ func TestRule(t *testing.T) {
{
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.kf-1",
Feature: "kf-1",
MatchExpressions: MatchExpressionSet{
"key-na": MustCreateMatchExpression(MatchExists),
},
@ -193,7 +192,7 @@ func TestRule(t *testing.T) {
MatchAnyElem{
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain-1.kf-1",
Feature: "kf-1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchExists),
},
@ -207,46 +206,44 @@ func TestRule(t *testing.T) {
}
func TestTemplating(t *testing.T) {
f := map[string]*DomainFeatures{
"domain_1": &DomainFeatures{
Flags: map[string]FlagFeatureSet{
"kf_1": FlagFeatureSet{
Elements: map[string]Nil{
"key-a": {},
"key-b": {},
"key-c": {},
},
f := &Features{
Flags: map[string]FlagFeatureSet{
"kf_1": {
Elements: map[string]Nil{
"key-a": {},
"key-b": {},
"key-c": {},
},
},
Attributes: map[string]AttributeFeatureSet{
"vf_1": AttributeFeatureSet{
Elements: map[string]string{
"key-1": "val-1",
"keu-2": "val-2",
"key-3": "val-3",
},
},
Attributes: map[string]AttributeFeatureSet{
"vf_1": {
Elements: map[string]string{
"key-1": "val-1",
"keu-2": "val-2",
"key-3": "val-3",
},
},
Instances: map[string]InstanceFeatureSet{
"if_1": InstanceFeatureSet{
Elements: []InstanceFeature{
{
Attributes: map[string]string{
"attr-1": "1",
"attr-2": "val-2",
},
},
Instances: map[string]InstanceFeatureSet{
"if_1": {
Elements: []InstanceFeature{
{
Attributes: map[string]string{
"attr-1": "1",
"attr-2": "val-2",
},
{
Attributes: map[string]string{
"attr-1": "10",
"attr-2": "val-20",
},
},
{
Attributes: map[string]string{
"attr-1": "10",
"attr-2": "val-20",
},
{
Attributes: map[string]string{
"attr-1": "100",
"attr-2": "val-200",
},
},
{
Attributes: map[string]string{
"attr-1": "100",
"attr-2": "val-200",
},
},
},
@ -259,21 +256,21 @@ func TestTemplating(t *testing.T) {
LabelsTemplate: `
label-1=will-be-overridden
label-2=
{{range .domain_1.kf_1}}kf-{{.Name}}=present
{{range .kf_1}}kf-{{.Name}}=present
{{end}}
{{range .domain_1.vf_1}}vf-{{.Name}}=vf-{{.Value}}
{{range .vf_1}}vf-{{.Name}}=vf-{{.Value}}
{{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}}`,
Vars: map[string]string{"var-1": "var-val-1"},
VarsTemplate: `
var-1=value-will-be-overridden-by-vars
var-2=
{{range .domain_1.kf_1}}kf-{{.Name}}=true
{{range .kf_1}}kf-{{.Name}}=true
{{end}}`,
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "domain_1.kf_1",
Feature: "kf_1",
MatchExpressions: MatchExpressionSet{
"key-a": MustCreateMatchExpression(MatchExists),
"key-c": MustCreateMatchExpression(MatchExists),
@ -281,14 +278,14 @@ var-2=
},
},
FeatureMatcherTerm{
Feature: "domain_1.vf_1",
Feature: "vf_1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-1", "val-2"),
"bar": MustCreateMatchExpression(MatchDoesNotExist),
},
},
FeatureMatcherTerm{
Feature: "domain_1.if_1",
Feature: "if_1",
MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchLt, "100"),
},
@ -342,7 +339,7 @@ var-2=
// We need at least one matcher to match to execute the template.
// Use a simple empty matchexpression set to match anything.
FeatureMatcherTerm{
Feature: "domain_1.kf_1",
Feature: "kf_1",
MatchExpressions: MatchExpressionSet{
"key-a": MustCreateMatchExpression(MatchExists),
},

View file

@ -20,18 +20,13 @@ import (
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
type Features map[string]*DomainFeatures
// DomainFeatures is the collection of all discovered features of one domain.
//
// +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"`
type Features struct {
Flags map[string]FlagFeatureSet `json:"flags" protobuf:"bytes,1,rep,name=flags"`
Attributes map[string]AttributeFeatureSet `json:"attributes" protobuf:"bytes,2,rep,name=vattributes"`
Instances map[string]InstanceFeatureSet `json:"instances" protobuf:"bytes,3,rep,name=instances"`
}
// 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.
func (in *DomainFeatures) DeepCopyInto(out *DomainFeatures) {
func (in *Features) DeepCopyInto(out *Features) {
*out = *in
if in.Flags != nil {
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.
func (in *DomainFeatures) DeepCopy() *DomainFeatures {
func (in *Features) DeepCopy() *Features {
if in == nil {
return nil
}
out := new(DomainFeatures)
out := new(Features)
in.DeepCopyInto(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.
func (in Features) DeepCopyInto(out *Features) {
func (in FeaturesMap) DeepCopyInto(out *FeaturesMap) {
{
in := &in
*out = make(Features, len(*in))
*out = make(FeaturesMap, len(*in))
for key, val := range *in {
var outVal *DomainFeatures
var outVal *Features
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = new(DomainFeatures)
*out = new(Features)
(*in).DeepCopyInto(*out)
}
(*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.
func (in Features) DeepCopy() Features {
func (in FeaturesMap) DeepCopy() FeaturesMap {
if in == nil {
return nil
}
out := new(Features)
out := new(FeaturesMap)
in.DeepCopyInto(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"`
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"`
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() {
@ -109,7 +109,7 @@ func (x *SetLabelsRequest) GetLabels() map[string]string {
return nil
}
func (x *SetLabelsRequest) GetFeatures() map[string]*v1alpha1.DomainFeatures {
func (x *SetLabelsRequest) GetFeatures() map[string]*v1alpha1.Features {
if x != nil {
return x.Features
}
@ -214,7 +214,7 @@ var file_labeler_proto_goTypes = []interface{}{
(*SetLabelsReply)(nil), // 1: labeler.SetLabelsReply
nil, // 2: labeler.SetLabelsRequest.LabelsEntry
nil, // 3: labeler.SetLabelsRequest.FeaturesEntry
(*v1alpha1.DomainFeatures)(nil), // 4: v1alpha1.DomainFeatures
(*v1alpha1.Features)(nil), // 4: v1alpha1.DomainFeatures
}
var file_labeler_proto_depIdxs = []int32{
2, // 0: labeler.SetLabelsRequest.labels:type_name -> labeler.SetLabelsRequest.LabelsEntry

View file

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

View file

@ -31,7 +31,6 @@ import (
"k8s.io/klog/v2"
"sigs.k8s.io/yaml"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
pb "sigs.k8s.io/node-feature-discovery/pkg/labeler"
clientcommon "sigs.k8s.io/node-feature-discovery/pkg/nfd-client"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
@ -532,17 +531,6 @@ func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) (
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
// via the NFD server.
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")
labelReq := pb.SetLabelsRequest{Labels: labels,
Features: getFeatures(),
Features: source.GetAllFeatures(),
NfdVersion: version.Get(),
NodeName: clientcommon.NodeName()}

View file

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

View file

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

View file

@ -107,10 +107,7 @@ func (s *customSource) Priority() int { return 10 }
// GetLabels method of the LabelSource interface
func (s *customSource) GetLabels() (source.FeatureLabels, error) {
// Get raw features from all sources
domainFeatures := make(map[string]*nfdv1alpha1.DomainFeatures)
for n, s := range source.GetAllFeatureSources() {
domainFeatures[n] = s.GetFeatures()
}
features := source.GetAllFeatures()
labels := source.FeatureLabels{}
allFeatureConfig := append(getStaticFeatureConfig(), *s.config...)
@ -118,7 +115,7 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) {
utils.KlogDump(2, "custom features configuration:", " ", allFeatureConfig)
// Iterate over features
for _, rule := range allFeatureConfig {
ruleOut, err := rule.execute(domainFeatures)
ruleOut, err := rule.execute(features)
if err != nil {
klog.Error(err)
continue
@ -128,15 +125,15 @@ func (s *customSource) GetLabels() (source.FeatureLabels, error) {
labels[n] = v
}
// Feed back rule output to features map for subsequent rules to match
nfdv1alpha1.InsertAttributeFeatures(domainFeatures, nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels)
nfdv1alpha1.InsertAttributeFeatures(domainFeatures, nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars)
features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels)
features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars)
}
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 {
ruleOut, err := r.LegacyRule.execute(features)
ruleOut, err := r.LegacyRule.execute()
if err != nil {
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")
}
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 {
// Logical OR over the legacy rules
matched := false

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,6 +21,8 @@ package source
import (
"fmt"
"k8s.io/klog/v2"
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
)
@ -38,7 +40,7 @@ type FeatureSource interface {
Discover() error
// GetFeatures returns discovered features in raw form
GetFeatures() *nfdv1alpha1.DomainFeatures
GetFeatures() *nfdv1alpha1.Features
}
// LabelSource represents a source of node feature labels
@ -154,3 +156,37 @@ func GetAllConfigurableSources() map[string]ConfigurableSource {
}
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.
type storageSource struct {
features *nfdv1alpha1.DomainFeatures
features *nfdv1alpha1.Features
}
// Singleton source instance
@ -73,7 +73,7 @@ func (s *storageSource) GetLabels() (source.FeatureLabels, error) {
// Discover method of the FeatureSource interface
func (s *storageSource) Discover() error {
s.features = nfdv1alpha1.NewDomainFeatures()
s.features = nfdv1alpha1.NewFeatures()
devs, err := detectBlock()
if err != nil {
@ -87,9 +87,9 @@ func (s *storageSource) Discover() error {
}
// GetFeatures method of the FeatureSource Interface.
func (s *storageSource) GetFeatures() *nfdv1alpha1.DomainFeatures {
func (s *storageSource) GetFeatures() *nfdv1alpha1.Features {
if s.features == nil {
s.features = nfdv1alpha1.NewDomainFeatures()
s.features = nfdv1alpha1.NewFeatures()
}
return s.features
}

View file

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

View file

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