Skip to content

Commit

Permalink
Use Sprig template functions and add asLabelValue function
Browse files Browse the repository at this point in the history
Fixes #2033
  • Loading branch information
fahhem committed Jan 29, 2025
1 parent 837d0cf commit 3ed1d51
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 52 deletions.
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ require (

require (
cel.dev/expr v0.19.0 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
Expand Down Expand Up @@ -88,6 +92,7 @@ require (
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jaypipes/pcidb v1.0.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand All @@ -97,7 +102,9 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
Expand All @@ -114,8 +121,10 @@ require (
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smarty/assertions v1.15.1 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
Expand Down
18 changes: 18 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0=
cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab h1:UKkYhof1njT1/xq4SEg5z+VpTgjmNeHwPGRQl7takDI=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
Expand Down Expand Up @@ -125,6 +133,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jaypipes/ghw v0.13.0 h1:log8MXuB8hzTNnSktqpXMHc0c/2k/WgjOMSUtnI1RV4=
Expand Down Expand Up @@ -165,8 +175,12 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
Expand Down Expand Up @@ -216,6 +230,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smarty/assertions v1.15.1 h1:812oFiXI+G55vxsFf+8bIZ1ux30qtkdqzKbEFwyX3Tk=
Expand All @@ -224,6 +240,8 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
Expand Down
54 changes: 4 additions & 50 deletions pkg/apis/nfd/nodefeaturerule/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ limitations under the License.
package nodefeaturerule

import (
"bytes"
"fmt"
"maps"
"slices"
"strings"
"text/template"

corev1 "k8s.io/api/core/v1"
"k8s.io/klog/v2"
Expand Down Expand Up @@ -188,12 +186,12 @@ func executeLabelsTemplate(r *nfdv1alpha1.Rule, in matchedFeatures, out map[stri
return nil
}

th, err := newTemplateHelper(r.LabelsTemplate)
th, err := utils.NewTemplateHelper(r.LabelsTemplate)
if err != nil {
return fmt.Errorf("failed to parse LabelsTemplate: %w", err)
}

labels, err := th.expandMap(in)
labels, err := th.ExpandMap(in)
if err != nil {
return fmt.Errorf("failed to expand LabelsTemplate: %w", err)
}
Expand All @@ -208,12 +206,12 @@ func executeVarsTemplate(r *nfdv1alpha1.Rule, in matchedFeatures, out map[string
return nil
}

th, err := newTemplateHelper(r.VarsTemplate)
th, err := utils.NewTemplateHelper(r.VarsTemplate)
if err != nil {
return err
}

vars, err := th.expandMap(in)
vars, err := th.ExpandMap(in)
if err != nil {
return err
}
Expand Down Expand Up @@ -309,47 +307,3 @@ func evaluateFeatureMatcher(m *nfdv1alpha1.FeatureMatcher, features *nfdv1alpha1
}
return isMatch, status, nil
}

type templateHelper struct {
template *template.Template
}

func newTemplateHelper(name string) (*templateHelper, error) {
tmpl, err := template.New("").Option("missingkey=error").Parse(name)
if err != nil {
return nil, fmt.Errorf("invalid template: %w", err)
}
return &templateHelper{template: tmpl}, nil
}

func (h *templateHelper) execute(data interface{}) (string, error) {
var tmp bytes.Buffer
if err := h.template.Execute(&tmp, data); err != nil {
return "", err
}
return tmp.String(), nil
}

// expandMap is a helper for expanding a template in to a map of strings. Data
// after executing the template is expexted to be key=value pairs separated by
// newlines.
func (h *templateHelper) expandMap(data interface{}) (map[string]string, error) {
expanded, err := h.execute(data)
if err != nil {
return nil, err
}

// Split out individual key-value pairs
out := make(map[string]string)
for _, item := range strings.Split(expanded, "\n") {
// Remove leading/trailing whitespace and skip empty lines
if trimmed := strings.TrimSpace(item); trimmed != "" {
split := strings.SplitN(trimmed, "=", 2)
if len(split) == 1 {
return nil, fmt.Errorf("missing value in expanded template line %q, (format must be '<key>=<value>')", trimmed)
}
out[split[0]] = split[1]
}
}
return out, nil
}
4 changes: 2 additions & 2 deletions pkg/apis/nfd/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ package validate
import (
"fmt"
"strings"
"text/template"

corev1 "k8s.io/api/core/v1"
k8sQuantity "k8s.io/apimachinery/pkg/api/resource"
k8svalidation "k8s.io/apimachinery/pkg/util/validation"

nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
)

var (
Expand Down Expand Up @@ -72,7 +72,7 @@ func Template(labelsTemplate string) []error {
var validationErr []error

// Validate template
_, err := template.New("").Option("missingkey=error").Parse(labelsTemplate)
_, err := utils.NewTemplateHelper(labelsTemplate)
if err != nil {
validationErr = append(validationErr, fmt.Errorf("invalid template: %w", err))
}
Expand Down
77 changes: 77 additions & 0 deletions pkg/utils/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
Copyright 2025 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package utils

import (
"bytes"
"fmt"
"strings"
"text/template"

"github.com/Masterminds/sprig/v3"
)

type TemplateHelper struct {
template *template.Template
}

func NewTemplateHelper(name string) (*TemplateHelper, error) {
tmpl := template.New("").Funcs(sprig.FuncMap()).Option("missingkey=error")
tmpl = tmpl.Funcs(template.FuncMap{
"asLabelValue": func(s string) string {
// Replace spaces and periods with dashes
return strings.ReplaceAll(strings.ReplaceAll(s, " ", "-"), ".", "-")
},
})
tmpl, err := tmpl.Parse(name)
if err != nil {
return nil, fmt.Errorf("invalid template: %w", err)
}
return &TemplateHelper{template: tmpl}, nil
}

func (h *TemplateHelper) execute(data interface{}) (string, error) {
var tmp bytes.Buffer
if err := h.template.Execute(&tmp, data); err != nil {
return "", err
}
return tmp.String(), nil
}

// ExpandMap is a helper for expanding a template in to a map of strings. Data
// after executing the template is expected to be key=value pairs separated by
// newlines.
func (h *TemplateHelper) ExpandMap(data interface{}) (map[string]string, error) {
expanded, err := h.execute(data)
if err != nil {
return nil, err
}

// Split out individual key-value pairs
out := make(map[string]string)
for _, item := range strings.Split(expanded, "\n") {
// Remove leading/trailing whitespace and skip empty lines
if trimmed := strings.TrimSpace(item); trimmed != "" {
split := strings.SplitN(trimmed, "=", 2)
if len(split) == 1 {
return nil, fmt.Errorf("missing value in expanded template line %q, (format must be '<key>=<value>')", trimmed)
}
out[split[0]] = split[1]
}
}
return out, nil
}

0 comments on commit 3ed1d51

Please sign in to comment.