Skip to content
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
3 changes: 2 additions & 1 deletion internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"
"text/template"

"github.com/Masterminds/sprig/v3"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
Expand Down Expand Up @@ -94,7 +95,7 @@ func IsInCluster() bool {
// RenderTemplate renders a template and returns the result as a string
func RenderTemplate(tpl string, data interface{}) (string, error) {
// Create a new template and parse the letter into it
t, err := template.New("data").Parse(tpl)
t, err := template.New("data").Funcs(sprig.FuncMap()).Parse(tpl)
if err != nil {
return "", err
}
Expand Down
14 changes: 14 additions & 0 deletions internal/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,20 @@ func TestRenderTemplate(t *testing.T) {
want: "Hello, <no value>!",
wantErr: false,
},
{
name: "template with sprig function works",
tpl: "{{ \"hello \" | upper }}{{ .Name }}",
data: map[string]string{"Name": "World"},
want: "HELLO World",
wantErr: false,
},
{
name: "template with undefined sprig function errors",
tpl: "{{ \"hello \" | upp }}{{ .Name }}",
data: map[string]string{"Name": "World"},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
16 changes: 4 additions & 12 deletions pkg/analyze/cluster_container_statuses.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package analyzer

import (
"bytes"
"encoding/json"
"fmt"
"path/filepath"
"slices"
"strings"
"text/template"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -240,21 +239,14 @@ func renderContainerMessage(message string, info *matchedContainerInfo) string {
if info == nil {
return message
}
out := fmt.Sprintf("Container matched. Container: %s, Namespace: %s, Pod: %s", info.ContainerName, info.Namespace, info.PodName)

tmpl := template.New("container")
msgTmpl, err := tmpl.Parse(message)
if err != nil {
klog.V(2).Infof("failed to parse message template: %v", err)
return out
}
out := fmt.Sprintf("Container matched. Container: %s, Namespace: %s, Pod: %s", info.ContainerName, info.Namespace, info.PodName)

var m bytes.Buffer
err = msgTmpl.Execute(&m, info)
renderedMsg, err := util.RenderTemplate(message, info)
if err != nil {
klog.V(2).Infof("failed to render message template: %v", err)
return out
}

return strings.TrimSpace(m.String())
return strings.TrimSpace(renderedMsg)
}
25 changes: 6 additions & 19 deletions pkg/analyze/cluster_pod_statuses.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package analyzer

import (
"bytes"
"encoding/json"
"fmt"
"path/filepath"
"strings"
"text/template"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
"github.com/replicatedhq/troubleshoot/pkg/k8sutil"
Expand Down Expand Up @@ -190,31 +189,19 @@ func clusterPodStatuses(analyzer *troubleshootv1beta2.ClusterPodStatuses, getChi
pod.Status.Message = "None"
}

tmpl := template.New("pod")

// template the title
titleTmpl, err := tmpl.Parse(r.Title)
if err != nil {
return nil, errors.Wrap(err, "failed to create new title template")
}
var t bytes.Buffer
err = titleTmpl.Execute(&t, pod)
renderedTitle, err := util.RenderTemplate(r.Title, pod)
if err != nil {
return nil, errors.Wrap(err, "failed to execute template")
return nil, errors.Wrap(err, "failed to render template")
}
r.Title = t.String()
r.Title = renderedTitle

// template the message
msgTmpl, err := tmpl.Parse(r.Message)
renderedMsg, err := util.RenderTemplate(r.Message, pod)
if err != nil {
return nil, errors.Wrap(err, "failed to create new title template")
}
var m bytes.Buffer
err = msgTmpl.Execute(&m, pod)
if err != nil {
return nil, errors.Wrap(err, "failed to execute template")
}
r.Message = strings.TrimSpace(m.String())
r.Message = strings.TrimSpace(renderedMsg)

// add to results, break and check the next pod
allResults = append(allResults, &r)
Expand Down
16 changes: 4 additions & 12 deletions pkg/analyze/event.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package analyzer

import (
"bytes"
"encoding/json"
"fmt"
"path"
"regexp"
"strconv"
"strings"
"text/template"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -198,21 +197,14 @@ func decorateMessage(message string, event *corev1.Event) string {
if event == nil {
return message
}

out := fmt.Sprintf("Event matched. Reason: %s Name: %s Message: %s", event.Reason, event.InvolvedObject.Name, event.Message)

tmpl := template.New("event")
msgTmpl, err := tmpl.Parse(message)
renderedMsg, err := util.RenderTemplate(message, event)
if err != nil {
klog.V(2).Infof("failed to parse message template: %v", err)
return out
}

var m bytes.Buffer
err = msgTmpl.Execute(&m, event)
if err != nil {
klog.V(2).Infof("failed to render message template: %v", err)
return out
}

return strings.TrimSpace(m.String())
return strings.TrimSpace(renderedMsg)
}
13 changes: 3 additions & 10 deletions pkg/analyze/host_filesystem_performance.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package analyzer

import (
"bytes"
"encoding/json"
"fmt"
"log"
"strings"
"text/template"
"time"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
Expand Down Expand Up @@ -171,18 +170,12 @@ func doCompareHostFilesystemPerformance(operator string, actual time.Duration, d
}

func renderFSPerfOutcome(outcome string, fsPerf collect.FSPerfResults) string {
t, err := template.New("").Parse(outcome)
if err != nil {
log.Printf("Failed to parse filesystem performance outcome: %v", err)
return outcome
}
var buf bytes.Buffer
err = t.Execute(&buf, fsPerf)
rendered, err := util.RenderTemplate(outcome, fsPerf)
if err != nil {
log.Printf("Failed to render filesystem performance outcome: %v", err)
return outcome
}
return buf.String()
return rendered
}

func (a *AnalyzeHostFilesystemPerformance) analyzeSingleNode(content collectedContent, currentTitle string) ([]*AnalyzeResult, error) {
Expand Down
28 changes: 6 additions & 22 deletions pkg/analyze/host_system_packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import (
"strings"
"unicode"

"text/template"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
Expand Down Expand Up @@ -158,20 +157,12 @@ func compareSystemPackagesConditionalToActual(conditional string, templateMap ma
return true, nil
}

tmpl := template.New("conditional")

conditionalTmpl, err := tmpl.Parse(conditional)
if err != nil {
return false, errors.Wrap(err, "failed to create new when template")
}

var when bytes.Buffer
err = conditionalTmpl.Execute(&when, templateMap)
when, err := util.RenderTemplate(conditional, templateMap)
if err != nil {
return false, errors.Wrap(err, "failed to execute when template")
return false, errors.Wrap(err, "failed to render when template")
}

t, err := strconv.ParseBool(when.String())
t, err := strconv.ParseBool(when)
if err != nil {
return false, errors.Wrap(err, "failed to parse templated when expression as a boolean")
}
Expand Down Expand Up @@ -223,21 +214,14 @@ func (a *AnalyzeHostSystemPackages) analyzeSingleNode(content collectedContent,
continue
}

tmpl := template.New("package")

r.Title = currentTitle

// template the message
msgTmpl, err := tmpl.Parse(r.Message)
renderedMsg, err := util.RenderTemplate(r.Message, templateMap)
if err != nil {
return nil, errors.Wrap(err, "failed to create new message template")
}
var m bytes.Buffer
err = msgTmpl.Execute(&m, templateMap)
if err != nil {
return nil, errors.Wrap(err, "failed to execute message template")
}
r.Message = m.String()
r.Message = renderedMsg

// add to results, break and check the next pod
allResults = append(allResults, &r)
Expand Down
16 changes: 4 additions & 12 deletions pkg/analyze/k8s_node_metrics.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package analyzer

import (
"bytes"
"encoding/json"
"fmt"
"path/filepath"
"regexp"
"strconv"
"strings"
"text/template"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"k8s.io/klog/v2"
kubeletv1alpha1 "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
Expand Down Expand Up @@ -299,18 +298,11 @@ func renderTemplate(tmpMsg string, data any) string {
return tmpMsg
}

t, err := template.New("msg").Parse(tmpMsg)
rendered, err := util.RenderTemplate(tmpMsg, data)
if err != nil {
klog.V(2).Infof("Failed to parse template: %s", err)
klog.V(2).Infof("Failed to render template: %s", err)
return tmpMsg
}

var m bytes.Buffer
err = t.Execute(&m, data)
if err != nil {
klog.V(2).Infof("Failed to execute template: %s", err)
return tmpMsg
}

return m.String()
return rendered
}
28 changes: 9 additions & 19 deletions pkg/analyze/text_analyze.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package analyzer

import (
"bytes"
"fmt"
"path/filepath"
"regexp"
"strconv"
"strings"
"text/template"

"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)

Expand Down Expand Up @@ -210,7 +209,7 @@ func analyzeRegexGroups(pattern string, collected []byte, outcomes []*troublesho

if isMatch {
result.IsFail = true
tplMessage, err := templateRegExGroup(outcome.Fail.Message, foundMatches)
tplMessage, err := util.RenderTemplate(outcome.Fail.Message, foundMatches)
if err != nil {
return result, errors.Wrap(err, "failed to template message in outcome.Fail block")
}
Expand All @@ -227,7 +226,7 @@ func analyzeRegexGroups(pattern string, collected []byte, outcomes []*troublesho

if isMatch {
result.IsWarn = true
tplMessage, err := templateRegExGroup(outcome.Warn.Message, foundMatches)
tplMessage, err := util.RenderTemplate(outcome.Warn.Message, foundMatches)
if err != nil {
return result, errors.Wrap(err, "failed to template message in outcome.Warn block")
}
Expand All @@ -244,7 +243,7 @@ func analyzeRegexGroups(pattern string, collected []byte, outcomes []*troublesho

if isMatch {
result.IsPass = true
tplMessage, err := templateRegExGroup(outcome.Pass.Message, foundMatches)
tplMessage, err := util.RenderTemplate(outcome.Pass.Message, foundMatches)
if err != nil {
return result, errors.Wrap(err, "failed to template message in outcome.Pass block")
}
Expand All @@ -259,20 +258,6 @@ func analyzeRegexGroups(pattern string, collected []byte, outcomes []*troublesho
return result, nil
}

// templateRegExGroup takes a tpl and replaces the variables using matches.
func templateRegExGroup(tpl string, matches map[string]string) (string, error) {
t, err := template.New("").Parse(tpl)
if err != nil {
return "", err
}
var msg bytes.Buffer
err = t.Execute(&msg, matches)
if err != nil {
return "", err
}
return msg.String(), nil
}

func compareRegex(conditional string, foundMatches map[string]string) (bool, error) {
if conditional == "" {
return true, nil
Expand All @@ -287,6 +272,11 @@ func compareRegex(conditional string, foundMatches map[string]string) (bool, err
operator := parts[1]
lookForValue := parts[2]

// handle empty strings
if lookForValue == "''" || lookForValue == `""` {
lookForValue = ""
}

foundValue, ok := foundMatches[lookForMatchName]
if !ok {
// not an error, just wasn't matched
Expand Down
Loading