...

Source file src/k8s.io/cli-runtime/pkg/printers/template.go

Documentation: k8s.io/cli-runtime/pkg/printers

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package printers
    18  
    19  import (
    20  	"encoding/base64"
    21  	"fmt"
    22  	"io"
    23  	"reflect"
    24  	"text/template"
    25  
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apimachinery/pkg/util/json"
    28  )
    29  
    30  // GoTemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template.
    31  type GoTemplatePrinter struct {
    32  	rawTemplate string
    33  	template    *template.Template
    34  }
    35  
    36  func NewGoTemplatePrinter(tmpl []byte) (*GoTemplatePrinter, error) {
    37  	t, err := template.New("output").
    38  		Funcs(template.FuncMap{
    39  			"exists":       exists,
    40  			"base64decode": base64decode,
    41  		}).
    42  		Parse(string(tmpl))
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	return &GoTemplatePrinter{
    47  		rawTemplate: string(tmpl),
    48  		template:    t,
    49  	}, nil
    50  }
    51  
    52  // AllowMissingKeys tells the template engine if missing keys are allowed.
    53  func (p *GoTemplatePrinter) AllowMissingKeys(allow bool) {
    54  	if allow {
    55  		p.template.Option("missingkey=default")
    56  	} else {
    57  		p.template.Option("missingkey=error")
    58  	}
    59  }
    60  
    61  // PrintObj formats the obj with the Go Template.
    62  func (p *GoTemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
    63  	if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
    64  		return fmt.Errorf(InternalObjectPrinterErr)
    65  	}
    66  
    67  	var data []byte
    68  	var err error
    69  	data, err = json.Marshal(obj)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	out := map[string]interface{}{}
    75  	if err := json.Unmarshal(data, &out); err != nil {
    76  		return err
    77  	}
    78  	if err = p.safeExecute(w, out); err != nil {
    79  		// It is way easier to debug this stuff when it shows up in
    80  		// stdout instead of just stdin. So in addition to returning
    81  		// a nice error, also print useful stuff with the writer.
    82  		fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err)
    83  		fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", p.rawTemplate)
    84  		fmt.Fprintf(w, "\traw data was:\n\t\t%v\n", string(data))
    85  		fmt.Fprintf(w, "\tobject given to template engine was:\n\t\t%+v\n\n", out)
    86  		return fmt.Errorf("error executing template %q: %v", p.rawTemplate, err)
    87  	}
    88  	return nil
    89  }
    90  
    91  // safeExecute tries to execute the template, but catches panics and returns an error
    92  // should the template engine panic.
    93  func (p *GoTemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
    94  	var panicErr error
    95  	// Sorry for the double anonymous function. There's probably a clever way
    96  	// to do this that has the defer'd func setting the value to be returned, but
    97  	// that would be even less obvious.
    98  	retErr := func() error {
    99  		defer func() {
   100  			if x := recover(); x != nil {
   101  				panicErr = fmt.Errorf("caught panic: %+v", x)
   102  			}
   103  		}()
   104  		return p.template.Execute(w, obj)
   105  	}()
   106  	if panicErr != nil {
   107  		return panicErr
   108  	}
   109  	return retErr
   110  }
   111  
   112  func base64decode(v string) (string, error) {
   113  	data, err := base64.StdEncoding.DecodeString(v)
   114  	if err != nil {
   115  		return "", fmt.Errorf("base64 decode failed: %v", err)
   116  	}
   117  	return string(data), nil
   118  }
   119  

View as plain text