feat: Allow templating of outcome messages for the JSON/YAML compare analyzers (#1432)

* feat: allow templating of the outcome message for the JSON and YAML Compare analyzers

* Update pkg/analyze/json_compare.go

Co-authored-by: Evans Mungai <evans@replicated.com>
This commit is contained in:
Diamon Wiggins
2024-01-26 10:56:57 -05:00
committed by GitHub
parent 37a5cce8ce
commit 1447e18c56
7 changed files with 196 additions and 4 deletions

View File

@@ -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})

View File

@@ -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
}

View File

@@ -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, <no value>!",
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)
})
}
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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 {