Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apis/nfd: fix NodeFeatureRule templating #935

Merged
merged 1 commit into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions pkg/apis/nfd/v1alpha1/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ func (r *Rule) executeVarsTemplate(in matchedFeatures, out map[string]string) er
return nil
}

type matchedFeatures map[string]interface{}
type matchedFeatures map[string]domainMatchedFeatures

type domainMatchedFeatures map[string]interface{}

func (e *MatchAnyElem) match(features *Features) (bool, matchedFeatures, error) {
return e.MatchFeatures.match(features)
Expand All @@ -159,23 +161,33 @@ func (m *FeatureMatcher) match(features *Features) (bool, matchedFeatures, error
// Ignore case
featureName := strings.ToLower(term.Feature)

nameSplit := strings.SplitN(term.Feature, ".", 2)
if len(nameSplit) != 2 {
klog.Warning("feature %q not of format <domain>.<feature>, cannot be used for templating", term.Feature)
nameSplit = []string{featureName, ""}
}

if _, ok := matches[nameSplit[0]]; !ok {
matches[nameSplit[0]] = make(domainMatchedFeatures)
}

var isMatch bool
var err error
if f, ok := features.Flags[featureName]; ok {
m, v, e := term.MatchExpressions.MatchGetKeys(f.Elements)
isMatch = m
err = e
matches[featureName] = v
matches[nameSplit[0]][nameSplit[1]] = v
} else if f, ok := features.Attributes[featureName]; ok {
m, v, e := term.MatchExpressions.MatchGetValues(f.Elements)
isMatch = m
err = e
matches[featureName] = v
matches[nameSplit[0]][nameSplit[1]] = v
} else if f, ok := features.Instances[featureName]; ok {
v, e := term.MatchExpressions.MatchGetInstances(f.Elements)
isMatch = len(v) > 0
err = e
matches[featureName] = v
matches[nameSplit[0]][nameSplit[1]] = v
} else {
return false, nil, fmt.Errorf("feature %q not available", featureName)
}
Expand Down
56 changes: 28 additions & 28 deletions pkg/apis/nfd/v1alpha1/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestRule(t *testing.T) {
Vars: map[string]string{"var-1": "var-val-1"},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "kf-1",
Feature: "domain-1.kf-1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchExists),
},
Expand Down Expand Up @@ -58,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
f.Flags["kf-1"] = NewFlagFeatures()
f.Attributes["vf-1"] = NewAttributeFeatures(nil)
f.Instances["if-1"] = NewInstanceFeatures(nil)
f.Flags["domain-1.kf-1"] = NewFlagFeatures()
f.Attributes["domain-1.vf-1"] = NewAttributeFeatures(nil)
f.Instances["domain-1.if-1"] = NewInstanceFeatures(nil)

m, err = r1.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err)
Expand All @@ -71,9 +71,9 @@ func TestRule(t *testing.T) {
assert.Nil(t, m.Labels, "unexpected match")

// Test non-empty feature sets
f.Flags["kf-1"].Elements["key-x"] = Nil{}
f.Attributes["vf-1"].Elements["key-1"] = "val-x"
f.Instances["if-1"] = NewInstanceFeatures([]InstanceFeature{
f.Flags["domain-1.kf-1"].Elements["key-x"] = Nil{}
f.Attributes["domain-1.vf-1"].Elements["key-1"] = "val-x"
f.Instances["domain-1.if-1"] = NewInstanceFeatures([]InstanceFeature{
*NewInstanceFeature(map[string]string{"attr-1": "val-x"})})

m, err = r1.Execute(f)
Expand All @@ -83,7 +83,7 @@ func TestRule(t *testing.T) {
// Test empty MatchExpressions
r1.MatchFeatures = FeatureMatcher{
FeatureMatcherTerm{
Feature: "kf-1",
Feature: "domain-1.kf-1",
MatchExpressions: MatchExpressionSet{},
},
}
Expand All @@ -96,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")

f.Flags["kf-1"].Elements["key-1"] = Nil{}
f.Flags["domain-1.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")
Expand All @@ -107,7 +107,7 @@ func TestRule(t *testing.T) {
Labels: map[string]string{"label-3": "label-val-3", "empty": ""},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "vf-1",
Feature: "domain-1.vf-1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-1"),
},
Expand All @@ -118,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")

f.Attributes["vf-1"].Elements["key-1"] = "val-1"
f.Attributes["domain-1.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")
Expand All @@ -128,7 +128,7 @@ func TestRule(t *testing.T) {
Labels: map[string]string{"label-4": "label-val-4"},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "if-1",
Feature: "domain-1.if-1",
MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchIn, "val-1"),
},
Expand All @@ -139,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")

f.Instances["if-1"].Elements[0].Attributes["attr-1"] = "val-1"
f.Instances["domain-1.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")
Expand All @@ -149,13 +149,13 @@ func TestRule(t *testing.T) {
Labels: map[string]string{"label-5": "label-val-5"},
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "vf-1",
Feature: "domain-1.vf-1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-x"),
},
},
FeatureMatcherTerm{
Feature: "if-1",
Feature: "domain-1.if-1",
MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchIn, "val-1"),
},
Expand All @@ -176,7 +176,7 @@ func TestRule(t *testing.T) {
{
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "kf-1",
Feature: "domain-1.kf-1",
MatchExpressions: MatchExpressionSet{
"key-na": MustCreateMatchExpression(MatchExists),
},
Expand All @@ -192,7 +192,7 @@ func TestRule(t *testing.T) {
MatchAnyElem{
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "kf-1",
Feature: "domain-1.kf-1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchExists),
},
Expand All @@ -208,7 +208,7 @@ func TestRule(t *testing.T) {
func TestTemplating(t *testing.T) {
f := &Features{
Flags: map[string]FlagFeatureSet{
"kf_1": {
"domain_1.kf_1": {
Elements: map[string]Nil{
"key-a": {},
"key-b": {},
Expand All @@ -217,7 +217,7 @@ func TestTemplating(t *testing.T) {
},
},
Attributes: map[string]AttributeFeatureSet{
"vf_1": {
"domain_1.vf_1": {
Elements: map[string]string{
"key-1": "val-1",
"keu-2": "val-2",
Expand All @@ -226,7 +226,7 @@ func TestTemplating(t *testing.T) {
},
},
Instances: map[string]InstanceFeatureSet{
"if_1": {
"domain_1.if_1": {
Elements: []InstanceFeature{
{
Attributes: map[string]string{
Expand Down Expand Up @@ -256,36 +256,36 @@ func TestTemplating(t *testing.T) {
LabelsTemplate: `
label-1=will-be-overridden
label-2=
{{range .kf_1}}kf-{{.Name}}=present
{{range .domain_1.kf_1}}kf-{{.Name}}=present
{{end}}
{{range .vf_1}}vf-{{.Name}}=vf-{{.Value}}
{{range .domain_1.vf_1}}vf-{{.Name}}=vf-{{.Value}}
{{end}}
{{range .if_1}}if-{{index . "attr-1"}}_{{index . "attr-2"}}=present
{{range .domain_1.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 .kf_1}}kf-{{.Name}}=true
{{range .domain_1.kf_1}}kf-{{.Name}}=true
{{end}}`,
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
Feature: "kf_1",
Feature: "domain_1.kf_1",
MatchExpressions: MatchExpressionSet{
"key-a": MustCreateMatchExpression(MatchExists),
"key-c": MustCreateMatchExpression(MatchExists),
"foo": MustCreateMatchExpression(MatchDoesNotExist),
},
},
FeatureMatcherTerm{
Feature: "vf_1",
Feature: "domain_1.vf_1",
MatchExpressions: MatchExpressionSet{
"key-1": MustCreateMatchExpression(MatchIn, "val-1", "val-2"),
"bar": MustCreateMatchExpression(MatchDoesNotExist),
},
},
FeatureMatcherTerm{
Feature: "if_1",
Feature: "domain_1.if_1",
MatchExpressions: MatchExpressionSet{
"attr-1": MustCreateMatchExpression(MatchLt, "100"),
},
Expand Down Expand Up @@ -339,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: "kf_1",
Feature: "domain_1.kf_1",
MatchExpressions: MatchExpressionSet{
"key-a": MustCreateMatchExpression(MatchExists),
},
Expand Down