...

Source file src/k8s.io/cli-runtime/pkg/printers/jsonpath.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  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  	"reflect"
    25  
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/client-go/util/jsonpath"
    28  )
    29  
    30  // exists returns true if it would be possible to call the index function
    31  // with these arguments.
    32  //
    33  // TODO: how to document this for users?
    34  //
    35  // index returns the result of indexing its first argument by the following
    36  // arguments.  Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
    37  // indexed item must be a map, slice, or array.
    38  func exists(item interface{}, indices ...interface{}) bool {
    39  	v := reflect.ValueOf(item)
    40  	for _, i := range indices {
    41  		index := reflect.ValueOf(i)
    42  		var isNil bool
    43  		if v, isNil = indirect(v); isNil {
    44  			return false
    45  		}
    46  		switch v.Kind() {
    47  		case reflect.Array, reflect.Slice, reflect.String:
    48  			var x int64
    49  			switch index.Kind() {
    50  			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    51  				x = index.Int()
    52  			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    53  				x = int64(index.Uint())
    54  			default:
    55  				return false
    56  			}
    57  			if x < 0 || x >= int64(v.Len()) {
    58  				return false
    59  			}
    60  			v = v.Index(int(x))
    61  		case reflect.Map:
    62  			if !index.IsValid() {
    63  				index = reflect.Zero(v.Type().Key())
    64  			}
    65  			if !index.Type().AssignableTo(v.Type().Key()) {
    66  				return false
    67  			}
    68  			if x := v.MapIndex(index); x.IsValid() {
    69  				v = x
    70  			} else {
    71  				v = reflect.Zero(v.Type().Elem())
    72  			}
    73  		default:
    74  			return false
    75  		}
    76  	}
    77  	if _, isNil := indirect(v); isNil {
    78  		return false
    79  	}
    80  	return true
    81  }
    82  
    83  // stolen from text/template
    84  // indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
    85  // We indirect through pointers and empty interfaces (only) because
    86  // non-empty interfaces have methods we might need.
    87  func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
    88  	for ; v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface; v = v.Elem() {
    89  		if v.IsNil() {
    90  			return v, true
    91  		}
    92  		if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
    93  			break
    94  		}
    95  	}
    96  	return v, false
    97  }
    98  
    99  // JSONPathPrinter is an implementation of ResourcePrinter which formats data with jsonpath expression.
   100  type JSONPathPrinter struct {
   101  	rawTemplate string
   102  	*jsonpath.JSONPath
   103  }
   104  
   105  func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
   106  	j := jsonpath.New("out")
   107  	if err := j.Parse(tmpl); err != nil {
   108  		return nil, err
   109  	}
   110  	return &JSONPathPrinter{
   111  		rawTemplate: tmpl,
   112  		JSONPath:    j,
   113  	}, nil
   114  }
   115  
   116  // PrintObj formats the obj with the JSONPath Template.
   117  func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
   118  	// we use reflect.Indirect here in order to obtain the actual value from a pointer.
   119  	// we need an actual value in order to retrieve the package path for an object.
   120  	// using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
   121  	if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
   122  		return fmt.Errorf(InternalObjectPrinterErr)
   123  	}
   124  
   125  	var queryObj interface{} = obj
   126  	if unstructured, ok := obj.(runtime.Unstructured); ok {
   127  		queryObj = unstructured.UnstructuredContent()
   128  	} else {
   129  		data, err := json.Marshal(obj)
   130  		if err != nil {
   131  			return err
   132  		}
   133  		queryObj = map[string]interface{}{}
   134  		if err := json.Unmarshal(data, &queryObj); err != nil {
   135  			return err
   136  		}
   137  	}
   138  
   139  	if err := j.JSONPath.Execute(w, queryObj); err != nil {
   140  		buf := bytes.NewBuffer(nil)
   141  		fmt.Fprintf(buf, "Error executing template: %v. Printing more information for debugging the template:\n", err)
   142  		fmt.Fprintf(buf, "\ttemplate was:\n\t\t%v\n", j.rawTemplate)
   143  		fmt.Fprintf(buf, "\tobject given to jsonpath engine was:\n\t\t%#v\n\n", queryObj)
   144  		return fmt.Errorf("error executing jsonpath %q: %v\n", j.rawTemplate, buf.String())
   145  	}
   146  	return nil
   147  }
   148  

View as plain text