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

pkg/apis/nfd: support templating of "vars"

Support templating of var names in a similar manner as labels. Add
support for a new 'varsTemplate' field to the feature rule spec which is
treated similarly to the 'labelsTemplate' field. The value of the field
is processed through the golang "text/template" template engine and the
expanded value must contain variables in <key>=<value> format, separated
by newlines i.e.:

  - name: <rule-name>
    varsTemplate: |
      <label-1>=<value-1>
      <label-2>=<value-2>
      ...

Similar rules as for 'labelsTemplate' apply, i.e.

1. In case of matchAny is specified, the template is executed separately
   against each individual matchFeatures matcher.
2. 'vars' field has priority over 'varsTemplate'
This commit is contained in:
Markus Lehtonen 2021-11-23 23:01:22 +02:00
parent f75303ce43
commit b648d005e1
5 changed files with 81 additions and 1 deletions

View file

@ -197,6 +197,12 @@ spec:
more complex rule hierarchies, without exposing intermediary
output values as labels.
type: object
varsTemplate:
description: VarsTemplate specifies a template to expand for
dynamically generating multiple variables. Data (after template
expansion) must be keys with an optional value (<key>[=<value>])
separated by newlines.
type: string
required:
- name
type: object

View file

@ -197,6 +197,12 @@ spec:
more complex rule hierarchies, without exposing intermediary
output values as labels.
type: object
varsTemplate:
description: VarsTemplate specifies a template to expand for
dynamically generating multiple variables. Data (after template
expansion) must be keys with an optional value (<key>[=<value>])
separated by newlines.
type: string
required:
- name
type: object

View file

@ -58,7 +58,9 @@ func (r *Rule) Execute(features feature.Features) (RuleOutput, error) {
if err := r.executeLabelsTemplate(m, labels); err != nil {
return RuleOutput{}, err
}
if err := r.executeVarsTemplate(m, vars); err != nil {
return RuleOutput{}, err
}
}
}
if !matched {
@ -78,6 +80,9 @@ func (r *Rule) Execute(features feature.Features) (RuleOutput, error) {
if err := r.executeLabelsTemplate(m, labels); err != nil {
return RuleOutput{}, err
}
if err := r.executeVarsTemplate(m, vars); err != nil {
return RuleOutput{}, err
}
}
}
@ -117,6 +122,28 @@ func (r *Rule) executeLabelsTemplate(in matchedFeatures, out map[string]string)
return nil
}
func (r *Rule) executeVarsTemplate(in matchedFeatures, out map[string]string) error {
if r.VarsTemplate == "" {
return nil
}
if r.varsTemplate == nil {
t, err := newTemplateHelper(r.VarsTemplate)
if err != nil {
return err
}
r.varsTemplate = t
}
vars, err := r.varsTemplate.expandMap(in)
if err != nil {
return err
}
for k, v := range vars {
out[k] = v
}
return nil
}
type matchedFeatures map[string]domainMatchedFeatures
type domainMatchedFeatures map[string]interface{}

View file

@ -254,6 +254,12 @@ label-2=
{{range .domain_1.vf_1}}vf-{{.Name}}=vf-{{.Value}}
{{end}}
{{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 .domain_1.kf_1}}kf-{{.Name}}=true
{{end}}`,
MatchFeatures: FeatureMatcher{
FeatureMatcherTerm{
@ -297,10 +303,19 @@ label-2=
"if-1_val-2": "present",
"if-10_val-20": "present",
}
expectedVars := map[string]string{
"var-1": "var-val-1",
"var-2": "",
// From template
"kf-key-a": "true",
"kf-key-c": "true",
"kf-foo": "true",
}
m, err := r1.Execute(f)
assert.Nilf(t, err, "unexpected error: %v", err)
assert.Equal(t, expectedLabels, m.Labels, "instances should have matched")
assert.Equal(t, expectedVars, m.Vars, "instances should have matched")
//
// Test error cases
@ -320,6 +335,7 @@ label-2=
m, err = r2.Execute(f)
assert.Nil(t, err)
assert.Equal(t, map[string]string{"foo": "bar"}, m.Labels, "instances should have matched")
assert.Empty(t, m.Vars)
r2.labelsTemplate = nil
r2.LabelsTemplate = "foo"
@ -330,4 +346,23 @@ label-2=
r2.LabelsTemplate = "{{"
_, err = r2.Execute(f)
assert.Error(t, err)
r2.labelsTemplate = nil
r2.LabelsTemplate = ""
r2.VarsTemplate = "bar=baz"
m, err = r2.Execute(f)
assert.Nil(t, err)
assert.Empty(t, m.Labels)
assert.Equal(t, map[string]string{"bar": "baz"}, m.Vars, "instances should have matched")
r2.varsTemplate = nil
r2.VarsTemplate = "bar"
_, err = r2.Execute(f)
assert.Error(t, err)
r2.varsTemplate = nil
r2.VarsTemplate = "{{"
_, err = r2.Execute(f)
assert.Error(t, err)
}

View file

@ -72,6 +72,12 @@ type Rule struct {
// +optional
Vars map[string]string `json:"vars"`
// VarsTemplate specifies a template to expand for dynamically generating
// multiple variables. Data (after template expansion) must be keys with an
// optional value (<key>[=<value>]) separated by newlines.
// +optional
VarsTemplate string `json:"varsTemplate"`
// MatchFeatures specifies a set of matcher terms all of which must match.
// +optional
MatchFeatures FeatureMatcher `json:"matchFeatures"`