diff --git a/Makefile b/Makefile index 1c73c6e1..7f0151d3 100644 --- a/Makefile +++ b/Makefile @@ -165,12 +165,8 @@ CONTROLLER_GEN=$(shell which controller-gen) .PHONY: client-gen client-gen: -ifeq (, $(shell which client-gen 2>/dev/null)) go install k8s.io/code-generator/cmd/client-gen@v0.28.2 CLIENT_GEN=$(shell go env GOPATH)/bin/client-gen -else -CLIENT_GEN=$(shell which client-gen) -endif .PHONY: release release: export GITHUB_TOKEN = $(shell echo ${GITHUB_TOKEN_TROUBLESHOOT}) diff --git a/internal/util/util.go b/internal/util/util.go index 1b41394f..f1cde73f 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -1,9 +1,11 @@ package util import ( + "bytes" "net/url" "os" "strings" + "text/template" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -82,3 +84,24 @@ func IsInCluster() bool { return true } + +// 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) + if err != nil { + return "", err + } + + // Create a new buffer + buf := new(bytes.Buffer) + + // Execute the template and write the bytes to the buffer + err = t.Execute(buf, data) + if err != nil { + return "", err + } + + // Return the string representation of the buffer + return buf.String(), nil +} diff --git a/internal/util/util_test.go b/internal/util/util_test.go index b3818457..441134e0 100644 --- a/internal/util/util_test.go +++ b/internal/util/util_test.go @@ -246,3 +246,59 @@ func TestAppend(t *testing.T) { }) } } + +func TestRenderTemplate(t *testing.T) { + tests := []struct { + name string + tpl string + data interface{} + want string + wantErr bool + }{ + { + name: "empty template and data", + tpl: "", + data: nil, + want: "", + wantErr: false, + }, + { + name: "empty template with data", + tpl: "", + data: map[string]string{"Name": "World"}, + want: "", + wantErr: false, + }, + { + name: "empty data with template with no keys", + tpl: "Hello, World!", + data: nil, + want: "Hello, World!", + wantErr: false, + }, + { + name: "simple template", + tpl: "Hello, {{ .Name }}!", + data: map[string]string{"Name": "World"}, + want: "Hello, World!", + wantErr: false, + }, + { + name: "template with missing key", + tpl: "Hello, {{ .Name }}!", + data: map[string]string{"Name2": "World"}, + want: "Hello, !", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := RenderTemplate(tt.tpl, tt.data) + if (err != nil) != tt.wantErr { + t.Errorf("RenderTemplate() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equal(t, tt.want, got, "RenderTemplate() = %v, want %v", got, tt.want) + }) + } +} diff --git a/pkg/analyze/json_compare.go b/pkg/analyze/json_compare.go index 5c83ea16..fd1d77e7 100644 --- a/pkg/analyze/json_compare.go +++ b/pkg/analyze/json_compare.go @@ -10,6 +10,7 @@ import ( "strconv" "github.com/pkg/errors" + util "github.com/replicatedhq/troubleshoot/internal/util" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils" "k8s.io/client-go/util/jsonpath" @@ -54,6 +55,8 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js return nil, errors.Wrap(err, "failed to parse collected data as json") } + originalActual := actual + if analyzer.Path != "" { actual, err = iutils.GetAtPath(actual, analyzer.Path) if err != nil { @@ -112,6 +115,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js } } + outcome.Fail.Message, err = util.RenderTemplate(outcome.Fail.Message, originalActual) + if err != nil { + return nil, errors.Wrap(err, "failed to render template on outcome message") + } + if when == equal { result.IsFail = true result.Message = outcome.Fail.Message @@ -128,6 +136,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js } } + outcome.Warn.Message, err = util.RenderTemplate(outcome.Warn.Message, originalActual) + if err != nil { + return nil, errors.Wrap(err, "failed to render template on outcome message") + } + if when == equal { result.IsWarn = true result.Message = outcome.Warn.Message @@ -144,6 +157,11 @@ func (a *AnalyzeJsonCompare) analyzeJsonCompare(analyzer *troubleshootv1beta2.Js } } + outcome.Pass.Message, err = util.RenderTemplate(outcome.Pass.Message, originalActual) + if err != nil { + return nil, errors.Wrap(err, "failed to render template on outcome message") + } + if when == equal { result.IsPass = true result.Message = outcome.Pass.Message diff --git a/pkg/analyze/json_compare_test.go b/pkg/analyze/json_compare_test.go index 0298aa87..5adfbb3d 100644 --- a/pkg/analyze/json_compare_test.go +++ b/pkg/analyze/json_compare_test.go @@ -756,6 +756,48 @@ func Test_jsonCompare(t *testing.T) { ] }`), }, + { + name: "basic comparison with outcome message templated", + analyzer: troubleshootv1beta2.JsonCompare{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Pass: &troubleshootv1beta2.SingleOutcome{ + Message: "Status: {{ .morestuff.status }}, Info: {{ .morestuff.info }}", + When: "true", + }, + }, + { + Fail: &troubleshootv1beta2.SingleOutcome{ + Message: "Status: {{ .morestuff.status }}, Info: {{ .morestuff.info }}", + When: "false", + }, + }, + }, + CollectorName: "json-compare", + FileName: "json-compare.json", + Path: "morestuff.status", + Value: `"ready"`, + }, + expectResult: AnalyzeResult{ + IsPass: false, + IsWarn: false, + IsFail: true, + Title: "json-compare", + Message: "Status: notready, Info: morestuff is not ready", + IconKey: "kubernetes_text_analyze", + IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg", + }, + fileContents: []byte(`{ + "stuff": { + "status": "ready", + "info": "this stuff is ready" + }, + "morestuff": { + "status": "notready", + "info": "morestuff is not ready" + } + }`), + }, } for _, test := range tests { diff --git a/pkg/analyze/yaml_compare.go b/pkg/analyze/yaml_compare.go index 1a9760bf..18b80672 100644 --- a/pkg/analyze/yaml_compare.go +++ b/pkg/analyze/yaml_compare.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/pkg/errors" + util "github.com/replicatedhq/troubleshoot/internal/util" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" iutils "github.com/replicatedhq/troubleshoot/pkg/interfaceutils" "gopkg.in/yaml.v2" @@ -50,6 +51,8 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya return nil, errors.Wrap(err, "failed to parse collected data as yaml doc") } + originalActual := actual + if analyzer.Path != "" { actual, err = iutils.GetAtPath(actual, analyzer.Path) if err != nil { @@ -81,6 +84,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya } } + outcome.Fail.Message, err = util.RenderTemplate(outcome.Fail.Message, originalActual) + if err != nil { + return nil, errors.Wrap(err, "failed to render template on outcome message") + } + if when == equal { result.IsFail = true result.Message = outcome.Fail.Message @@ -96,6 +104,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya } } + outcome.Warn.Message, err = util.RenderTemplate(outcome.Warn.Message, originalActual) + if err != nil { + return nil, errors.Wrap(err, "failed to render template on outcome message") + } + if when == equal { result.IsWarn = true result.Message = outcome.Warn.Message @@ -111,6 +124,11 @@ func (a *AnalyzeYamlCompare) analyzeYamlCompare(analyzer *troubleshootv1beta2.Ya } } + outcome.Pass.Message, err = util.RenderTemplate(outcome.Pass.Message, originalActual) + if err != nil { + return nil, errors.Wrap(err, "failed to render template on outcome message") + } + if when == equal { result.IsPass = true result.Message = outcome.Pass.Message diff --git a/pkg/analyze/yaml_compare_test.go b/pkg/analyze/yaml_compare_test.go index f7d43941..c22adbf0 100644 --- a/pkg/analyze/yaml_compare_test.go +++ b/pkg/analyze/yaml_compare_test.go @@ -430,6 +430,45 @@ otherstuff: }, fileContents: []byte(``), }, + { + name: "basic comparison with outcome message templated", + analyzer: troubleshootv1beta2.YamlCompare{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Pass: &troubleshootv1beta2.SingleOutcome{ + Message: "Status: {{ .stuff.status }}, Info: {{ .stuff.info }}", + When: "true", + }, + }, + { + Fail: &troubleshootv1beta2.SingleOutcome{ + Message: "Status: {{ .stuff.status }}, Info: {{ .stuff.info }}", + When: "false", + }, + }, + }, + CollectorName: "yaml-compare", + FileName: "yaml-compare.yaml", + Value: `ready`, + Path: "stuff.status", + }, + expectResult: AnalyzeResult{ + IsPass: true, + IsWarn: false, + IsFail: false, + Title: "yaml-compare", + Message: "Status: ready, Info: stuff is ready", + IconKey: "kubernetes_text_analyze", + IconURI: "https://troubleshoot.sh/images/analyzer-icons/text-analyze.svg", + }, + fileContents: []byte(` +stuff: + status: ready + info: stuff is ready +morestuff: + status: notready + info: morestuff is not ready`), + }, } for _, test := range tests {