improve error reporting for bad templates

This commit is contained in:
Daniel Smith 2014-12-23 11:21:38 -08:00
parent 6916235513
commit 16c624b2e6
4 changed files with 58 additions and 6 deletions

View File

@ -327,7 +327,8 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
// TemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template.
type TemplatePrinter struct {
template *template.Template
rawTemplate string
template *template.Template
}
func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
@ -335,17 +336,27 @@ func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
if err != nil {
return nil, err
}
return &TemplatePrinter{t}, nil
return &TemplatePrinter{string(tmpl), t}, nil
}
// Print parses the data as JSON, and re-formats it with the Go Template.
func (t *TemplatePrinter) Print(data []byte, w io.Writer) error {
obj := map[string]interface{}{}
err := json.Unmarshal(data, &obj)
out := map[string]interface{}{}
err := json.Unmarshal(data, &out)
if err != nil {
return err
}
return t.template.Execute(w, obj)
if err := t.template.Execute(w, out); err != nil {
// It is way easier to debug this stuff when it shows up in
// stdout instead of just stdin. So in addition to returning
// a nice error, also print useful stuff with the writer.
fmt.Fprintf(w, "Error executing template: %v\n", err)
fmt.Fprintf(w, "template was:\n%v\n", t.rawTemplate)
fmt.Fprintf(w, "raw data was:\n%v\n", string(data))
fmt.Fprintf(w, "object given to template engine was:\n%+v\n", out)
return fmt.Errorf("error executing template '%v': '%v'\n----data----\n%#v\n", t.rawTemplate, err, out)
}
return nil
}
// PrintObj formats the obj with the Go Template.

View File

@ -177,3 +177,20 @@ func TestTemplateEmitsVersionedObjects(t *testing.T) {
t.Errorf("Expected %v, got %v", e, a)
}
}
func TestTemplatePanic(t *testing.T) {
tmpl := `{{and ((index .currentState.info "update-demo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
// kind is always blank in memory and set on the wire
printer, err := NewTemplatePrinter([]byte(tmpl))
if err != nil {
t.Fatalf("tmpl fail: %v", err)
}
buffer := &bytes.Buffer{}
err = printer.PrintObj(&api.Pod{}, buffer)
if err == nil {
t.Fatalf("expected that template to crash")
}
if buffer.String() == "" {
t.Errorf("no debugging info was printed")
}
}

View File

@ -417,7 +417,14 @@ func (p *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
return err
}
if err = p.template.Execute(w, out); err != nil {
return fmt.Errorf("error executing template '%v': '%v'\n----data----\n%#v\n", p.rawTemplate, err, out)
// It is way easier to debug this stuff when it shows up in
// stdout instead of just stdin. So in addition to returning
// a nice error, also print useful stuff with the writer.
fmt.Fprintf(w, "Error executing template: %v\n", err)
fmt.Fprintf(w, "template was:\n\t%v\n", p.rawTemplate)
fmt.Fprintf(w, "raw data was:\n\t%v\n", string(data))
fmt.Fprintf(w, "object given to template engine was:\n\t%+v\n", out)
return fmt.Errorf("error executing template '%v': '%v'\n----data----\n%+v\n", p.rawTemplate, err, out)
}
return nil
}

View File

@ -290,6 +290,23 @@ func TestTemplateEmitsVersionedObjects(t *testing.T) {
}
}
func TestTemplatePanic(t *testing.T) {
tmpl := `{{and ((index .currentState.info "update-demo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
// kind is always blank in memory and set on the wire
printer, err := NewTemplatePrinter([]byte(tmpl), testapi.Version(), api.Scheme)
if err != nil {
t.Fatalf("tmpl fail: %v", err)
}
buffer := &bytes.Buffer{}
err = printer.PrintObj(&api.Pod{}, buffer)
if err == nil {
t.Fatalf("expected that template to crash")
}
if buffer.String() == "" {
t.Errorf("no debugging info was printed")
}
}
func TestPrinters(t *testing.T) {
om := func(name string) api.ObjectMeta { return api.ObjectMeta{Name: name} }
templatePrinter, err := NewTemplatePrinter([]byte("{{.name}}"), testapi.Version(), api.Scheme)