mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
wire through template PrintFlags
This commit is contained in:
parent
90c09c75d6
commit
ec672ca279
@ -16,11 +16,14 @@ go_library(
|
|||||||
"json.go",
|
"json.go",
|
||||||
"json_yaml_flags.go",
|
"json_yaml_flags.go",
|
||||||
"jsonpath.go",
|
"jsonpath.go",
|
||||||
|
"jsonpath_flags.go",
|
||||||
|
"kube_template_flags.go",
|
||||||
"name.go",
|
"name.go",
|
||||||
"name_flags.go",
|
"name_flags.go",
|
||||||
"printers.go",
|
"printers.go",
|
||||||
"tabwriter.go",
|
"tabwriter.go",
|
||||||
"template.go",
|
"template.go",
|
||||||
|
"template_flags.go",
|
||||||
"versioned.go",
|
"versioned.go",
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/pkg/printers",
|
importpath = "k8s.io/kubernetes/pkg/printers",
|
||||||
@ -48,7 +51,9 @@ go_test(
|
|||||||
"customcolumn_flags_test.go",
|
"customcolumn_flags_test.go",
|
||||||
"customcolumn_test.go",
|
"customcolumn_test.go",
|
||||||
"json_yaml_flags_test.go",
|
"json_yaml_flags_test.go",
|
||||||
|
"jsonpath_flags_test.go",
|
||||||
"name_flags_test.go",
|
"name_flags_test.go",
|
||||||
|
"template_flags_test.go",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
":go_default_library",
|
":go_default_library",
|
||||||
|
@ -309,7 +309,7 @@ func TestBadPrinter(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"empty template", &printers.PrintOptions{OutputFormatType: "template", AllowMissingKeys: false}, fmt.Errorf("template format specified but no template given")},
|
{"empty template", &printers.PrintOptions{OutputFormatType: "template", AllowMissingKeys: false}, fmt.Errorf("template format specified but no template given")},
|
||||||
{"bad template", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{ .Name", AllowMissingKeys: false}, fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")},
|
{"bad template", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{ .Name", AllowMissingKeys: false}, fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")},
|
||||||
{"bad templatefile", &printers.PrintOptions{OutputFormatType: "templatefile", AllowMissingKeys: false}, fmt.Errorf("templatefile format specified but no template file given")},
|
{"bad templatefile", &printers.PrintOptions{OutputFormatType: "templatefile", AllowMissingKeys: false}, fmt.Errorf("template format specified but no template given")},
|
||||||
{"bad jsonpath", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.Name", AllowMissingKeys: false}, fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")},
|
{"bad jsonpath", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.Name", AllowMissingKeys: false}, fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")},
|
||||||
{"unknown format", &printers.PrintOptions{OutputFormatType: "anUnknownFormat", OutputFormatArgument: "", AllowMissingKeys: false}, fmt.Errorf("output format \"anUnknownFormat\" not recognized")},
|
{"unknown format", &printers.PrintOptions{OutputFormatType: "anUnknownFormat", OutputFormatArgument: "", AllowMissingKeys: false}, fmt.Errorf("output format \"anUnknownFormat\" not recognized")},
|
||||||
}
|
}
|
||||||
@ -462,7 +462,7 @@ func TestUnknownTypePrinting(t *testing.T) {
|
|||||||
|
|
||||||
func TestTemplatePanic(t *testing.T) {
|
func TestTemplatePanic(t *testing.T) {
|
||||||
tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
|
tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
|
||||||
printer, err := printers.NewTemplatePrinter([]byte(tmpl))
|
printer, err := printers.NewGoTemplatePrinter([]byte(tmpl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tmpl fail: %v", err)
|
t.Fatalf("tmpl fail: %v", err)
|
||||||
}
|
}
|
||||||
@ -618,7 +618,7 @@ func TestTemplateStrings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// The point of this test is to verify that the below template works.
|
// The point of this test is to verify that the below template works.
|
||||||
tmpl := `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}`
|
tmpl := `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}`
|
||||||
p, err := printers.NewTemplatePrinter([]byte(tmpl))
|
p, err := printers.NewGoTemplatePrinter([]byte(tmpl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("tmpl fail: %v", err)
|
t.Fatalf("tmpl fail: %v", err)
|
||||||
}
|
}
|
||||||
@ -652,13 +652,13 @@ func TestPrinters(t *testing.T) {
|
|||||||
jsonpathPrinter printers.ResourcePrinter
|
jsonpathPrinter printers.ResourcePrinter
|
||||||
)
|
)
|
||||||
|
|
||||||
templatePrinter, err = printers.NewTemplatePrinter([]byte("{{.name}}"))
|
templatePrinter, err = printers.NewGoTemplatePrinter([]byte("{{.name}}"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
templatePrinter = printers.NewVersionedPrinter(templatePrinter, legacyscheme.Scheme, legacyscheme.Scheme, v1.SchemeGroupVersion)
|
templatePrinter = printers.NewVersionedPrinter(templatePrinter, legacyscheme.Scheme, legacyscheme.Scheme, v1.SchemeGroupVersion)
|
||||||
|
|
||||||
templatePrinter2, err = printers.NewTemplatePrinter([]byte("{{len .items}}"))
|
templatePrinter2, err = printers.NewGoTemplatePrinter([]byte("{{len .items}}"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
118
pkg/printers/jsonpath_flags.go
Normal file
118
pkg/printers/jsonpath_flags.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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 printers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSONPathPrintFlags provides default flags necessary for template printing.
|
||||||
|
// Given the following flag values, a printer can be requested that knows
|
||||||
|
// how to handle printing based on these values.
|
||||||
|
type JSONPathPrintFlags struct {
|
||||||
|
// indicates if it is OK to ignore missing keys for rendering
|
||||||
|
// an output template.
|
||||||
|
AllowMissingKeys *bool
|
||||||
|
TemplateArgument *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPrinter receives an templateFormat and returns a printer capable of
|
||||||
|
// handling --template format printing.
|
||||||
|
// Returns false if the specified templateFormat does not match a template format.
|
||||||
|
func (f *JSONPathPrintFlags) ToPrinter(templateFormat string) (ResourcePrinter, bool, error) {
|
||||||
|
if (f.TemplateArgument == nil || len(*f.TemplateArgument) == 0) && len(templateFormat) == 0 {
|
||||||
|
return nil, false, fmt.Errorf("missing --template value")
|
||||||
|
}
|
||||||
|
|
||||||
|
templateValue := ""
|
||||||
|
|
||||||
|
// templates are logically optional for specifying a format.
|
||||||
|
// this allows a user to specify a template format value
|
||||||
|
// as --output=jsonpath=
|
||||||
|
templateFormats := map[string]bool{
|
||||||
|
"jsonpath": true,
|
||||||
|
"jsonpath-file": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.TemplateArgument == nil || len(*f.TemplateArgument) == 0 {
|
||||||
|
for format := range templateFormats {
|
||||||
|
format = format + "="
|
||||||
|
if strings.HasPrefix(templateFormat, format) {
|
||||||
|
templateValue = templateFormat[len(format):]
|
||||||
|
templateFormat = format[:len(format)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templateValue = *f.TemplateArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, supportedFormat := templateFormats[templateFormat]; !supportedFormat {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(templateValue) == 0 {
|
||||||
|
return nil, true, fmt.Errorf("template format specified but no template given")
|
||||||
|
}
|
||||||
|
|
||||||
|
if templateFormat == "jsonpath-file" {
|
||||||
|
data, err := ioutil.ReadFile(templateValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, fmt.Errorf("error reading --template %s, %v\n", templateValue, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
templateValue = string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := NewJSONPathPrinter(templateValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, fmt.Errorf("error parsing jsonpath %s, %v\n", templateValue, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allowMissingKeys := true
|
||||||
|
if f.AllowMissingKeys != nil {
|
||||||
|
allowMissingKeys = *f.AllowMissingKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
p.AllowMissingKeys(allowMissingKeys)
|
||||||
|
return p, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlags receives a *cobra.Command reference and binds
|
||||||
|
// flags related to template printing to it
|
||||||
|
func (f *JSONPathPrintFlags) AddFlags(c *cobra.Command) {
|
||||||
|
if f.TemplateArgument != nil {
|
||||||
|
c.Flags().StringVar(f.TemplateArgument, "template", *f.TemplateArgument, "Template string or path to template file to use when --output=jsonpath, --output=jsonpath-file.")
|
||||||
|
c.MarkFlagFilename("template")
|
||||||
|
}
|
||||||
|
if f.AllowMissingKeys != nil {
|
||||||
|
c.Flags().BoolVar(f.AllowMissingKeys, "allow-missing-template-keys", *f.AllowMissingKeys, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewJSONPathPrintFlags returns flags associated with
|
||||||
|
// --template printing, with default values set.
|
||||||
|
func NewJSONPathPrintFlags(templateValue string, allowMissingKeys bool) *JSONPathPrintFlags {
|
||||||
|
return &JSONPathPrintFlags{
|
||||||
|
TemplateArgument: &templateValue,
|
||||||
|
AllowMissingKeys: &allowMissingKeys,
|
||||||
|
}
|
||||||
|
}
|
212
pkg/printers/jsonpath_flags_test.go
Normal file
212
pkg/printers/jsonpath_flags_test.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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 printers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrinterSupportsExpectedJSONPathFormats(t *testing.T) {
|
||||||
|
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||||
|
|
||||||
|
jsonpathFile, err := ioutil.TempFile("", "printers_jsonpath_flags")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
defer func(tempFile *os.File) {
|
||||||
|
tempFile.Close()
|
||||||
|
os.Remove(tempFile.Name())
|
||||||
|
}(jsonpathFile)
|
||||||
|
|
||||||
|
fmt.Fprintf(jsonpathFile, "{ .metadata.name }\n")
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
outputFormat string
|
||||||
|
templateArg string
|
||||||
|
expectedError string
|
||||||
|
expectedParseError string
|
||||||
|
expectedOutput string
|
||||||
|
expectNoMatch bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid output format also containing the jsonpath argument succeeds",
|
||||||
|
outputFormat: "jsonpath={ .metadata.name }",
|
||||||
|
expectedOutput: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid output format and no --template argument results in an error",
|
||||||
|
outputFormat: "jsonpath",
|
||||||
|
expectedError: "template format specified but no template given",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid output format and --template argument succeeds",
|
||||||
|
outputFormat: "jsonpath",
|
||||||
|
templateArg: "{ .metadata.name }",
|
||||||
|
expectedOutput: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jsonpath template file should match, and successfully return correct value",
|
||||||
|
outputFormat: "jsonpath-file",
|
||||||
|
templateArg: jsonpathFile.Name(),
|
||||||
|
expectedOutput: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid output format and invalid --template argument results in a parsing from the printer",
|
||||||
|
outputFormat: "jsonpath",
|
||||||
|
templateArg: "{invalid}",
|
||||||
|
expectedParseError: "unrecognized identifier invalid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no printer is matched on an invalid outputFormat",
|
||||||
|
outputFormat: "invalid",
|
||||||
|
expectNoMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jsonpath printer should not match on any other format supported by another printer",
|
||||||
|
outputFormat: "go-template",
|
||||||
|
expectNoMatch: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
templateArg := &tc.templateArg
|
||||||
|
if len(tc.templateArg) == 0 {
|
||||||
|
templateArg = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
printFlags := printers.JSONPathPrintFlags{
|
||||||
|
TemplateArgument: templateArg,
|
||||||
|
}
|
||||||
|
|
||||||
|
p, matched, err := printFlags.ToPrinter(tc.outputFormat)
|
||||||
|
if tc.expectNoMatch {
|
||||||
|
if matched {
|
||||||
|
t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tc.expectedError) > 0 {
|
||||||
|
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||||
|
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := bytes.NewBuffer([]byte{})
|
||||||
|
err = p.PrintObj(testObject, out)
|
||||||
|
if len(tc.expectedParseError) > 0 {
|
||||||
|
if err == nil || !strings.Contains(err.Error(), tc.expectedParseError) {
|
||||||
|
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(out.String(), tc.expectedOutput) {
|
||||||
|
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONPathPrinterDefaultsAllowMissingKeysToTrue(t *testing.T) {
|
||||||
|
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||||
|
|
||||||
|
allowMissingKeys := false
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
templateArg string
|
||||||
|
expectedOutput string
|
||||||
|
expectedError string
|
||||||
|
allowMissingKeys *bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "existing field does not error and returns expected value",
|
||||||
|
templateArg: "{ .metadata.name }",
|
||||||
|
expectedOutput: "foo",
|
||||||
|
allowMissingKeys: &allowMissingKeys,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing field does not error and returns an empty string since missing keys are allowed by default",
|
||||||
|
templateArg: "{ .metadata.missing }",
|
||||||
|
expectedOutput: "",
|
||||||
|
allowMissingKeys: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing field returns expected error if field is missing and allowMissingKeys is explicitly set to false",
|
||||||
|
templateArg: "{ .metadata.missing }",
|
||||||
|
expectedError: "error executing jsonpath",
|
||||||
|
allowMissingKeys: &allowMissingKeys,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
printFlags := printers.JSONPathPrintFlags{
|
||||||
|
TemplateArgument: &tc.templateArg,
|
||||||
|
AllowMissingKeys: tc.allowMissingKeys,
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFormat := "jsonpath"
|
||||||
|
p, matched, err := printFlags.ToPrinter(outputFormat)
|
||||||
|
if !matched {
|
||||||
|
t.Fatalf("expected to match template printer for output format %q", outputFormat)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := bytes.NewBuffer([]byte{})
|
||||||
|
err = p.PrintObj(testObject, out)
|
||||||
|
|
||||||
|
if len(tc.expectedError) > 0 {
|
||||||
|
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||||
|
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out.String()) != len(tc.expectedOutput) {
|
||||||
|
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
70
pkg/printers/kube_template_flags.go
Normal file
70
pkg/printers/kube_template_flags.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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 printers
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
// KubeTemplatePrintFlags composes print flags that provide both a JSONPath and a go-template printer.
|
||||||
|
// This is necessary if dealing with cases that require support both both printers, since both sets of flags
|
||||||
|
// require overlapping flags.
|
||||||
|
type KubeTemplatePrintFlags struct {
|
||||||
|
*GoTemplatePrintFlags
|
||||||
|
*JSONPathPrintFlags
|
||||||
|
|
||||||
|
AllowMissingKeys *bool
|
||||||
|
TemplateArgument *string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *KubeTemplatePrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, bool, error) {
|
||||||
|
if p, match, err := f.JSONPathPrintFlags.ToPrinter(outputFormat); match {
|
||||||
|
return p, match, err
|
||||||
|
}
|
||||||
|
return f.GoTemplatePrintFlags.ToPrinter(outputFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlags receives a *cobra.Command reference and binds
|
||||||
|
// flags related to template printing to it
|
||||||
|
func (f *KubeTemplatePrintFlags) AddFlags(c *cobra.Command) {
|
||||||
|
if f.TemplateArgument != nil {
|
||||||
|
c.Flags().StringVar(f.TemplateArgument, "template", *f.TemplateArgument, "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
|
||||||
|
c.MarkFlagFilename("template")
|
||||||
|
}
|
||||||
|
if f.AllowMissingKeys != nil {
|
||||||
|
c.Flags().BoolVar(f.AllowMissingKeys, "allow-missing-template-keys", *f.AllowMissingKeys, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKubeTemplatePrintFlags returns flags associated with
|
||||||
|
// --template printing, with default values set.
|
||||||
|
func NewKubeTemplatePrintFlags() *KubeTemplatePrintFlags {
|
||||||
|
allowMissingKeysPtr := true
|
||||||
|
templateArgPtr := ""
|
||||||
|
|
||||||
|
return &KubeTemplatePrintFlags{
|
||||||
|
GoTemplatePrintFlags: &GoTemplatePrintFlags{
|
||||||
|
TemplateArgument: &templateArgPtr,
|
||||||
|
AllowMissingKeys: &allowMissingKeysPtr,
|
||||||
|
},
|
||||||
|
JSONPathPrintFlags: &JSONPathPrintFlags{
|
||||||
|
TemplateArgument: &templateArgPtr,
|
||||||
|
AllowMissingKeys: &allowMissingKeysPtr,
|
||||||
|
},
|
||||||
|
|
||||||
|
TemplateArgument: &templateArgPtr,
|
||||||
|
AllowMissingKeys: &allowMissingKeysPtr,
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,6 @@ package printers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
@ -57,57 +56,28 @@ func GetStandardPrinter(typer runtime.ObjectTyper, encoder runtime.Encoder, deco
|
|||||||
|
|
||||||
printer = namePrinter
|
printer = namePrinter
|
||||||
|
|
||||||
case "template", "go-template":
|
case "template", "go-template", "jsonpath", "templatefile", "go-template-file", "jsonpath-file":
|
||||||
if len(formatArgument) == 0 {
|
// TODO: construct and bind this separately (at the command level)
|
||||||
return nil, fmt.Errorf("template format specified but no template given")
|
kubeTemplateFlags := KubeTemplatePrintFlags{
|
||||||
|
GoTemplatePrintFlags: &GoTemplatePrintFlags{
|
||||||
|
AllowMissingKeys: &allowMissingTemplateKeys,
|
||||||
|
TemplateArgument: &formatArgument,
|
||||||
|
},
|
||||||
|
JSONPathPrintFlags: &JSONPathPrintFlags{
|
||||||
|
AllowMissingKeys: &allowMissingTemplateKeys,
|
||||||
|
TemplateArgument: &formatArgument,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
templatePrinter, err := NewTemplatePrinter([]byte(formatArgument))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error parsing template %s, %v\n", formatArgument, err)
|
|
||||||
}
|
|
||||||
templatePrinter.AllowMissingKeys(allowMissingTemplateKeys)
|
|
||||||
printer = templatePrinter
|
|
||||||
|
|
||||||
case "templatefile", "go-template-file":
|
kubeTemplatePrinter, matched, err := kubeTemplateFlags.ToPrinter(format)
|
||||||
if len(formatArgument) == 0 {
|
if !matched {
|
||||||
return nil, fmt.Errorf("templatefile format specified but no template file given")
|
return nil, fmt.Errorf("unable to match a template printer to handle current print options")
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadFile(formatArgument)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
templatePrinter, err := NewTemplatePrinter(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error parsing template %s, %v\n", string(data), err)
|
|
||||||
}
|
|
||||||
templatePrinter.AllowMissingKeys(allowMissingTemplateKeys)
|
|
||||||
printer = templatePrinter
|
|
||||||
|
|
||||||
case "jsonpath":
|
printer = kubeTemplatePrinter
|
||||||
if len(formatArgument) == 0 {
|
|
||||||
return nil, fmt.Errorf("jsonpath template format specified but no template given")
|
|
||||||
}
|
|
||||||
jsonpathPrinter, err := NewJSONPathPrinter(formatArgument)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error parsing jsonpath %s, %v\n", formatArgument, err)
|
|
||||||
}
|
|
||||||
jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys)
|
|
||||||
printer = jsonpathPrinter
|
|
||||||
|
|
||||||
case "jsonpath-file":
|
|
||||||
if len(formatArgument) == 0 {
|
|
||||||
return nil, fmt.Errorf("jsonpath file format specified but no template file given")
|
|
||||||
}
|
|
||||||
data, err := ioutil.ReadFile(formatArgument)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
|
|
||||||
}
|
|
||||||
jsonpathPrinter, err := NewJSONPathPrinter(string(data))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error parsing template %s, %v\n", string(data), err)
|
|
||||||
}
|
|
||||||
jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys)
|
|
||||||
printer = jsonpathPrinter
|
|
||||||
|
|
||||||
case "custom-columns", "custom-columns-file":
|
case "custom-columns", "custom-columns-file":
|
||||||
customColumnsFlags := &CustomColumnsPrintFlags{
|
customColumnsFlags := &CustomColumnsPrintFlags{
|
||||||
|
@ -26,13 +26,13 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template.
|
// GoTemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template.
|
||||||
type TemplatePrinter struct {
|
type GoTemplatePrinter struct {
|
||||||
rawTemplate string
|
rawTemplate string
|
||||||
template *template.Template
|
template *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
|
func NewGoTemplatePrinter(tmpl []byte) (*GoTemplatePrinter, error) {
|
||||||
t, err := template.New("output").
|
t, err := template.New("output").
|
||||||
Funcs(template.FuncMap{
|
Funcs(template.FuncMap{
|
||||||
"exists": exists,
|
"exists": exists,
|
||||||
@ -42,14 +42,14 @@ func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &TemplatePrinter{
|
return &GoTemplatePrinter{
|
||||||
rawTemplate: string(tmpl),
|
rawTemplate: string(tmpl),
|
||||||
template: t,
|
template: t,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllowMissingKeys tells the template engine if missing keys are allowed.
|
// AllowMissingKeys tells the template engine if missing keys are allowed.
|
||||||
func (p *TemplatePrinter) AllowMissingKeys(allow bool) {
|
func (p *GoTemplatePrinter) AllowMissingKeys(allow bool) {
|
||||||
if allow {
|
if allow {
|
||||||
p.template.Option("missingkey=default")
|
p.template.Option("missingkey=default")
|
||||||
} else {
|
} else {
|
||||||
@ -57,12 +57,12 @@ func (p *TemplatePrinter) AllowMissingKeys(allow bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *TemplatePrinter) AfterPrint(w io.Writer, res string) error {
|
func (p *GoTemplatePrinter) AfterPrint(w io.Writer, res string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintObj formats the obj with the Go Template.
|
// PrintObj formats the obj with the Go Template.
|
||||||
func (p *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
func (p *GoTemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||||
var data []byte
|
var data []byte
|
||||||
var err error
|
var err error
|
||||||
data, err = json.Marshal(obj)
|
data, err = json.Marshal(obj)
|
||||||
@ -88,17 +88,17 @@ func (p *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement HandledResources()
|
// TODO: implement HandledResources()
|
||||||
func (p *TemplatePrinter) HandledResources() []string {
|
func (p *GoTemplatePrinter) HandledResources() []string {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *TemplatePrinter) IsGeneric() bool {
|
func (p *GoTemplatePrinter) IsGeneric() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// safeExecute tries to execute the template, but catches panics and returns an error
|
// safeExecute tries to execute the template, but catches panics and returns an error
|
||||||
// should the template engine panic.
|
// should the template engine panic.
|
||||||
func (p *TemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
|
func (p *GoTemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
|
||||||
var panicErr error
|
var panicErr error
|
||||||
// Sorry for the double anonymous function. There's probably a clever way
|
// Sorry for the double anonymous function. There's probably a clever way
|
||||||
// to do this that has the defer'd func setting the value to be returned, but
|
// to do this that has the defer'd func setting the value to be returned, but
|
||||||
|
123
pkg/printers/template_flags.go
Normal file
123
pkg/printers/template_flags.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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 printers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GoTemplatePrintFlags provides default flags necessary for template printing.
|
||||||
|
// Given the following flag values, a printer can be requested that knows
|
||||||
|
// how to handle printing based on these values.
|
||||||
|
type GoTemplatePrintFlags struct {
|
||||||
|
// indicates if it is OK to ignore missing keys for rendering
|
||||||
|
// an output template.
|
||||||
|
AllowMissingKeys *bool
|
||||||
|
TemplateArgument *string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPrinter receives an templateFormat and returns a printer capable of
|
||||||
|
// handling --template format printing.
|
||||||
|
// Returns false if the specified templateFormat does not match a template format.
|
||||||
|
func (f *GoTemplatePrintFlags) ToPrinter(templateFormat string) (ResourcePrinter, bool, error) {
|
||||||
|
if (f.TemplateArgument == nil || len(*f.TemplateArgument) == 0) && len(templateFormat) == 0 {
|
||||||
|
return nil, false, fmt.Errorf("missing --template argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
templateValue := ""
|
||||||
|
|
||||||
|
// templates are logically optional for specifying a format.
|
||||||
|
// this allows a user to specify a template format value
|
||||||
|
// as --output=go-template=
|
||||||
|
supportedFormats := map[string]bool{
|
||||||
|
"template": true,
|
||||||
|
"go-template": true,
|
||||||
|
"go-template-file": true,
|
||||||
|
"templatefile": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.TemplateArgument == nil || len(*f.TemplateArgument) == 0 {
|
||||||
|
for format := range supportedFormats {
|
||||||
|
format = format + "="
|
||||||
|
if strings.HasPrefix(templateFormat, format) {
|
||||||
|
templateValue = templateFormat[len(format):]
|
||||||
|
templateFormat = format[:len(format)-1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templateValue = *f.TemplateArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, supportedFormat := supportedFormats[templateFormat]; !supportedFormat {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(templateValue) == 0 {
|
||||||
|
return nil, true, fmt.Errorf("template format specified but no template given")
|
||||||
|
}
|
||||||
|
|
||||||
|
if templateFormat == "templatefile" || templateFormat == "go-template-file" {
|
||||||
|
data, err := ioutil.ReadFile(templateValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, fmt.Errorf("error reading --template %s, %v\n", templateValue, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
templateValue = string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := NewGoTemplatePrinter([]byte(templateValue))
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, fmt.Errorf("error parsing template %s, %v\n", templateValue, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allowMissingKeys := true
|
||||||
|
if f.AllowMissingKeys != nil {
|
||||||
|
allowMissingKeys = *f.AllowMissingKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
p.AllowMissingKeys(allowMissingKeys)
|
||||||
|
return p, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlags receives a *cobra.Command reference and binds
|
||||||
|
// flags related to template printing to it
|
||||||
|
func (f *GoTemplatePrintFlags) AddFlags(c *cobra.Command) {
|
||||||
|
if f.TemplateArgument != nil {
|
||||||
|
c.Flags().StringVar(f.TemplateArgument, "template", *f.TemplateArgument, "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
|
||||||
|
c.MarkFlagFilename("template")
|
||||||
|
}
|
||||||
|
if f.AllowMissingKeys != nil {
|
||||||
|
c.Flags().BoolVar(f.AllowMissingKeys, "allow-missing-template-keys", *f.AllowMissingKeys, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGoTemplatePrintFlags returns flags associated with
|
||||||
|
// --template printing, with default values set.
|
||||||
|
func NewGoTemplatePrintFlags() *GoTemplatePrintFlags {
|
||||||
|
allowMissingKeysPtr := true
|
||||||
|
templateValuePtr := ""
|
||||||
|
|
||||||
|
return &GoTemplatePrintFlags{
|
||||||
|
TemplateArgument: &templateValuePtr,
|
||||||
|
AllowMissingKeys: &allowMissingKeysPtr,
|
||||||
|
}
|
||||||
|
}
|
206
pkg/printers/template_flags_test.go
Normal file
206
pkg/printers/template_flags_test.go
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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 printers_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrinterSupportsExpectedTemplateFormats(t *testing.T) {
|
||||||
|
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||||
|
|
||||||
|
templateFile, err := ioutil.TempFile("", "printers_jsonpath_flags")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
defer func(tempFile *os.File) {
|
||||||
|
tempFile.Close()
|
||||||
|
os.Remove(tempFile.Name())
|
||||||
|
}(templateFile)
|
||||||
|
|
||||||
|
fmt.Fprintf(templateFile, "{{ .metadata.name }}")
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
outputFormat string
|
||||||
|
templateArg string
|
||||||
|
expectedError string
|
||||||
|
expectedParseError string
|
||||||
|
expectedOutput string
|
||||||
|
expectNoMatch bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid output format also containing the template argument succeeds",
|
||||||
|
outputFormat: "go-template={{ .metadata.name }}",
|
||||||
|
expectedOutput: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid output format and no template argument results in an error",
|
||||||
|
outputFormat: "template",
|
||||||
|
expectedError: "template format specified but no template given",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid output format and template argument succeeds",
|
||||||
|
outputFormat: "go-template",
|
||||||
|
templateArg: "{{ .metadata.name }}",
|
||||||
|
expectedOutput: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Go-template file should match, and successfully return correct value",
|
||||||
|
outputFormat: "go-template-file",
|
||||||
|
templateArg: templateFile.Name(),
|
||||||
|
expectedOutput: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid output format and invalid template argument results in the templateArg contents as the output",
|
||||||
|
outputFormat: "go-template",
|
||||||
|
templateArg: "invalid",
|
||||||
|
expectedOutput: "invalid",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no printer is matched on an invalid outputFormat",
|
||||||
|
outputFormat: "invalid",
|
||||||
|
expectNoMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "go-template printer should not match on any other format supported by another printer",
|
||||||
|
outputFormat: "jsonpath",
|
||||||
|
expectNoMatch: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
templateArg := &tc.templateArg
|
||||||
|
if len(tc.templateArg) == 0 {
|
||||||
|
templateArg = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
printFlags := printers.GoTemplatePrintFlags{
|
||||||
|
TemplateArgument: templateArg,
|
||||||
|
}
|
||||||
|
|
||||||
|
p, matched, err := printFlags.ToPrinter(tc.outputFormat)
|
||||||
|
if tc.expectNoMatch {
|
||||||
|
if matched {
|
||||||
|
t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tc.expectedError) > 0 {
|
||||||
|
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||||
|
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := bytes.NewBuffer([]byte{})
|
||||||
|
err = p.PrintObj(testObject, out)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out.String()) != len(tc.expectedOutput) {
|
||||||
|
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTemplatePrinterDefaultsAllowMissingKeysToTrue(t *testing.T) {
|
||||||
|
testObject := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||||
|
|
||||||
|
allowMissingKeys := false
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
templateArg string
|
||||||
|
expectedOutput string
|
||||||
|
expectedError string
|
||||||
|
allowMissingKeys *bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "existing field does not error and returns expected value",
|
||||||
|
templateArg: "{{ .metadata.name }}",
|
||||||
|
expectedOutput: "foo",
|
||||||
|
allowMissingKeys: &allowMissingKeys,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing field does not error and returns no value since missing keys are allowed by default",
|
||||||
|
templateArg: "{{ .metadata.missing }}",
|
||||||
|
expectedOutput: "<no value>",
|
||||||
|
allowMissingKeys: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing field returns expected error if field is missing and allowMissingKeys is explicitly set to false",
|
||||||
|
templateArg: "{{ .metadata.missing }}",
|
||||||
|
expectedError: "error executing template",
|
||||||
|
allowMissingKeys: &allowMissingKeys,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
printFlags := printers.GoTemplatePrintFlags{
|
||||||
|
TemplateArgument: &tc.templateArg,
|
||||||
|
AllowMissingKeys: tc.allowMissingKeys,
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFormat := "template"
|
||||||
|
p, matched, err := printFlags.ToPrinter(outputFormat)
|
||||||
|
if !matched {
|
||||||
|
t.Fatalf("expected to match template printer for output format %q", outputFormat)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := bytes.NewBuffer([]byte{})
|
||||||
|
err = p.PrintObj(testObject, out)
|
||||||
|
|
||||||
|
if len(tc.expectedError) > 0 {
|
||||||
|
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||||
|
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out.String()) != len(tc.expectedOutput) {
|
||||||
|
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -53,7 +53,7 @@ func TestTemplate(t *testing.T) {
|
|||||||
for name, test := range testCase {
|
for name, test := range testCase {
|
||||||
buffer := &bytes.Buffer{}
|
buffer := &bytes.Buffer{}
|
||||||
|
|
||||||
p, err := NewTemplatePrinter([]byte(test.template))
|
p, err := NewGoTemplatePrinter([]byte(test.template))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if test.expectErr == nil {
|
if test.expectErr == nil {
|
||||||
t.Errorf("[%s]expected success but got:\n %v\n", name, err)
|
t.Errorf("[%s]expected success but got:\n %v\n", name, err)
|
||||||
|
Loading…
Reference in New Issue
Block a user