mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-03 23:40:03 +00:00 
			
		
		
		
	This fixes #38779. This allows us to avoid case in which printers.GetStandardPrinter returns nil for both printer and err removing any potential panics that may arise throughout kubectl commands. Please see #38779 and #38112 for complete context. Add comment explaining adding handlers to printers.HumanReadablePrinter also remove an unnecessary conversion of printers.HumanReadablePrinter to printers.ResourcePrinter.
		
			
				
	
	
		
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2017 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 (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"reflect"
 | 
						|
 | 
						|
	"k8s.io/apimachinery/pkg/api/meta"
 | 
						|
	"k8s.io/apimachinery/pkg/runtime"
 | 
						|
	"k8s.io/client-go/util/jsonpath"
 | 
						|
)
 | 
						|
 | 
						|
// exists returns true if it would be possible to call the index function
 | 
						|
// with these arguments.
 | 
						|
//
 | 
						|
// TODO: how to document this for users?
 | 
						|
//
 | 
						|
// index returns the result of indexing its first argument by the following
 | 
						|
// arguments.  Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
 | 
						|
// indexed item must be a map, slice, or array.
 | 
						|
func exists(item interface{}, indices ...interface{}) bool {
 | 
						|
	v := reflect.ValueOf(item)
 | 
						|
	for _, i := range indices {
 | 
						|
		index := reflect.ValueOf(i)
 | 
						|
		var isNil bool
 | 
						|
		if v, isNil = indirect(v); isNil {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
		switch v.Kind() {
 | 
						|
		case reflect.Array, reflect.Slice, reflect.String:
 | 
						|
			var x int64
 | 
						|
			switch index.Kind() {
 | 
						|
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
				x = index.Int()
 | 
						|
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | 
						|
				x = int64(index.Uint())
 | 
						|
			default:
 | 
						|
				return false
 | 
						|
			}
 | 
						|
			if x < 0 || x >= int64(v.Len()) {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
			v = v.Index(int(x))
 | 
						|
		case reflect.Map:
 | 
						|
			if !index.IsValid() {
 | 
						|
				index = reflect.Zero(v.Type().Key())
 | 
						|
			}
 | 
						|
			if !index.Type().AssignableTo(v.Type().Key()) {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
			if x := v.MapIndex(index); x.IsValid() {
 | 
						|
				v = x
 | 
						|
			} else {
 | 
						|
				v = reflect.Zero(v.Type().Elem())
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if _, isNil := indirect(v); isNil {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// stolen from text/template
 | 
						|
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
 | 
						|
// We indirect through pointers and empty interfaces (only) because
 | 
						|
// non-empty interfaces have methods we might need.
 | 
						|
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
 | 
						|
	for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
 | 
						|
		if v.IsNil() {
 | 
						|
			return v, true
 | 
						|
		}
 | 
						|
		if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return v, false
 | 
						|
}
 | 
						|
 | 
						|
// JSONPathPrinter is an implementation of ResourcePrinter which formats data with jsonpath expression.
 | 
						|
type JSONPathPrinter struct {
 | 
						|
	rawTemplate string
 | 
						|
	*jsonpath.JSONPath
 | 
						|
}
 | 
						|
 | 
						|
func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
 | 
						|
	j := jsonpath.New("out")
 | 
						|
	if err := j.Parse(tmpl); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return &JSONPathPrinter{tmpl, j}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (j *JSONPathPrinter) AfterPrint(w io.Writer, res string) error {
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// PrintObj formats the obj with the JSONPath Template.
 | 
						|
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
 | 
						|
	var queryObj interface{} = obj
 | 
						|
	if meta.IsListType(obj) {
 | 
						|
		data, err := json.Marshal(obj)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		queryObj = map[string]interface{}{}
 | 
						|
		if err := json.Unmarshal(data, &queryObj); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if unknown, ok := obj.(*runtime.Unknown); ok {
 | 
						|
		data, err := json.Marshal(unknown)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		queryObj = map[string]interface{}{}
 | 
						|
		if err := json.Unmarshal(data, &queryObj); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if unstructured, ok := obj.(runtime.Unstructured); ok {
 | 
						|
		queryObj = unstructured.UnstructuredContent()
 | 
						|
	}
 | 
						|
 | 
						|
	if err := j.JSONPath.Execute(w, queryObj); err != nil {
 | 
						|
		fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err)
 | 
						|
		fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", j.rawTemplate)
 | 
						|
		fmt.Fprintf(w, "\tobject given to jsonpath engine was:\n\t\t%#v\n\n", queryObj)
 | 
						|
		return fmt.Errorf("error executing jsonpath %q: %v\n", j.rawTemplate, err)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// TODO: implement HandledResources()
 | 
						|
func (p *JSONPathPrinter) HandledResources() []string {
 | 
						|
	return []string{}
 | 
						|
}
 | 
						|
 | 
						|
func (p *JSONPathPrinter) IsGeneric() bool {
 | 
						|
	return true
 | 
						|
}
 |