diff --git a/staging/src/k8s.io/kubectl/pkg/explain/v2/explain.go b/staging/src/k8s.io/kubectl/pkg/explain/v2/explain.go index e88054b6a56..43db73378da 100644 --- a/staging/src/k8s.io/kubectl/pkg/explain/v2/explain.go +++ b/staging/src/k8s.io/kubectl/pkg/explain/v2/explain.go @@ -18,6 +18,7 @@ package v2 import ( "encoding/json" + "errors" "fmt" "io" @@ -85,5 +86,12 @@ func printModelDescriptionWithGenerator( return fmt.Errorf("failed to parse openapi schema for %s: %w", resourcePath, err) } - return generator.Render(outputFormat, parsedV3Schema, gvr, fieldsPath, recursive, w) + err = generator.Render(outputFormat, parsedV3Schema, gvr, fieldsPath, recursive, w) + + explainErr := explainError("") + if errors.As(err, &explainErr) { + return explainErr + } + + return err } diff --git a/staging/src/k8s.io/kubectl/pkg/explain/v2/funcs.go b/staging/src/k8s.io/kubectl/pkg/explain/v2/funcs.go index 60740d8d895..f67a9f4ca36 100644 --- a/staging/src/k8s.io/kubectl/pkg/explain/v2/funcs.go +++ b/staging/src/k8s.io/kubectl/pkg/explain/v2/funcs.go @@ -29,8 +29,18 @@ import ( "k8s.io/kubectl/pkg/util/term" ) +type explainError string + +func (e explainError) Error() string { + return string(e) +} + func WithBuiltinTemplateFuncs(tmpl *template.Template) *template.Template { return tmpl.Funcs(map[string]interface{}{ + "throw": func(e string, args ...any) (string, error) { + errString := fmt.Sprintf(e, args...) + return "", explainError(errString) + }, "toJson": func(obj any) (string, error) { res, err := json.Marshal(obj) return string(res), err diff --git a/staging/src/k8s.io/kubectl/pkg/explain/v2/templates/plaintext.tmpl b/staging/src/k8s.io/kubectl/pkg/explain/v2/templates/plaintext.tmpl index ecd4cb66712..09fe4406ed1 100644 --- a/staging/src/k8s.io/kubectl/pkg/explain/v2/templates/plaintext.tmpl +++ b/staging/src/k8s.io/kubectl/pkg/explain/v2/templates/plaintext.tmpl @@ -19,10 +19,10 @@ {{- with include "schema" (dict "gvk" $gvk "Document" $.Document "FieldPath" $.FieldPath "Recursive" $.Recursive) -}} {{- . -}} {{- else -}} - error: GVK {{$gvk}} not found in OpenAPI schema + {{- throw "error: GVK %v not found in OpenAPI schema" $gvk -}} {{- end -}} {{- else -}} - error: GVR ({{$.GVR.String}}) not found in OpenAPI schema + {{- throw "error: GVR (%v) not found in OpenAPI schema" $.GVR.String -}} {{- end -}} {{- "\n" -}} @@ -43,7 +43,8 @@ Takes dictionary as argument with keys: {{- with include "output" (set $ "schema" .) -}} {{- . -}} {{- else -}} - error: field "{{index $.FieldPath (sub (len $.FieldPath) 1)}}" does not exist + {{- $fieldName := (index $.FieldPath (sub (len $.FieldPath) 1)) -}} + {{- throw "error: field \"%v\" does not exist" $fieldName}} {{- end -}} {{- break -}} {{- end -}} diff --git a/staging/src/k8s.io/kubectl/pkg/explain/v2/templates/plaintext_test.go b/staging/src/k8s.io/kubectl/pkg/explain/v2/templates/plaintext_test.go index 91e68155ed2..f7ab947b473 100644 --- a/staging/src/k8s.io/kubectl/pkg/explain/v2/templates/plaintext_test.go +++ b/staging/src/k8s.io/kubectl/pkg/explain/v2/templates/plaintext_test.go @@ -66,21 +66,21 @@ type testCase struct { } type check interface { - doCheck(output string) error + doCheck(output string, err error) error } type checkError string -func (c checkError) doCheck(output string) error { - if !strings.Contains(output, "error: "+string(c)) { - return fmt.Errorf("expected error: '%v' in string:\n%v", string(c), output) +func (c checkError) doCheck(output string, err error) error { + if !strings.Contains(err.Error(), "error: "+string(c)) { + return fmt.Errorf("expected error: '%v' in string:\n%v", string(c), err) } return nil } type checkContains string -func (c checkContains) doCheck(output string) error { +func (c checkContains) doCheck(output string, err error) error { if !strings.Contains(output, string(c)) { return fmt.Errorf("expected substring: '%v' in string:\n%v", string(c), output) } @@ -89,7 +89,7 @@ func (c checkContains) doCheck(output string) error { type checkEquals string -func (c checkEquals) doCheck(output string) error { +func (c checkEquals) doCheck(output string, err error) error { if output != string(c) { return fmt.Errorf("output is not equal to expectation:\n%v", cmp.Diff(string(c), output)) } @@ -123,7 +123,7 @@ func TestPlaintext(t *testing.T) { Recursive: false, }, Checks: []check{ - checkError("GVR (/, Resource=) not found in OpenAPI schema\n"), + checkError("GVR (/, Resource=) not found in OpenAPI schema"), }, }, { @@ -158,7 +158,7 @@ func TestPlaintext(t *testing.T) { Recursive: false, }, Checks: []check{ - checkError(`field "[does not exist]" does not exist`), + checkError(`field "exist" does not exist`), }, }, { @@ -564,16 +564,17 @@ func TestPlaintext(t *testing.T) { t.Run(testName, func(t *testing.T) { buf := bytes.NewBuffer(nil) + + var outputErr error if len(tcase.Subtemplate) == 0 { - tmpl.Execute(buf, tcase.Context) + outputErr = tmpl.Execute(buf, tcase.Context) } else { - tmpl.ExecuteTemplate(buf, tcase.Subtemplate, tcase.Context) + outputErr = tmpl.ExecuteTemplate(buf, tcase.Subtemplate, tcase.Context) } - require.NoError(t, err) output := buf.String() for _, check := range tcase.Checks { - err = check.doCheck(output) + err = check.doCheck(output, outputErr) if err != nil { t.Log("test failed on output:\n" + output)