ToPrinter

// 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) (printers.ResourcePrinter, error) {
	if (f.TemplateArgument == nil || len(*f.TemplateArgument) == 0) && len(templateFormat) == 0 {
		return nil, NoCompatiblePrinterError{Options: f, OutputFormat: &templateFormat}
	}

	templateValue := ""

	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, NoCompatiblePrinterError{OutputFormat: &templateFormat, AllowedFormats: f.AllowedFormats()}
	}

	if len(templateValue) == 0 {
		return nil, 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, fmt.Errorf("error reading --template %s, %v", templateValue, err)
		}

		templateValue = string(data)
	}

	p, err := printers.NewGoTemplatePrinter([]byte(templateValue))
	if err != nil {
		return nil, fmt.Errorf("error parsing template %s, %v", templateValue, err)
	}

	allowMissingKeys := true
	if f.AllowMissingKeys != nil {
		allowMissingKeys = *f.AllowMissingKeys
	}

	p.AllowMissingKeys(allowMissingKeys)
	return p, nil
}


AddFlags

// 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

// NewGoTemplatePrintFlags returns flags associated with
// --template printing, with default values set.
func NewGoTemplatePrintFlags() *GoTemplatePrintFlags {
	allowMissingKeysPtr := true
	templateValuePtr := ""

	return &GoTemplatePrintFlags{
		TemplateArgument: &templateValuePtr,
		AllowMissingKeys: &allowMissingKeysPtr,
	}
}


NewDiscardingPrinter

// NewDiscardingPrinter is a printer that discards all objects
func NewDiscardingPrinter() ResourcePrinterFunc {
	return ResourcePrinterFunc(func(runtime.Object, io.Writer) error {
		return nil
	})
}


PrintObj

// PrintObj implements ResourcePrinter
func (fn ResourcePrinterFunc) PrintObj(obj runtime.Object, w io.Writer) error {
	return fn(obj, w)
}


// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer.
func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
	// we use reflect.Indirect here in order to obtain the actual value from a pointer.
	// we need an actual value in order to retrieve the package path for an object.
	// using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
	if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
		return fmt.Errorf(InternalObjectPrinterErr)
	}

	switch obj := obj.(type) {
	case *metav1.WatchEvent:
		if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj.Object.Object)).Type().PkgPath()) {
			return fmt.Errorf(InternalObjectPrinterErr)
		}
		data, err := json.Marshal(obj)
		if err != nil {
			return err
		}
		_, err = w.Write(data)
		if err != nil {
			return err
		}
		_, err = w.Write([]byte{'\n'})
		return err
	case *runtime.Unknown:
		var buf bytes.Buffer
		err := json.Indent(&buf, obj.Raw, "", "    ")
		if err != nil {
			return err
		}
		buf.WriteRune('\n')
		_, err = buf.WriteTo(w)
		return err
	}

	if obj.GetObjectKind().GroupVersionKind().Empty() {
		return fmt.Errorf("missing apiVersion or kind; try GetObjectKind().SetGroupVersionKind() if you know the type")
	}

	data, err := json.MarshalIndent(obj, "", "    ")
	if err != nil {
		return err
	}
	data = append(data, '\n')
	_, err = w.Write(data)
	return err
}


exists

// 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
}


indirect

// 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.Pointer || 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
}


NewJSONPathPrinter

func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
	j := jsonpath.New("out")
	if err := j.Parse(tmpl); err != nil {
		return nil, err
	}
	return &JSONPathPrinter{
		rawTemplate: tmpl,
		JSONPath:    j,
	}, nil
}


PrintObj

// PrintObj formats the obj with the JSONPath Template.
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
	// we use reflect.Indirect here in order to obtain the actual value from a pointer.
	// we need an actual value in order to retrieve the package path for an object.
	// using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
	if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
		return fmt.Errorf(InternalObjectPrinterErr)
	}

	var queryObj interface{} = obj
	if unstructured, ok := obj.(runtime.Unstructured); ok {
		queryObj = unstructured.UnstructuredContent()
	} else {
		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 err := j.JSONPath.Execute(w, queryObj); err != nil {
		buf := bytes.NewBuffer(nil)
		fmt.Fprintf(buf, "Error executing template: %v. Printing more information for debugging the template:\n", err)
		fmt.Fprintf(buf, "\ttemplate was:\n\t\t%v\n", j.rawTemplate)
		fmt.Fprintf(buf, "\tobject given to jsonpath engine was:\n\t\t%#v\n\n", queryObj)
		return fmt.Errorf("error executing jsonpath %q: %v\n", j.rawTemplate, buf.String())
	}
	return nil
}


omitManagedFields

func omitManagedFields(o runtime.Object) runtime.Object {
	a, err := meta.Accessor(o)
	if err != nil {
		// The object is not a `metav1.Object`, ignore it.
		return o
	}
	a.SetManagedFields(nil)
	return o
}


PrintObj

// PrintObj copies the object and omits the managed fields from the copied object before printing it.
func (p *OmitManagedFieldsPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
	if obj == nil {
		return p.Delegate.PrintObj(obj, w)
	}
	if meta.IsListType(obj) {
		obj = obj.DeepCopyObject()
		_ = meta.EachListItem(obj, func(item runtime.Object) error {
			omitManagedFields(item)
			return nil
		})
	} else if _, err := meta.Accessor(obj); err == nil {
		obj = omitManagedFields(obj.DeepCopyObject())
	}
	return p.Delegate.PrintObj(obj, w)
}


// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object
// and print "resource/name" pair. If the object is a List, print all items in it.
func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
	switch castObj := obj.(type) {
	case *metav1.WatchEvent:
		obj = castObj.Object.Object
	}

	// we use reflect.Indirect here in order to obtain the actual value from a pointer.
	// using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
	// we need an actual value in order to retrieve the package path for an object.
	if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
		return fmt.Errorf(InternalObjectPrinterErr)
	}

	if meta.IsListType(obj) {
		// we allow unstructured lists for now because they always contain the GVK information.  We should chase down
		// callers and stop them from passing unflattened lists
		// TODO chase the caller that is setting this and remove it.
		if _, ok := obj.(*unstructured.UnstructuredList); !ok {
			return fmt.Errorf("list types are not supported by name printing: %T", obj)
		}

		items, err := meta.ExtractList(obj)
		if err != nil {
			return err
		}
		for _, obj := range items {
			if err := p.PrintObj(obj, w); err != nil {
				return err
			}
		}
		return nil
	}

	if obj.GetObjectKind().GroupVersionKind().Empty() {
		return fmt.Errorf("missing apiVersion or kind; try GetObjectKind().SetGroupVersionKind() if you know the type")
	}

	name := "<unknown>"
	if acc, err := meta.Accessor(obj); err == nil {
		if n := acc.GetName(); len(n) > 0 {
			name = n
		}
	}

	return printObj(w, name, p.Operation, p.ShortOutput, GetObjectGroupKind(obj))
}


GetObjectGroupKind

func GetObjectGroupKind(obj runtime.Object) schema.GroupKind {
	if obj == nil {
		return schema.GroupKind{Kind: "<unknown>"}
	}
	groupVersionKind := obj.GetObjectKind().GroupVersionKind()
	if len(groupVersionKind.Kind) > 0 {
		return groupVersionKind.GroupKind()
	}

	if uns, ok := obj.(*unstructured.Unstructured); ok {
		if len(uns.GroupVersionKind().Kind) > 0 {
			return uns.GroupVersionKind().GroupKind()
		}
	}

	return schema.GroupKind{Kind: "<unknown>"}
}


printObj

func printObj(w io.Writer, name string, operation string, shortOutput bool, groupKind schema.GroupKind) error {
	if len(groupKind.Kind) == 0 {
		return fmt.Errorf("missing kind for resource with name %v", name)
	}

	if len(operation) > 0 {
		operation = " " + operation
	}

	if shortOutput {
		operation = ""
	}

	if len(groupKind.Group) == 0 {
		fmt.Fprintf(w, "%s/%s%s\n", strings.ToLower(groupKind.Kind), name, operation)
		return nil
	}

	fmt.Fprintf(w, "%s.%s/%s%s\n", strings.ToLower(groupKind.Kind), groupKind.Group, name, operation)
	return nil
}


IsInternalObjectError

func IsInternalObjectError(err error) bool {
	if err == nil {
		return false
	}

	return err.Error() == InternalObjectPrinterErr
}


IsForbidden

func (c *illegalPackageSourceChecker) IsForbidden(pkgPath string) bool {
	for _, forbiddenPrefix := range c.disallowedPrefixes {
		if strings.HasPrefix(pkgPath, forbiddenPrefix) || strings.Contains(pkgPath, "/vendor/"+forbiddenPrefix) {
			return true
		}
	}

	return false
}


NewTablePrinter

// NewTablePrinter creates a printer suitable for calling PrintObj().
func NewTablePrinter(options PrintOptions) ResourcePrinter {
	printer := &HumanReadablePrinter{
		options: options,
	}
	return printer
}


printHeader

func printHeader(columnNames []string, w io.Writer) error {
	if _, err := fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")); err != nil {
		return err
	}
	return nil
}


PrintObj

// PrintObj prints the obj in a human-friendly format according to the type of the obj.
func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error {

	if _, found := output.(*tabwriter.Writer); !found {
		w := GetNewTabWriter(output)
		output = w
		defer w.Flush()
	}

	var eventType string
	if event, isEvent := obj.(*metav1.WatchEvent); isEvent {
		eventType = event.Type
		obj = event.Object.Object
	}

	// Parameter "obj" is a table from server; print it.
	// display tables following the rules of options
	if table, ok := obj.(*metav1.Table); ok {
		// Do not print headers if this table has no column definitions, or they are the same as the last ones we printed
		localOptions := h.options
		if h.printedHeaders && (len(table.ColumnDefinitions) == 0 || reflect.DeepEqual(table.ColumnDefinitions, h.lastColumns)) {
			localOptions.NoHeaders = true
		}

		if len(table.ColumnDefinitions) == 0 {
			// If this table has no column definitions, use the columns from the last table we printed for decoration and layout.
			// This is done when receiving tables in watch events to save bandwidth.
			table.ColumnDefinitions = h.lastColumns
		} else if !reflect.DeepEqual(table.ColumnDefinitions, h.lastColumns) {
			// If this table has column definitions, remember them for future use.
			h.lastColumns = table.ColumnDefinitions
			h.printedHeaders = false
		}

		if len(table.Rows) > 0 {
			h.printedHeaders = true
		}

		if err := decorateTable(table, localOptions); err != nil {
			return err
		}
		if len(eventType) > 0 {
			if err := addColumns(beginning, table,
				[]metav1.TableColumnDefinition{{Name: "Event", Type: "string"}},
				[]cellValueFunc{func(metav1.TableRow) (interface{}, error) { return formatEventType(eventType), nil }},
			); err != nil {
				return err
			}
		}
		return printTable(table, output, localOptions)
	}

	// Could not find print handler for "obj"; use the default or status print handler.
	// Print with the default or status handler, and use the columns from the last time
	var handler *printHandler
	if _, isStatus := obj.(*metav1.Status); isStatus {
		handler = statusHandlerEntry
	} else {
		handler = defaultHandlerEntry
	}

	includeHeaders := h.lastType != handler && !h.options.NoHeaders

	if h.lastType != nil && h.lastType != handler && !h.options.NoHeaders {
		fmt.Fprintln(output)
	}

	if err := printRowsForHandlerEntry(output, handler, eventType, obj, h.options, includeHeaders); err != nil {
		return err
	}
	h.lastType = handler

	return nil
}


printTable

// printTable prints a table to the provided output respecting the filtering rules for options
// for wide columns and filtered rows. It filters out rows that are Completed. You should call
// decorateTable if you receive a table from a remote server before calling printTable.
func printTable(table *metav1.Table, output io.Writer, options PrintOptions) error {
	if !options.NoHeaders {
		// avoid printing headers if we have no rows to display
		if len(table.Rows) == 0 {
			return nil
		}

		first := true
		for _, column := range table.ColumnDefinitions {
			if !options.Wide && column.Priority != 0 {
				continue
			}
			if first {
				first = false
			} else {
				fmt.Fprint(output, "\t")
			}
			fmt.Fprint(output, strings.ToUpper(column.Name))
		}
		fmt.Fprintln(output)
	}
	for _, row := range table.Rows {
		first := true
		for i, cell := range row.Cells {
			if i >= len(table.ColumnDefinitions) {
				// https://issue.k8s.io/66379
				// don't panic in case of bad output from the server, with more cells than column definitions
				break
			}
			column := table.ColumnDefinitions[i]
			if !options.Wide && column.Priority != 0 {
				continue
			}
			if first {
				first = false
			} else {
				fmt.Fprint(output, "\t")
			}
			if cell != nil {
				switch val := cell.(type) {
				case string:
					print := val
					truncated := false
					// Truncate at the first newline, carriage return or formfeed
					// (treated as a newline by tabwriter).
					breakchar := strings.IndexAny(print, "\f\n\r")
					if breakchar >= 0 {
						truncated = true
						print = print[:breakchar]
					}
					WriteEscaped(output, print)
					if truncated {
						fmt.Fprint(output, "...")
					}
				default:
					WriteEscaped(output, fmt.Sprint(val))
				}
			}
		}
		fmt.Fprintln(output)
	}
	return nil
}


addColumns

func addColumns(pos columnAddPosition, table *metav1.Table, columns []metav1.TableColumnDefinition, valueFuncs []cellValueFunc) error {
	if len(columns) != len(valueFuncs) {
		return fmt.Errorf("cannot prepend columns, unmatched value functions")
	}
	if len(columns) == 0 {
		return nil
	}

	// Compute the new rows
	newRows := make([][]interface{}, len(table.Rows))
	for i := range table.Rows {
		newCells := make([]interface{}, 0, len(columns)+len(table.Rows[i].Cells))

		if pos == end {
			// If we're appending, start with the existing cells,
			// then add nil cells to match the number of columns
			newCells = append(newCells, table.Rows[i].Cells...)
			for len(newCells) < len(table.ColumnDefinitions) {
				newCells = append(newCells, nil)
			}
		}

		// Compute cells for new columns
		for _, f := range valueFuncs {
			newCell, err := f(table.Rows[i])
			if err != nil {
				return err
			}
			newCells = append(newCells, newCell)
		}

		if pos == beginning {
			// If we're prepending, add existing cells
			newCells = append(newCells, table.Rows[i].Cells...)
		}

		// Remember the new cells for this row
		newRows[i] = newCells
	}

	// All cells successfully computed, now replace columns and rows
	newColumns := make([]metav1.TableColumnDefinition, 0, len(columns)+len(table.ColumnDefinitions))
	switch pos {
	case beginning:
		newColumns = append(newColumns, columns...)
		newColumns = append(newColumns, table.ColumnDefinitions...)
	case end:
		newColumns = append(newColumns, table.ColumnDefinitions...)
		newColumns = append(newColumns, columns...)
	default:
		return fmt.Errorf("invalid column add position: %v", pos)
	}
	table.ColumnDefinitions = newColumns
	for i := range table.Rows {
		table.Rows[i].Cells = newRows[i]
	}

	return nil
}


decorateTable

// decorateTable takes a table and attempts to add label columns and the
// namespace column. It will fill empty columns with nil (if the object
// does not expose metadata). It returns an error if the table cannot
// be decorated.
func decorateTable(table *metav1.Table, options PrintOptions) error {
	width := len(table.ColumnDefinitions) + len(options.ColumnLabels)
	if options.WithNamespace {
		width++
	}
	if options.ShowLabels {
		width++
	}

	columns := table.ColumnDefinitions

	nameColumn := -1
	if options.WithKind && !options.Kind.Empty() {
		for i := range columns {
			if columns[i].Format == "name" && columns[i].Type == "string" {
				nameColumn = i
				break
			}
		}
	}

	if width != len(table.ColumnDefinitions) {
		columns = make([]metav1.TableColumnDefinition, 0, width)
		if options.WithNamespace {
			columns = append(columns, metav1.TableColumnDefinition{
				Name: "Namespace",
				Type: "string",
			})
		}
		columns = append(columns, table.ColumnDefinitions...)
		for _, label := range formatLabelHeaders(options.ColumnLabels) {
			columns = append(columns, metav1.TableColumnDefinition{
				Name: label,
				Type: "string",
			})
		}
		if options.ShowLabels {
			columns = append(columns, metav1.TableColumnDefinition{
				Name: "Labels",
				Type: "string",
			})
		}
	}

	rows := table.Rows

	includeLabels := len(options.ColumnLabels) > 0 || options.ShowLabels
	if includeLabels || options.WithNamespace || nameColumn != -1 {
		for i := range rows {
			row := rows[i]

			if nameColumn != -1 {
				row.Cells[nameColumn] = fmt.Sprintf("%s/%s", strings.ToLower(options.Kind.String()), row.Cells[nameColumn])
			}

			var m metav1.Object
			if obj := row.Object.Object; obj != nil {
				if acc, err := meta.Accessor(obj); err == nil {
					m = acc
				}
			}
			// if we can't get an accessor, fill out the appropriate columns with empty spaces
			if m == nil {
				if options.WithNamespace {
					r := make([]interface{}, 1, width)
					row.Cells = append(r, row.Cells...)
				}
				for j := 0; j < width-len(row.Cells); j++ {
					row.Cells = append(row.Cells, nil)
				}
				rows[i] = row
				continue
			}

			if options.WithNamespace {
				r := make([]interface{}, 1, width)
				r[0] = m.GetNamespace()
				row.Cells = append(r, row.Cells...)
			}
			if includeLabels {
				row.Cells = appendLabelCells(row.Cells, m.GetLabels(), options)
			}
			rows[i] = row
		}
	}

	table.ColumnDefinitions = columns
	table.Rows = rows
	return nil
}


printRowsForHandlerEntry

// printRowsForHandlerEntry prints the incremental table output (headers if the current type is
// different from lastType) including all the rows in the object. It returns the current type
// or an error, if any.
func printRowsForHandlerEntry(output io.Writer, handler *printHandler, eventType string, obj runtime.Object, options PrintOptions, includeHeaders bool) error {
	var results []reflect.Value

	args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(options)}
	results = handler.printFunc.Call(args)
	if !results[1].IsNil() {
		return results[1].Interface().(error)
	}

	if includeHeaders {
		var headers []string
		for _, column := range handler.columnDefinitions {
			if column.Priority != 0 && !options.Wide {
				continue
			}
			headers = append(headers, strings.ToUpper(column.Name))
		}
		headers = append(headers, formatLabelHeaders(options.ColumnLabels)...)
		// LABELS is always the last column.
		headers = append(headers, formatShowLabelsHeader(options.ShowLabels)...)
		// prepend namespace header
		if options.WithNamespace {
			headers = append(withNamespacePrefixColumns, headers...)
		}
		// prepend event type header
		if len(eventType) > 0 {
			headers = append(withEventTypePrefixColumns, headers...)
		}
		printHeader(headers, output)
	}

	if results[1].IsNil() {
		rows := results[0].Interface().([]metav1.TableRow)
		printRows(output, eventType, rows, options)
		return nil
	}
	return results[1].Interface().(error)
}


formatEventType

func formatEventType(eventType string) string {
	if formatted, ok := formattedEventType[eventType]; ok {
		return formatted
	}
	return eventType
}


printRows

// printRows writes the provided rows to output.
func printRows(output io.Writer, eventType string, rows []metav1.TableRow, options PrintOptions) {
	for _, row := range rows {
		if len(eventType) > 0 {
			fmt.Fprint(output, formatEventType(eventType))
			fmt.Fprint(output, "\t")
		}
		if options.WithNamespace {
			if obj := row.Object.Object; obj != nil {
				if m, err := meta.Accessor(obj); err == nil {
					fmt.Fprint(output, m.GetNamespace())
				}
			}
			fmt.Fprint(output, "\t")
		}

		for i, cell := range row.Cells {
			if i != 0 {
				fmt.Fprint(output, "\t")
			} else {
				// TODO: remove this once we drop the legacy printers
				if options.WithKind && !options.Kind.Empty() {
					fmt.Fprintf(output, "%s/%s", strings.ToLower(options.Kind.String()), cell)
					continue
				}
			}
			fmt.Fprint(output, cell)
		}

		hasLabels := len(options.ColumnLabels) > 0
		if obj := row.Object.Object; obj != nil && (hasLabels || options.ShowLabels) {
			if m, err := meta.Accessor(obj); err == nil {
				for _, value := range labelValues(m.GetLabels(), options) {
					output.Write([]byte("\t"))
					output.Write([]byte(value))
				}
			}
		}

		output.Write([]byte("\n"))
	}
}


formatLabelHeaders

func formatLabelHeaders(columnLabels []string) []string {
	formHead := make([]string, len(columnLabels))
	for i, l := range columnLabels {
		p := strings.Split(l, "/")
		formHead[i] = strings.ToUpper(p[len(p)-1])
	}
	return formHead
}


formatShowLabelsHeader

// headers for --show-labels=true
func formatShowLabelsHeader(showLabels bool) []string {
	if showLabels {
		return []string{"LABELS"}
	}
	return nil
}


labelValues

// labelValues returns a slice of value columns matching the requested print options.
func labelValues(itemLabels map[string]string, opts PrintOptions) []string {
	var values []string
	for _, key := range opts.ColumnLabels {
		values = append(values, itemLabels[key])
	}
	if opts.ShowLabels {
		values = append(values, labels.FormatLabels(itemLabels))
	}
	return values
}


appendLabelCells

// appendLabelCells returns a slice of value columns matching the requested print options.
// Intended for use with tables.
func appendLabelCells(values []interface{}, itemLabels map[string]string, opts PrintOptions) []interface{} {
	for _, key := range opts.ColumnLabels {
		values = append(values, itemLabels[key])
	}
	if opts.ShowLabels {
		values = append(values, labels.FormatLabels(itemLabels))
	}
	return values
}


printStatus

func printStatus(obj runtime.Object, options PrintOptions) ([]metav1.TableRow, error) {
	status, ok := obj.(*metav1.Status)
	if !ok {
		return nil, fmt.Errorf("expected *v1.Status, got %T", obj)
	}
	return []metav1.TableRow{{
		Object: runtime.RawExtension{Object: obj},
		Cells:  []interface{}{status.Status, status.Reason, status.Message},
	}}, nil
}


printObjectMeta

func printObjectMeta(obj runtime.Object, options PrintOptions) ([]metav1.TableRow, error) {
	if meta.IsListType(obj) {
		rows := make([]metav1.TableRow, 0, 16)
		err := meta.EachListItem(obj, func(obj runtime.Object) error {
			nestedRows, err := printObjectMeta(obj, options)
			if err != nil {
				return err
			}
			rows = append(rows, nestedRows...)
			return nil
		})
		if err != nil {
			return nil, err
		}
		return rows, nil
	}

	rows := make([]metav1.TableRow, 0, 1)
	m, err := meta.Accessor(obj)
	if err != nil {
		return nil, err
	}
	row := metav1.TableRow{
		Object: runtime.RawExtension{Object: obj},
	}
	row.Cells = append(row.Cells, m.GetName(), translateTimestampSince(m.GetCreationTimestamp()))
	rows = append(rows, row)
	return rows, nil
}


translateTimestampSince

// translateTimestampSince returns the elapsed time since timestamp in
// human-readable approximation.
func translateTimestampSince(timestamp metav1.Time) string {
	if timestamp.IsZero() {
		return "<unknown>"
	}

	return duration.HumanDuration(time.Since(timestamp.Time))
}


GetNewTabWriter

// GetNewTabWriter returns a tabwriter that translates tabbed columns in input into properly aligned text.
func GetNewTabWriter(output io.Writer) *tabwriter.Writer {
	return tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags)
}


NewGoTemplatePrinter

func NewGoTemplatePrinter(tmpl []byte) (*GoTemplatePrinter, error) {
	t, err := template.New("output").
		Funcs(template.FuncMap{
			"exists":       exists,
			"base64decode": base64decode,
		}).
		Parse(string(tmpl))
	if err != nil {
		return nil, err
	}
	return &GoTemplatePrinter{
		rawTemplate: string(tmpl),
		template:    t,
	}, nil
}


AllowMissingKeys

// AllowMissingKeys tells the template engine if missing keys are allowed.
func (p *GoTemplatePrinter) AllowMissingKeys(allow bool) {
	if allow {
		p.template.Option("missingkey=default")
	} else {
		p.template.Option("missingkey=error")
	}
}


PrintObj

// PrintObj formats the obj with the Go Template.
func (p *GoTemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
	if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
		return fmt.Errorf(InternalObjectPrinterErr)
	}

	var data []byte
	var err error
	data, err = json.Marshal(obj)
	if err != nil {
		return err
	}

	out := map[string]interface{}{}
	if err := json.Unmarshal(data, &out); err != nil {
		return err
	}
	if err = p.safeExecute(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. Printing more information for debugging the template:\n", err)
		fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", p.rawTemplate)
		fmt.Fprintf(w, "\traw data was:\n\t\t%v\n", string(data))
		fmt.Fprintf(w, "\tobject given to template engine was:\n\t\t%+v\n\n", out)
		return fmt.Errorf("error executing template %q: %v", p.rawTemplate, err)
	}
	return nil
}


safeExecute

// safeExecute tries to execute the template, but catches panics and returns an error
// should the template engine panic.
func (p *GoTemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
	var panicErr error
	// 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
	// that would be even less obvious.
	retErr := func() error {
		defer func() {
			if x := recover(); x != nil {
				panicErr = fmt.Errorf("caught panic: %+v", x)
			}
		}()
		return p.template.Execute(w, obj)
	}()
	if panicErr != nil {
		return panicErr
	}
	return retErr
}


func base64decode(v string) (string, error) {
	data, err := base64.StdEncoding.DecodeString(v)
	if err != nil {
		return "", fmt.Errorf("base64 decode failed: %v", err)
	}
	return string(data), nil
}


WriteEscaped

// WriteEscaped replaces unsafe terminal characters with replacement strings
// and writes them to the given writer.
func WriteEscaped(writer io.Writer, output string) error {
	_, err := terminalEscaper.WriteString(writer, output)
	return err
}


EscapeTerminal

// EscapeTerminal escapes terminal special characters in a human readable (but
// non-reversible) format.
func EscapeTerminal(in string) string {
	return terminalEscaper.Replace(in)
}


NewTypeSetter

// NewTypeSetter constructs a wrapping printer with required params
func NewTypeSetter(typer runtime.ObjectTyper) *TypeSetterPrinter {
	return &TypeSetterPrinter{Typer: typer}
}


PrintObj

// PrintObj is an implementation of ResourcePrinter.PrintObj which sets type information on the obj for the duration
// of printing.  It is NOT threadsafe.
func (p *TypeSetterPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
	if obj == nil {
		return p.Delegate.PrintObj(obj, w)
	}
	if !obj.GetObjectKind().GroupVersionKind().Empty() {
		return p.Delegate.PrintObj(obj, w)
	}

	// we were empty coming in, make sure we're empty going out.  This makes the call thread-unsafe
	defer func() {
		obj.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
	}()

	gvks, _, err := p.Typer.ObjectKinds(obj)
	if err != nil {
		// printers wrapped by us expect to find the type information present
		return fmt.Errorf("missing apiVersion or kind and cannot assign it; %v", err)
	}

	for _, gvk := range gvks {
		if len(gvk.Kind) == 0 {
			continue
		}
		if len(gvk.Version) == 0 || gvk.Version == runtime.APIVersionInternal {
			continue
		}
		obj.GetObjectKind().SetGroupVersionKind(gvk)
		break
	}

	return p.Delegate.PrintObj(obj, w)
}


ToPrinter

// ToPrinter returns a printer (not threadsafe!) that has been wrapped
func (p *TypeSetterPrinter) ToPrinter(delegate ResourcePrinter) ResourcePrinter {
	if p == nil {
		return delegate
	}

	p.Delegate = delegate
	return p
}


WrapToPrinter

// WrapToPrinter wraps the common ToPrinter method
func (p *TypeSetterPrinter) WrapToPrinter(delegate ResourcePrinter, err error) (ResourcePrinter, error) {
	if err != nil {
		return delegate, err
	}
	if p == nil {
		return delegate, nil
	}

	p.Delegate = delegate
	return p, nil
}


NewWarningPrinter

// NewWarningPrinter returns an implementation of warningPrinter that outputs warnings to the specified writer.
func NewWarningPrinter(out io.Writer, opts WarningPrinterOptions) *WarningPrinter {
	h := &WarningPrinter{out: out, opts: opts}
	return h
}


Print

// Print prints warnings to the configured writer.
func (w *WarningPrinter) Print(message string) {
	if w.opts.Color {
		fmt.Fprintf(w.out, "%sWarning:%s %s\n", yellowColor, resetColor, message)
	} else {
		fmt.Fprintf(w.out, "Warning: %s\n", message)
	}
}


PrintObj

// PrintObj prints the data as YAML.
func (p *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
	// we use reflect.Indirect here in order to obtain the actual value from a pointer.
	// we need an actual value in order to retrieve the package path for an object.
	// using reflect.Indirect indiscriminately is valid here, as all runtime.Objects are supposed to be pointers.
	if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
		return fmt.Errorf(InternalObjectPrinterErr)
	}

	count := atomic.AddInt64(&p.printCount, 1)
	if count > 1 {
		if _, err := w.Write([]byte("---\n")); err != nil {
			return err
		}
	}

	switch obj := obj.(type) {
	case *metav1.WatchEvent:
		if InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj.Object.Object)).Type().PkgPath()) {
			return fmt.Errorf(InternalObjectPrinterErr)
		}
		data, err := yaml.Marshal(obj)
		if err != nil {
			return err
		}
		_, err = w.Write(data)
		return err
	case *runtime.Unknown:
		data, err := yaml.JSONToYAML(obj.Raw)
		if err != nil {
			return err
		}
		_, err = w.Write(data)
		return err
	}

	if obj.GetObjectKind().GroupVersionKind().Empty() {
		return fmt.Errorf("missing apiVersion or kind; try GetObjectKind().SetGroupVersionKind() if you know the type")
	}

	output, err := yaml.Marshal(obj)
	if err != nil {
		return err
	}
	_, err = fmt.Fprint(w, string(output))
	return err
}


IsUsageError

// TODO: expand this to include other errors.
func IsUsageError(err error) bool {
	if err == nil {
		return false
	}
	return err == missingResourceError
}


validate

func (o *FilenameOptions) validate() []error {
	var errs []error
	if len(o.Filenames) > 0 && len(o.Kustomize) > 0 {
		errs = append(errs, fmt.Errorf("only one of -f or -k can be specified"))
	}
	if len(o.Kustomize) > 0 && o.Recursive {
		errs = append(errs, fmt.Errorf("the -k flag can't be used with -f or -R"))
	}
	return errs
}


RequireFilenameOrKustomize

func (o *FilenameOptions) RequireFilenameOrKustomize() error {
	if len(o.Filenames) == 0 && len(o.Kustomize) == 0 {
		return fmt.Errorf("must specify one of -f and -k")
	}
	return nil
}


NewFakeBuilder

func NewFakeBuilder(fakeClientFn FakeClientFunc, restMapper RESTMapperFunc, categoryExpander CategoryExpanderFunc) *Builder {
	ret := newBuilder(nil, restMapper, categoryExpander)
	ret.fakeClientFn = fakeClientFn
	return ret
}


newBuilder

// NewBuilder creates a builder that operates on generic objects. At least one of
// internal or unstructured must be specified.
// TODO: Add versioned client (although versioned is still lossy)
// TODO remove internal and unstructured mapper and instead have them set the negotiated serializer for use in the client
func newBuilder(clientConfigFn ClientConfigFunc, restMapper RESTMapperFunc, categoryExpander CategoryExpanderFunc) *Builder {
	return &Builder{
		clientConfigFn:     clientConfigFn,
		restMapperFn:       restMapper,
		categoryExpanderFn: categoryExpander,
		requireObject:      true,
	}
}


ToRESTConfig

func (noopClientGetter) ToRESTConfig() (*rest.Config, error) {
	return nil, fmt.Errorf("local operation only")
}


ToDiscoveryClient

func (noopClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
	return nil, fmt.Errorf("local operation only")
}


ToRESTMapper

func (noopClientGetter) ToRESTMapper() (meta.RESTMapper, error) {
	return nil, fmt.Errorf("local operation only")
}


NewLocalBuilder

// NewLocalBuilder returns a builder that is configured not to create REST clients and avoids asking the server for results.
func NewLocalBuilder() *Builder {
	return NewBuilder(noopClientGetter{}).Local()
}


NewBuilder

func NewBuilder(restClientGetter RESTClientGetter) *Builder {
	categoryExpanderFn := func() (restmapper.CategoryExpander, error) {
		discoveryClient, err := restClientGetter.ToDiscoveryClient()
		if err != nil {
			return nil, err
		}
		return restmapper.NewDiscoveryCategoryExpander(discoveryClient), err
	}

	return newBuilder(
		restClientGetter.ToRESTConfig,
		restClientGetter.ToRESTMapper,
		(&cachingCategoryExpanderFunc{delegate: categoryExpanderFn}).ToCategoryExpander,
	)
}


Schema

func (b *Builder) Schema(schema ContentValidator) *Builder {
	b.schema = schema
	return b
}


AddError

func (b *Builder) AddError(err error) *Builder {
	if err == nil {
		return b
	}
	b.errs = append(b.errs, err)
	return b
}


FilenameParam

// FilenameParam groups input in two categories: URLs and files (files, directories, STDIN)
// If enforceNamespace is false, namespaces in the specs will be allowed to
// override the default namespace. If it is true, namespaces that don't match
// will cause an error.
// If ContinueOnError() is set prior to this method, objects on the path that are not
// recognized will be ignored (but logged at V(2)).
func (b *Builder) FilenameParam(enforceNamespace bool, filenameOptions *FilenameOptions) *Builder {
	if errs := filenameOptions.validate(); len(errs) > 0 {
		b.errs = append(b.errs, errs...)
		return b
	}
	recursive := filenameOptions.Recursive
	paths := filenameOptions.Filenames
	for _, s := range paths {
		switch {
		case s == "-":
			b.Stdin()
		case strings.Index(s, "http://") == 0 || strings.Index(s, "https://") == 0:
			url, err := url.Parse(s)
			if err != nil {
				b.errs = append(b.errs, fmt.Errorf("the URL passed to filename %q is not valid: %v", s, err))
				continue
			}
			b.URL(defaultHttpGetAttempts, url)
		default:
			matches, err := expandIfFilePattern(s)
			if err != nil {
				b.errs = append(b.errs, err)
				continue
			}
			if !recursive && len(matches) == 1 {
				b.singleItemImplied = true
			}
			b.Path(recursive, matches...)
		}
	}
	if filenameOptions.Kustomize != "" {
		b.paths = append(
			b.paths,
			&KustomizeVisitor{
				mapper:  b.mapper,
				dirPath: filenameOptions.Kustomize,
				schema:  b.schema,
				fSys:    filesys.MakeFsOnDisk(),
			})
	}

	if enforceNamespace {
		b.RequireNamespace()
	}

	return b
}


Unstructured

// Unstructured updates the builder so that it will request and send unstructured
// objects. Unstructured objects preserve all fields sent by the server in a map format
// based on the object's JSON structure which means no data is lost when the client
// reads and then writes an object. Use this mode in preference to Internal unless you
// are working with Go types directly.
func (b *Builder) Unstructured() *Builder {
	if b.mapper != nil {
		b.errs = append(b.errs, fmt.Errorf("another mapper was already selected, cannot use unstructured types"))
		return b
	}
	b.objectTyper = unstructuredscheme.NewUnstructuredObjectTyper()
	b.mapper = &mapper{
		localFn:      b.isLocal,
		restMapperFn: b.restMapperFn,
		clientFn:     b.getClient,
		decoder:      &metadataValidatingDecoder{unstructured.UnstructuredJSONScheme},
	}

	return b
}


WithScheme

// WithScheme uses the scheme to manage typing, conversion (optional), and decoding.  If decodingVersions
// is empty, then you can end up with internal types.  You have been warned.
func (b *Builder) WithScheme(scheme *runtime.Scheme, decodingVersions ...schema.GroupVersion) *Builder {
	if b.mapper != nil {
		b.errs = append(b.errs, fmt.Errorf("another mapper was already selected, cannot use internal types"))
		return b
	}
	b.objectTyper = scheme
	codecFactory := serializer.NewCodecFactory(scheme)
	negotiatedSerializer := runtime.NegotiatedSerializer(codecFactory)
	// if you specified versions, you're specifying a desire for external types, which you don't want to round-trip through
	// internal types
	if len(decodingVersions) > 0 {
		negotiatedSerializer = codecFactory.WithoutConversion()
	}
	b.negotiatedSerializer = negotiatedSerializer

	b.mapper = &mapper{
		localFn:      b.isLocal,
		restMapperFn: b.restMapperFn,
		clientFn:     b.getClient,
		decoder:      codecFactory.UniversalDecoder(decodingVersions...),
	}

	return b
}


LocalParam

// LocalParam calls Local() if local is true.
func (b *Builder) LocalParam(local bool) *Builder {
	if local {
		b.Local()
	}
	return b
}


Local

// Local will avoid asking the server for results.
func (b *Builder) Local() *Builder {
	b.local = true
	return b
}


isLocal

func (b *Builder) isLocal() bool {
	return b.local
}


Mapper

// Mapper returns a copy of the current mapper.
func (b *Builder) Mapper() *mapper {
	mapper := *b.mapper
	return &mapper
}


URL

// URL accepts a number of URLs directly.
func (b *Builder) URL(httpAttemptCount int, urls ...*url.URL) *Builder {
	for _, u := range urls {
		b.paths = append(b.paths, &URLVisitor{
			URL:              u,
			StreamVisitor:    NewStreamVisitor(nil, b.mapper, u.String(), b.schema),
			HttpAttemptCount: httpAttemptCount,
		})
	}
	return b
}


Stdin

// Stdin will read objects from the standard input. If ContinueOnError() is set
// prior to this method being called, objects in the stream that are unrecognized
// will be ignored (but logged at V(2)). If StdinInUse() is set prior to this method
// being called, an error will be recorded as there are multiple entities trying to use
// the single standard input stream.
func (b *Builder) Stdin() *Builder {
	b.stream = true
	if b.stdinInUse {
		b.errs = append(b.errs, StdinMultiUseError)
	}
	b.stdinInUse = true
	b.paths = append(b.paths, FileVisitorForSTDIN(b.mapper, b.schema))
	return b
}


StdinInUse

// StdinInUse will mark standard input as in use by this Builder, and therefore standard
// input should not be used by another entity. If Stdin() is set prior to this method
// being called, an error will be recorded as there are multiple entities trying to use
// the single standard input stream.
func (b *Builder) StdinInUse() *Builder {
	if b.stdinInUse {
		b.errs = append(b.errs, StdinMultiUseError)
	}
	b.stdinInUse = true
	return b
}


Stream

// Stream will read objects from the provided reader, and if an error occurs will
// include the name string in the error message. If ContinueOnError() is set
// prior to this method being called, objects in the stream that are unrecognized
// will be ignored (but logged at V(2)).
func (b *Builder) Stream(r io.Reader, name string) *Builder {
	b.stream = true
	b.paths = append(b.paths, NewStreamVisitor(r, b.mapper, name, b.schema))
	return b
}


Path

// Path accepts a set of paths that may be files, directories (all can containing
// one or more resources). Creates a FileVisitor for each file and then each
// FileVisitor is streaming the content to a StreamVisitor. If ContinueOnError() is set
// prior to this method being called, objects on the path that are unrecognized will be
// ignored (but logged at V(2)).
func (b *Builder) Path(recursive bool, paths ...string) *Builder {
	for _, p := range paths {
		_, err := os.Stat(p)
		if os.IsNotExist(err) {
			b.errs = append(b.errs, fmt.Errorf(pathNotExistError, p))
			continue
		}
		if err != nil {
			b.errs = append(b.errs, fmt.Errorf("the path %q cannot be accessed: %v", p, err))
			continue
		}

		visitors, err := ExpandPathsToFileVisitors(b.mapper, p, recursive, FileExtensions, b.schema)
		if err != nil {
			b.errs = append(b.errs, fmt.Errorf("error reading %q: %v", p, err))
		}
		if len(visitors) > 1 {
			b.dir = true
		}

		b.paths = append(b.paths, visitors...)
	}
	if len(b.paths) == 0 && len(b.errs) == 0 {
		b.errs = append(b.errs, fmt.Errorf("error reading %v: recognized file extensions are %v", paths, FileExtensions))
	}
	return b
}


ResourceTypes

// ResourceTypes is a list of types of resources to operate on, when listing objects on
// the server or retrieving objects that match a selector.
func (b *Builder) ResourceTypes(types ...string) *Builder {
	b.resources = append(b.resources, types...)
	return b
}


ResourceNames

// ResourceNames accepts a default type and one or more names, and creates tuples of
// resources
func (b *Builder) ResourceNames(resource string, names ...string) *Builder {
	for _, name := range names {
		// See if this input string is of type/name format
		tuple, ok, err := splitResourceTypeName(name)
		if err != nil {
			b.errs = append(b.errs, err)
			return b
		}

		if ok {
			b.resourceTuples = append(b.resourceTuples, tuple)
			continue
		}
		if len(resource) == 0 {
			b.errs = append(b.errs, fmt.Errorf("the argument %q must be RESOURCE/NAME", name))
			continue
		}

		// Use the given default type to create a resource tuple
		b.resourceTuples = append(b.resourceTuples, resourceTuple{Resource: resource, Name: name})
	}
	return b
}


LabelSelectorParam

// LabelSelectorParam defines a selector that should be applied to the object types to load.
// This will not affect files loaded from disk or URL. If the parameter is empty it is
// a no-op - to select all resources invoke `b.LabelSelector(labels.Everything.String)`.
func (b *Builder) LabelSelectorParam(s string) *Builder {
	selector := strings.TrimSpace(s)
	if len(selector) == 0 {
		return b
	}
	if b.selectAll {
		b.errs = append(b.errs, fmt.Errorf("found non-empty label selector %q with previously set 'all' parameter. ", s))
		return b
	}
	return b.LabelSelector(selector)
}


LabelSelector

// LabelSelector accepts a selector directly and will filter the resulting list by that object.
// Use LabelSelectorParam instead for user input.
func (b *Builder) LabelSelector(selector string) *Builder {
	if len(selector) == 0 {
		return b
	}

	b.labelSelector = &selector
	return b
}


FieldSelectorParam

// FieldSelectorParam defines a selector that should be applied to the object types to load.
// This will not affect files loaded from disk or URL. If the parameter is empty it is
// a no-op - to select all resources.
func (b *Builder) FieldSelectorParam(s string) *Builder {
	s = strings.TrimSpace(s)
	if len(s) == 0 {
		return b
	}
	if b.selectAll {
		b.errs = append(b.errs, fmt.Errorf("found non-empty field selector %q with previously set 'all' parameter. ", s))
		return b
	}
	b.fieldSelector = &s
	return b
}


NamespaceParam

// NamespaceParam accepts the namespace that these resources should be
// considered under from - used by DefaultNamespace() and RequireNamespace()
func (b *Builder) NamespaceParam(namespace string) *Builder {
	b.namespace = namespace
	return b
}


DefaultNamespace

// DefaultNamespace instructs the builder to set the namespace value for any object found
// to NamespaceParam() if empty.
func (b *Builder) DefaultNamespace() *Builder {
	b.defaultNamespace = true
	return b
}


AllNamespaces

// AllNamespaces instructs the builder to metav1.NamespaceAll as a namespace to request resources
// across all of the namespace. This overrides the namespace set by NamespaceParam().
func (b *Builder) AllNamespaces(allNamespace bool) *Builder {
	if allNamespace {
		b.namespace = metav1.NamespaceAll
	}
	b.allNamespace = allNamespace
	return b
}


RequireNamespace

// RequireNamespace instructs the builder to set the namespace value for any object found
// to NamespaceParam() if empty, and if the value on the resource does not match
// NamespaceParam() an error will be returned.
func (b *Builder) RequireNamespace() *Builder {
	b.requireNamespace = true
	return b
}


RequestChunksOf

// RequestChunksOf attempts to load responses from the server in batches of size limit
// to avoid long delays loading and transferring very large lists. If unset defaults to
// no chunking.
func (b *Builder) RequestChunksOf(chunkSize int64) *Builder {
	b.limitChunks = chunkSize
	return b
}


TransformRequests

// TransformRequests alters API calls made by clients requested from this builder. Pass
// an empty list to clear modifiers.
func (b *Builder) TransformRequests(opts ...RequestTransform) *Builder {
	b.requestTransforms = opts
	return b
}


Subresource

// Subresource instructs the builder to retrieve the object at the
// subresource path instead of the main resource path.
func (b *Builder) Subresource(subresource string) *Builder {
	b.subresource = subresource
	return b
}


SelectAllParam

// SelectEverythingParam
func (b *Builder) SelectAllParam(selectAll bool) *Builder {
	if selectAll && (b.labelSelector != nil || b.fieldSelector != nil) {
		b.errs = append(b.errs, fmt.Errorf("setting 'all' parameter but found a non empty selector. "))
		return b
	}
	b.selectAll = selectAll
	return b
}


ResourceTypeOrNameArgs

// ResourceTypeOrNameArgs indicates that the builder should accept arguments
// of the form `(<type1>[,<type2>,...]|<type> <name1>[,<name2>,...])`. When one argument is
// received, the types provided will be retrieved from the server (and be comma delimited).
// When two or more arguments are received, they must be a single type and resource name(s).
// The allowEmptySelector permits to select all the resources (via Everything func).
func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string) *Builder {
	args = normalizeMultipleResourcesArgs(args)
	if ok, err := hasCombinedTypeArgs(args); ok {
		if err != nil {
			b.errs = append(b.errs, err)
			return b
		}
		for _, s := range args {
			tuple, ok, err := splitResourceTypeName(s)
			if err != nil {
				b.errs = append(b.errs, err)
				return b
			}
			if ok {
				b.resourceTuples = append(b.resourceTuples, tuple)
			}
		}
		return b
	}
	if len(args) > 0 {
		// Try replacing aliases only in types
		args[0] = b.ReplaceAliases(args[0])
	}
	switch {
	case len(args) > 2:
		b.names = append(b.names, args[1:]...)
		b.ResourceTypes(SplitResourceArgument(args[0])...)
	case len(args) == 2:
		b.names = append(b.names, args[1])
		b.ResourceTypes(SplitResourceArgument(args[0])...)
	case len(args) == 1:
		b.ResourceTypes(SplitResourceArgument(args[0])...)
		if b.labelSelector == nil && allowEmptySelector {
			selector := labels.Everything().String()
			b.labelSelector = &selector
		}
	case len(args) == 0:
	default:
		b.errs = append(b.errs, fmt.Errorf("arguments must consist of a resource or a resource and name"))
	}
	return b
}


ReplaceAliases

// ReplaceAliases accepts an argument and tries to expand any existing
// aliases found in it
func (b *Builder) ReplaceAliases(input string) string {
	replaced := []string{}
	for _, arg := range strings.Split(input, ",") {
		if b.categoryExpanderFn == nil {
			continue
		}
		categoryExpander, err := b.categoryExpanderFn()
		if err != nil {
			b.AddError(err)
			continue
		}

		if resources, ok := categoryExpander.Expand(arg); ok {
			asStrings := []string{}
			for _, resource := range resources {
				if len(resource.Group) == 0 {
					asStrings = append(asStrings, resource.Resource)
					continue
				}
				asStrings = append(asStrings, resource.Resource+"."+resource.Group)
			}
			arg = strings.Join(asStrings, ",")
		}
		replaced = append(replaced, arg)
	}
	return strings.Join(replaced, ",")
}


hasCombinedTypeArgs

func hasCombinedTypeArgs(args []string) (bool, error) {
	hasSlash := 0
	for _, s := range args {
		if strings.Contains(s, "/") {
			hasSlash++
		}
	}
	switch {
	case hasSlash > 0 && hasSlash == len(args):
		return true, nil
	case hasSlash > 0 && hasSlash != len(args):
		baseCmd := "cmd"
		if len(os.Args) > 0 {
			baseCmdSlice := strings.Split(os.Args[0], "/")
			baseCmd = baseCmdSlice[len(baseCmdSlice)-1]
		}
		return true, fmt.Errorf("there is no need to specify a resource type as a separate argument when passing arguments in resource/name form (e.g. '%s get resource/<resource_name>' instead of '%s get resource resource/<resource_name>'", baseCmd, baseCmd)
	default:
		return false, nil
	}
}


normalizeMultipleResourcesArgs

// Normalize args convert multiple resources to resource tuples, a,b,c d
// as a transform to a/d b/d c/d
func normalizeMultipleResourcesArgs(args []string) []string {
	if len(args) >= 2 {
		resources := []string{}
		resources = append(resources, SplitResourceArgument(args[0])...)
		if len(resources) > 1 {
			names := []string{}
			names = append(names, args[1:]...)
			newArgs := []string{}
			for _, resource := range resources {
				for _, name := range names {
					newArgs = append(newArgs, strings.Join([]string{resource, name}, "/"))
				}
			}
			return newArgs
		}
	}
	return args
}


splitResourceTypeName

// splitResourceTypeName handles type/name resource formats and returns a resource tuple
// (empty or not), whether it successfully found one, and an error
func splitResourceTypeName(s string) (resourceTuple, bool, error) {
	if !strings.Contains(s, "/") {
		return resourceTuple{}, false, nil
	}
	seg := strings.Split(s, "/")
	if len(seg) != 2 {
		return resourceTuple{}, false, fmt.Errorf("arguments in resource/name form may not have more than one slash")
	}
	resource, name := seg[0], seg[1]
	if len(resource) == 0 || len(name) == 0 || len(SplitResourceArgument(resource)) != 1 {
		return resourceTuple{}, false, fmt.Errorf("arguments in resource/name form must have a single resource and name")
	}
	return resourceTuple{Resource: resource, Name: name}, true, nil
}


Flatten

// Flatten will convert any objects with a field named "Items" that is an array of runtime.Object
// compatible types into individual entries and give them their own items. The original object
// is not passed to any visitors.
func (b *Builder) Flatten() *Builder {
	b.flatten = true
	return b
}


Latest

// Latest will fetch the latest copy of any objects loaded from URLs or files from the server.
func (b *Builder) Latest() *Builder {
	b.latest = true
	return b
}


RequireObject

// RequireObject ensures that resulting infos have an object set. If false, resulting info may not have an object set.
func (b *Builder) RequireObject(require bool) *Builder {
	b.requireObject = require
	return b
}


ContinueOnError

// ContinueOnError will attempt to load and visit as many objects as possible, even if some visits
// return errors or some objects cannot be loaded. The default behavior is to terminate after
// the first error is returned from a VisitorFunc.
func (b *Builder) ContinueOnError() *Builder {
	b.continueOnError = true
	return b
}


SingleResourceType

// SingleResourceType will cause the builder to error if the user specifies more than a single type
// of resource.
func (b *Builder) SingleResourceType() *Builder {
	b.singleResourceType = true
	return b
}


mappingFor

// mappingFor returns the RESTMapping for the Kind given, or the Kind referenced by the resource.
// Prefers a fully specified GroupVersionResource match. If one is not found, we match on a fully
// specified GroupVersionKind, or fallback to a match on GroupKind.
func (b *Builder) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, error) {
	fullySpecifiedGVR, groupResource := schema.ParseResourceArg(resourceOrKindArg)
	gvk := schema.GroupVersionKind{}
	restMapper, err := b.restMapperFn()
	if err != nil {
		return nil, err
	}

	if fullySpecifiedGVR != nil {
		gvk, _ = restMapper.KindFor(*fullySpecifiedGVR)
	}
	if gvk.Empty() {
		gvk, _ = restMapper.KindFor(groupResource.WithVersion(""))
	}
	if !gvk.Empty() {
		return restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
	}

	fullySpecifiedGVK, groupKind := schema.ParseKindArg(resourceOrKindArg)
	if fullySpecifiedGVK == nil {
		gvk := groupKind.WithVersion("")
		fullySpecifiedGVK = &gvk
	}

	if !fullySpecifiedGVK.Empty() {
		if mapping, err := restMapper.RESTMapping(fullySpecifiedGVK.GroupKind(), fullySpecifiedGVK.Version); err == nil {
			return mapping, nil
		}
	}

	mapping, err := restMapper.RESTMapping(groupKind, gvk.Version)
	if err != nil {
		// if we error out here, it is because we could not match a resource or a kind
		// for the given argument. To maintain consistency with previous behavior,
		// announce that a resource type could not be found.
		// if the error is _not_ a *meta.NoKindMatchError, then we had trouble doing discovery,
		// so we should return the original error since it may help a user diagnose what is actually wrong
		if meta.IsNoMatchError(err) {
			return nil, fmt.Errorf("the server doesn't have a resource type %q", groupResource.Resource)
		}
		return nil, err
	}

	return mapping, nil
}


resourceMappings

func (b *Builder) resourceMappings() ([]*meta.RESTMapping, error) {
	if len(b.resources) > 1 && b.singleResourceType {
		return nil, fmt.Errorf("you may only specify a single resource type")
	}
	mappings := []*meta.RESTMapping{}
	seen := map[schema.GroupVersionKind]bool{}
	for _, r := range b.resources {
		mapping, err := b.mappingFor(r)
		if err != nil {
			return nil, err
		}
		// This ensures the mappings for resources(shortcuts, plural) unique
		if seen[mapping.GroupVersionKind] {
			continue
		}
		seen[mapping.GroupVersionKind] = true

		mappings = append(mappings, mapping)
	}
	return mappings, nil
}


resourceTupleMappings

func (b *Builder) resourceTupleMappings() (map[string]*meta.RESTMapping, error) {
	mappings := make(map[string]*meta.RESTMapping)
	canonical := make(map[schema.GroupVersionResource]struct{})
	for _, r := range b.resourceTuples {
		if _, ok := mappings[r.Resource]; ok {
			continue
		}
		mapping, err := b.mappingFor(r.Resource)
		if err != nil {
			return nil, err
		}

		mappings[r.Resource] = mapping
		canonical[mapping.Resource] = struct{}{}
	}
	if len(canonical) > 1 && b.singleResourceType {
		return nil, fmt.Errorf("you may only specify a single resource type")
	}
	return mappings, nil
}


visitorResult

func (b *Builder) visitorResult() *Result {
	if len(b.errs) > 0 {
		return &Result{err: utilerrors.NewAggregate(b.errs)}
	}

	if b.selectAll {
		selector := labels.Everything().String()
		b.labelSelector = &selector
	}

	// visit items specified by paths
	if len(b.paths) != 0 {
		return b.visitByPaths()
	}

	// visit selectors
	if b.labelSelector != nil || b.fieldSelector != nil {
		return b.visitBySelector()
	}

	// visit items specified by resource and name
	if len(b.resourceTuples) != 0 {
		return b.visitByResource()
	}

	// visit items specified by name
	if len(b.names) != 0 {
		return b.visitByName()
	}

	if len(b.resources) != 0 {
		for _, r := range b.resources {
			_, err := b.mappingFor(r)
			if err != nil {
				return &Result{err: err}
			}
		}
		return &Result{err: fmt.Errorf("resource(s) were provided, but no name was specified")}
	}
	return &Result{err: missingResourceError}
}


visitBySelector

func (b *Builder) visitBySelector() *Result {
	result := &Result{
		targetsSingleItems: false,
	}

	if len(b.names) != 0 {
		return result.withError(fmt.Errorf("name cannot be provided when a selector is specified"))
	}
	if len(b.resourceTuples) != 0 {
		return result.withError(fmt.Errorf("selectors and the all flag cannot be used when passing resource/name arguments"))
	}
	if len(b.resources) == 0 {
		return result.withError(fmt.Errorf("at least one resource must be specified to use a selector"))
	}
	if len(b.subresource) != 0 {
		return result.withError(fmt.Errorf("subresource cannot be used when bulk resources are specified"))
	}

	mappings, err := b.resourceMappings()
	if err != nil {
		result.err = err
		return result
	}

	var labelSelector, fieldSelector string
	if b.labelSelector != nil {
		labelSelector = *b.labelSelector
	}
	if b.fieldSelector != nil {
		fieldSelector = *b.fieldSelector
	}

	visitors := []Visitor{}
	for _, mapping := range mappings {
		client, err := b.getClient(mapping.GroupVersionKind.GroupVersion())
		if err != nil {
			result.err = err
			return result
		}
		selectorNamespace := b.namespace
		if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
			selectorNamespace = ""
		}
		visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, labelSelector, fieldSelector, b.limitChunks))
	}
	if b.continueOnError {
		result.visitor = EagerVisitorList(visitors)
	} else {
		result.visitor = VisitorList(visitors)
	}
	result.sources = visitors
	return result
}


getClient

func (b *Builder) getClient(gv schema.GroupVersion) (RESTClient, error) {
	var (
		client RESTClient
		err    error
	)

	switch {
	case b.fakeClientFn != nil:
		client, err = b.fakeClientFn(gv)
	case b.negotiatedSerializer != nil:
		client, err = b.clientConfigFn.withStdinUnavailable(b.stdinInUse).clientForGroupVersion(gv, b.negotiatedSerializer)
	default:
		client, err = b.clientConfigFn.withStdinUnavailable(b.stdinInUse).unstructuredClientForGroupVersion(gv)
	}

	if err != nil {
		return nil, err
	}

	return NewClientWithOptions(client, b.requestTransforms...), nil
}


visitByResource

func (b *Builder) visitByResource() *Result {
	// if b.singleItemImplied is false, this could be by default, so double-check length
	// of resourceTuples to determine if in fact it is singleItemImplied or not
	isSingleItemImplied := b.singleItemImplied
	if !isSingleItemImplied {
		isSingleItemImplied = len(b.resourceTuples) == 1
	}

	result := &Result{
		singleItemImplied:  isSingleItemImplied,
		targetsSingleItems: true,
	}

	if len(b.resources) != 0 {
		return result.withError(fmt.Errorf("you may not specify individual resources and bulk resources in the same call"))
	}

	// retrieve one client for each resource
	mappings, err := b.resourceTupleMappings()
	if err != nil {
		result.err = err
		return result
	}
	clients := make(map[string]RESTClient)
	for _, mapping := range mappings {
		s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource.Resource)
		if _, ok := clients[s]; ok {
			continue
		}
		client, err := b.getClient(mapping.GroupVersionKind.GroupVersion())
		if err != nil {
			result.err = err
			return result
		}
		clients[s] = client
	}

	items := []Visitor{}
	for _, tuple := range b.resourceTuples {
		mapping, ok := mappings[tuple.Resource]
		if !ok {
			return result.withError(fmt.Errorf("resource %q is not recognized: %v", tuple.Resource, mappings))
		}
		s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource.Resource)
		client, ok := clients[s]
		if !ok {
			return result.withError(fmt.Errorf("could not find a client for resource %q", tuple.Resource))
		}

		selectorNamespace := b.namespace
		if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
			selectorNamespace = ""
		} else {
			if len(b.namespace) == 0 {
				errMsg := "namespace may not be empty when retrieving a resource by name"
				if b.allNamespace {
					errMsg = "a resource cannot be retrieved by name across all namespaces"
				}
				return result.withError(fmt.Errorf(errMsg))
			}
		}

		info := &Info{
			Client:      client,
			Mapping:     mapping,
			Namespace:   selectorNamespace,
			Name:        tuple.Name,
			Subresource: b.subresource,
		}
		items = append(items, info)
	}

	var visitors Visitor
	if b.continueOnError {
		visitors = EagerVisitorList(items)
	} else {
		visitors = VisitorList(items)
	}
	result.visitor = visitors
	result.sources = items
	return result
}


visitByName

func (b *Builder) visitByName() *Result {
	result := &Result{
		singleItemImplied:  len(b.names) == 1,
		targetsSingleItems: true,
	}

	if len(b.paths) != 0 {
		return result.withError(fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well"))
	}
	if len(b.resources) == 0 {
		return result.withError(fmt.Errorf("you must provide a resource and a resource name together"))
	}
	if len(b.resources) > 1 {
		return result.withError(fmt.Errorf("you must specify only one resource"))
	}

	mappings, err := b.resourceMappings()
	if err != nil {
		result.err = err
		return result
	}
	mapping := mappings[0]

	client, err := b.getClient(mapping.GroupVersionKind.GroupVersion())
	if err != nil {
		result.err = err
		return result
	}

	selectorNamespace := b.namespace
	if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
		selectorNamespace = ""
	} else {
		if len(b.namespace) == 0 {
			errMsg := "namespace may not be empty when retrieving a resource by name"
			if b.allNamespace {
				errMsg = "a resource cannot be retrieved by name across all namespaces"
			}
			return result.withError(fmt.Errorf(errMsg))
		}
	}

	visitors := []Visitor{}
	for _, name := range b.names {
		info := &Info{
			Client:      client,
			Mapping:     mapping,
			Namespace:   selectorNamespace,
			Name:        name,
			Subresource: b.subresource,
		}
		visitors = append(visitors, info)
	}
	result.visitor = VisitorList(visitors)
	result.sources = visitors
	return result
}


visitByPaths

func (b *Builder) visitByPaths() *Result {
	result := &Result{
		singleItemImplied:  !b.dir && !b.stream && len(b.paths) == 1,
		targetsSingleItems: true,
	}

	if len(b.resources) != 0 {
		return result.withError(fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well"))
	}
	if len(b.names) != 0 {
		return result.withError(fmt.Errorf("name cannot be provided when a path is specified"))
	}
	if len(b.resourceTuples) != 0 {
		return result.withError(fmt.Errorf("resource/name arguments cannot be provided when a path is specified"))
	}

	var visitors Visitor
	if b.continueOnError {
		visitors = EagerVisitorList(b.paths)
	} else {
		visitors = VisitorList(b.paths)
	}

	if b.flatten {
		visitors = NewFlattenListVisitor(visitors, b.objectTyper, b.mapper)
	}

	// only items from disk can be refetched
	if b.latest {
		// must set namespace prior to fetching
		if b.defaultNamespace {
			visitors = NewDecoratedVisitor(visitors, SetNamespace(b.namespace))
		}
		visitors = NewDecoratedVisitor(visitors, RetrieveLatest)
	}
	if b.labelSelector != nil {
		selector, err := labels.Parse(*b.labelSelector)
		if err != nil {
			return result.withError(fmt.Errorf("the provided selector %q is not valid: %v", *b.labelSelector, err))
		}
		visitors = NewFilteredVisitor(visitors, FilterByLabelSelector(selector))
	}
	result.visitor = visitors
	result.sources = b.paths
	return result
}


Do

// Do returns a Result object with a Visitor for the resources identified by the Builder.
// The visitor will respect the error behavior specified by ContinueOnError. Note that stream
// inputs are consumed by the first execution - use Infos() or Object() on the Result to capture a list
// for further iteration.
func (b *Builder) Do() *Result {
	r := b.visitorResult()
	r.mapper = b.Mapper()
	if r.err != nil {
		return r
	}
	if b.flatten {
		r.visitor = NewFlattenListVisitor(r.visitor, b.objectTyper, b.mapper)
	}
	helpers := []VisitorFunc{}
	if b.defaultNamespace {
		helpers = append(helpers, SetNamespace(b.namespace))
	}
	if b.requireNamespace {
		helpers = append(helpers, RequireNamespace(b.namespace))
	}
	helpers = append(helpers, FilterNamespace)
	if b.requireObject {
		helpers = append(helpers, RetrieveLazy)
	}
	if b.continueOnError {
		r.visitor = ContinueOnErrorVisitor{Visitor: r.visitor}
	}
	r.visitor = NewDecoratedVisitor(r.visitor, helpers...)
	return r
}


SplitResourceArgument

// SplitResourceArgument splits the argument with commas and returns unique
// strings in the original order.
func SplitResourceArgument(arg string) []string {
	out := []string{}
	set := sets.NewString()
	for _, s := range strings.Split(arg, ",") {
		if set.Has(s) {
			continue
		}
		set.Insert(s)
		out = append(out, s)
	}
	return out
}


HasNames

// HasNames returns true if the provided args contain resource names
func HasNames(args []string) (bool, error) {
	args = normalizeMultipleResourcesArgs(args)
	hasCombinedTypes, err := hasCombinedTypeArgs(args)
	if err != nil {
		return false, err
	}
	return hasCombinedTypes || len(args) > 1, nil
}


expandIfFilePattern

// expandIfFilePattern returns all the filenames that match the input pattern
// or the filename if it is a specific filename and not a pattern.
// If the input is a pattern and it yields no result it will result in an error.
func expandIfFilePattern(pattern string) ([]string, error) {
	if _, err := os.Stat(pattern); os.IsNotExist(err) {
		matches, err := filepath.Glob(pattern)
		if err == nil && len(matches) == 0 {
			return nil, fmt.Errorf(pathNotExistError, pattern)
		}
		if err == filepath.ErrBadPattern {
			return nil, fmt.Errorf("pattern %q is not valid: %v", pattern, err)
		}
		return matches, err
	}
	return []string{pattern}, nil
}


ToCategoryExpander

func (c *cachingCategoryExpanderFunc) ToCategoryExpander() (restmapper.CategoryExpander, error) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.cached != nil {
		return c.cached, nil
	}

	ret, err := c.delegate()
	if err != nil {
		return nil, err
	}
	c.cached = ret
	return c.cached, nil
}


clientForGroupVersion

// TODO require negotiatedSerializer.  leaving it optional lets us plumb current behavior and deal with the difference after major plumbing is complete
func (clientConfigFn ClientConfigFunc) clientForGroupVersion(gv schema.GroupVersion, negotiatedSerializer runtime.NegotiatedSerializer) (RESTClient, error) {
	cfg, err := clientConfigFn()
	if err != nil {
		return nil, err
	}
	if negotiatedSerializer != nil {
		cfg.ContentConfig.NegotiatedSerializer = negotiatedSerializer
	}
	cfg.GroupVersion = &gv
	if len(gv.Group) == 0 {
		cfg.APIPath = "/api"
	} else {
		cfg.APIPath = "/apis"
	}

	return rest.RESTClientFor(cfg)
}


unstructuredClientForGroupVersion

func (clientConfigFn ClientConfigFunc) unstructuredClientForGroupVersion(gv schema.GroupVersion) (RESTClient, error) {
	cfg, err := clientConfigFn()
	if err != nil {
		return nil, err
	}
	cfg.ContentConfig = UnstructuredPlusDefaultContentConfig()
	cfg.GroupVersion = &gv
	if len(gv.Group) == 0 {
		cfg.APIPath = "/api"
	} else {
		cfg.APIPath = "/apis"
	}

	return rest.RESTClientFor(cfg)
}


withStdinUnavailable

func (clientConfigFn ClientConfigFunc) withStdinUnavailable(stdinUnavailable bool) ClientConfigFunc {
	return func() (*rest.Config, error) {
		cfg, err := clientConfigFn()
		if stdinUnavailable && cfg != nil && cfg.ExecProvider != nil {
			cfg.ExecProvider.StdinUnavailable = stdinUnavailable
			cfg.ExecProvider.StdinUnavailableMessage = "used by stdin resource manifest reader"
		}
		return cfg, err
	}
}


CRDFromDynamic

func CRDFromDynamic(client dynamic.Interface) CRDGetter {
	return func() ([]schema.GroupKind, error) {
		list, err := client.Resource(schema.GroupVersionResource{
			Group:    "apiextensions.k8s.io",
			Version:  "v1",
			Resource: "customresourcedefinitions",
		}).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return nil, fmt.Errorf("failed to list CRDs: %v", err)
		}
		if list == nil {
			return nil, nil
		}

		gks := []schema.GroupKind{}

		// We need to parse the list to get the gvk, I guess that's fine.
		for _, crd := range (*list).Items {
			// Look for group, version, and kind
			group, _, _ := unstructured.NestedString(crd.Object, "spec", "group")
			kind, _, _ := unstructured.NestedString(crd.Object, "spec", "names", "kind")

			gks = append(gks, schema.GroupKind{
				Group: group,
				Kind:  kind,
			})
		}

		return gks, nil
	}
}


NewCRDFinder

func NewCRDFinder(getter CRDGetter) CRDFinder {
	return &crdFinder{
		getter: getter,
	}
}


cacheCRDs

func (f *crdFinder) cacheCRDs() error {
	if f.cache != nil {
		return nil
	}

	list, err := f.getter()
	if err != nil {
		return err
	}
	f.cache = &list
	return nil
}


findCRD

func (f *crdFinder) findCRD(gvk schema.GroupKind) bool {
	for _, crd := range *f.cache {
		if reflect.DeepEqual(gvk, crd) {
			return true
		}
	}
	return false
}


HasCRD

func (f *crdFinder) HasCRD(gvk schema.GroupKind) (bool, error) {
	if err := f.cacheCRDs(); err != nil {
		return false, err
	}
	return f.findCRD(gvk), nil
}


NewFallbackQueryParamVerifier

// NewFallbackQueryParamVerifier returns a new Verifier which will invoke the
// initial/primary Verifier. If the primary Verifier is "NotFound", then the
// secondary Verifier is invoked as a fallback.
func NewFallbackQueryParamVerifier(primary Verifier, secondary Verifier) Verifier {
	return &fallbackQueryParamVerifier{
		primary:   primary,
		secondary: secondary,
	}
}


HasSupport

// HasSupport returns an error if the passed GVK does not support the
// query param (fieldValidation), as determined by the primary and
// secondary OpenAPI endpoints. The primary endoint is checked first,
// but if there is an error retrieving the OpenAPI V3 document, the
// secondary attempts to determine support. If the GVK supports the query param,
// nil is returned.
func (f *fallbackQueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error {
	err := f.primary.HasSupport(gvk)
	// If an error was returned from the primary OpenAPI endpoint,
	// we fallback to check the secondary OpenAPI endpoint for
	// any error *except* "paramUnsupportedError".
	if err != nil && !IsParamUnsupportedError(err) {
		klog.V(7).Infof("openapi v3 error...falling back to legacy: %s", err)
		err = f.secondary.HasSupport(gvk)
	}
	return err
}


NewHelper

// NewHelper creates a Helper from a ResourceMapping
func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper {
	return &Helper{
		Resource:        mapping.Resource.Resource,
		RESTClient:      client,
		NamespaceScoped: mapping.Scope.Name() == meta.RESTScopeNameNamespace,
	}
}


DryRun

// DryRun, if true, will use server-side dry-run to not persist changes to storage.
// Otherwise, changes will be persisted to storage.
func (m *Helper) DryRun(dryRun bool) *Helper {
	m.ServerDryRun = dryRun
	return m
}


WithFieldManager

// WithFieldManager sets the field manager option to indicate the actor or entity
// that is making changes in a create or update operation.
func (m *Helper) WithFieldManager(fieldManager string) *Helper {
	m.FieldManager = fieldManager
	return m
}


WithFieldValidation

// WithFieldValidation sets the field validation option to indicate
// how the server should perform field validation (Ignore, Warn, or Strict).
func (m *Helper) WithFieldValidation(validationDirective string) *Helper {
	m.FieldValidation = validationDirective
	return m
}


WithSubresource

// Subresource sets the helper to access (<resource>/[ns/<namespace>/]<name>/<subresource>)
func (m *Helper) WithSubresource(subresource string) *Helper {
	m.Subresource = subresource
	return m
}


Get

func (m *Helper) Get(namespace, name string) (runtime.Object, error) {
	req := m.RESTClient.Get().
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(m.Resource).
		Name(name).
		SubResource(m.Subresource)
	return req.Do(context.TODO()).Get()
}


List

func (m *Helper) List(namespace, apiVersion string, options *metav1.ListOptions) (runtime.Object, error) {
	req := m.RESTClient.Get().
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(m.Resource).
		VersionedParams(options, metav1.ParameterCodec)
	return req.Do(context.TODO()).Get()
}


FollowContinue

// FollowContinue handles the continue parameter returned by the API server when using list
// chunking. To take advantage of this, the initial ListOptions provided by the consumer
// should include a non-zero Limit parameter.
func FollowContinue(initialOpts *metav1.ListOptions,
	listFunc func(metav1.ListOptions) (runtime.Object, error)) error {
	opts := initialOpts
	for {
		list, err := listFunc(*opts)
		if err != nil {
			return err
		}
		nextContinueToken, _ := metadataAccessor.Continue(list)
		if len(nextContinueToken) == 0 {
			return nil
		}
		opts.Continue = nextContinueToken
	}
}


EnhanceListError

// EnhanceListError augments errors typically returned by List operations with additional context,
// making sure to retain the StatusError type when applicable.
func EnhanceListError(err error, opts metav1.ListOptions, subj string) error {
	if apierrors.IsResourceExpired(err) {
		return err
	}
	if apierrors.IsBadRequest(err) || apierrors.IsNotFound(err) {
		if se, ok := err.(*apierrors.StatusError); ok {
			// modify the message without hiding this is an API error
			if len(opts.LabelSelector) == 0 && len(opts.FieldSelector) == 0 {
				se.ErrStatus.Message = fmt.Sprintf("Unable to list %q: %v", subj,
					se.ErrStatus.Message)
			} else {
				se.ErrStatus.Message = fmt.Sprintf(
					"Unable to find %q that match label selector %q, field selector %q: %v", subj,
					opts.LabelSelector,
					opts.FieldSelector, se.ErrStatus.Message)
			}
			return se
		}
		if len(opts.LabelSelector) == 0 && len(opts.FieldSelector) == 0 {
			return fmt.Errorf("Unable to list %q: %v", subj, err)
		}
		return fmt.Errorf("Unable to find %q that match label selector %q, field selector %q: %v",
			subj, opts.LabelSelector, opts.FieldSelector, err)
	}
	return err
}


Watch

func (m *Helper) Watch(namespace, apiVersion string, options *metav1.ListOptions) (watch.Interface, error) {
	options.Watch = true
	return m.RESTClient.Get().
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(m.Resource).
		VersionedParams(options, metav1.ParameterCodec).
		Watch(context.TODO())
}


WatchSingle

func (m *Helper) WatchSingle(namespace, name, resourceVersion string) (watch.Interface, error) {
	return m.RESTClient.Get().
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(m.Resource).
		VersionedParams(&metav1.ListOptions{
			ResourceVersion: resourceVersion,
			Watch:           true,
			FieldSelector:   fields.OneTermEqualSelector("metadata.name", name).String(),
		}, metav1.ParameterCodec).
		Watch(context.TODO())
}


Delete

func (m *Helper) Delete(namespace, name string) (runtime.Object, error) {
	return m.DeleteWithOptions(namespace, name, nil)
}


DeleteWithOptions

func (m *Helper) DeleteWithOptions(namespace, name string, options *metav1.DeleteOptions) (runtime.Object, error) {
	if options == nil {
		options = &metav1.DeleteOptions{}
	}
	if m.ServerDryRun {
		options.DryRun = []string{metav1.DryRunAll}
	}

	return m.RESTClient.Delete().
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(m.Resource).
		Name(name).
		Body(options).
		Do(context.TODO()).
		Get()
}


Create

func (m *Helper) Create(namespace string, modify bool, obj runtime.Object) (runtime.Object, error) {
	return m.CreateWithOptions(namespace, modify, obj, nil)
}


CreateWithOptions

func (m *Helper) CreateWithOptions(namespace string, modify bool, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) {
	if options == nil {
		options = &metav1.CreateOptions{}
	}
	if m.ServerDryRun {
		options.DryRun = []string{metav1.DryRunAll}
	}
	if m.FieldManager != "" {
		options.FieldManager = m.FieldManager
	}
	if m.FieldValidation != "" {
		options.FieldValidation = m.FieldValidation
	}
	if modify {
		// Attempt to version the object based on client logic.
		version, err := metadataAccessor.ResourceVersion(obj)
		if err != nil {
			// We don't know how to clear the version on this object, so send it to the server as is
			return m.createResource(m.RESTClient, m.Resource, namespace, obj, options)
		}
		if version != "" {
			if err := metadataAccessor.SetResourceVersion(obj, ""); err != nil {
				return nil, err
			}
		}
	}

	return m.createResource(m.RESTClient, m.Resource, namespace, obj, options)
}


createResource

func (m *Helper) createResource(c RESTClient, resource, namespace string, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) {
	return c.Post().
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(resource).
		VersionedParams(options, metav1.ParameterCodec).
		Body(obj).
		Do(context.TODO()).
		Get()
}


Patch

func (m *Helper) Patch(namespace, name string, pt types.PatchType, data []byte, options *metav1.PatchOptions) (runtime.Object, error) {
	if options == nil {
		options = &metav1.PatchOptions{}
	}
	if m.ServerDryRun {
		options.DryRun = []string{metav1.DryRunAll}
	}
	if m.FieldManager != "" {
		options.FieldManager = m.FieldManager
	}
	if m.FieldValidation != "" {
		options.FieldValidation = m.FieldValidation
	}
	return m.RESTClient.Patch(pt).
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(m.Resource).
		Name(name).
		SubResource(m.Subresource).
		VersionedParams(options, metav1.ParameterCodec).
		Body(data).
		Do(context.TODO()).
		Get()
}


Replace

func (m *Helper) Replace(namespace, name string, overwrite bool, obj runtime.Object) (runtime.Object, error) {
	c := m.RESTClient
	var options = &metav1.UpdateOptions{}
	if m.ServerDryRun {
		options.DryRun = []string{metav1.DryRunAll}
	}
	if m.FieldManager != "" {
		options.FieldManager = m.FieldManager
	}
	if m.FieldValidation != "" {
		options.FieldValidation = m.FieldValidation
	}

	// Attempt to version the object based on client logic.
	version, err := metadataAccessor.ResourceVersion(obj)
	if err != nil {
		// We don't know how to version this object, so send it to the server as is
		return m.replaceResource(c, m.Resource, namespace, name, obj, options)
	}
	if version == "" && overwrite {
		// Retrieve the current version of the object to overwrite the server object
		serverObj, err := c.Get().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(m.Resource).Name(name).SubResource(m.Subresource).Do(context.TODO()).Get()
		if err != nil {
			// The object does not exist, but we want it to be created
			return m.replaceResource(c, m.Resource, namespace, name, obj, options)
		}
		serverVersion, err := metadataAccessor.ResourceVersion(serverObj)
		if err != nil {
			return nil, err
		}
		if err := metadataAccessor.SetResourceVersion(obj, serverVersion); err != nil {
			return nil, err
		}
	}

	return m.replaceResource(c, m.Resource, namespace, name, obj, options)
}


replaceResource

func (m *Helper) replaceResource(c RESTClient, resource, namespace, name string, obj runtime.Object, options *metav1.UpdateOptions) (runtime.Object, error) {
	return c.Put().
		NamespaceIfScoped(namespace, m.NamespaceScoped).
		Resource(resource).
		Name(name).
		SubResource(m.Subresource).
		VersionedParams(options, metav1.ParameterCodec).
		Body(obj).
		Do(context.TODO()).
		Get()
}


NewClientWithOptions

// NewClientWithOptions wraps the provided RESTClient and invokes each transform on each
// newly created request.
func NewClientWithOptions(c RESTClient, transforms ...RequestTransform) RESTClient {
	if len(transforms) == 0 {
		return c
	}
	return &clientOptions{c: c, transforms: transforms}
}


modify

func (c *clientOptions) modify(req *rest.Request) *rest.Request {
	for _, transform := range c.transforms {
		transform(req)
	}
	return req
}


Get

func (c *clientOptions) Get() *rest.Request {
	return c.modify(c.c.Get())
}


Post

func (c *clientOptions) Post() *rest.Request {
	return c.modify(c.c.Post())
}


Patch

func (c *clientOptions) Patch(t types.PatchType) *rest.Request {
	return c.modify(c.c.Patch(t))
}


Delete

func (c *clientOptions) Delete() *rest.Request {
	return c.modify(c.c.Delete())
}


Put

func (c *clientOptions) Put() *rest.Request {
	return c.modify(c.c.Put())
}


Visit

// Visit passes the result of a kustomize build to a StreamVisitor.
func (v *KustomizeVisitor) Visit(fn VisitorFunc) error {
	kOpts := krusty.MakeDefaultOptions()
	kOpts.Reorder = krusty.ReorderOptionLegacy
	k := krusty.MakeKustomizer(kOpts)
	m, err := k.Run(v.fSys, v.dirPath)
	if err != nil {
		return err
	}
	v.yml, err = m.AsYaml()
	if err != nil {
		return err
	}
	sv := NewStreamVisitor(
		bytes.NewReader(v.yml), v.mapper, v.dirPath, v.schema)
	return sv.Visit(fn)
}


infoForData

// InfoForData creates an Info object for the given data. An error is returned
// if any of the decoding or client lookup steps fail. Name and namespace will be
// set into Info if the mapping's MetadataAccessor can retrieve them.
func (m *mapper) infoForData(data []byte, source string) (*Info, error) {
	obj, gvk, err := m.decoder.Decode(data, nil, nil)
	if err != nil {
		return nil, fmt.Errorf("unable to decode %q: %v", source, err)
	}

	name, _ := metadataAccessor.Name(obj)
	namespace, _ := metadataAccessor.Namespace(obj)
	resourceVersion, _ := metadataAccessor.ResourceVersion(obj)

	ret := &Info{
		Source:          source,
		Namespace:       namespace,
		Name:            name,
		ResourceVersion: resourceVersion,

		Object: obj,
	}

	if m.localFn == nil || !m.localFn() {
		restMapper, err := m.restMapperFn()
		if err != nil {
			return nil, err
		}
		mapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
		if err != nil {
			if _, ok := err.(*meta.NoKindMatchError); ok {
				return nil, fmt.Errorf("resource mapping not found for name: %q namespace: %q from %q: %v\nensure CRDs are installed first",
					name, namespace, source, err)
			}
			return nil, fmt.Errorf("unable to recognize %q: %v", source, err)
		}
		ret.Mapping = mapping

		client, err := m.clientFn(gvk.GroupVersion())
		if err != nil {
			return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
		}
		ret.Client = client
	}

	return ret, nil
}


infoForObject

// InfoForObject creates an Info object for the given Object. An error is returned
// if the object cannot be introspected. Name and namespace will be set into Info
// if the mapping's MetadataAccessor can retrieve them.
func (m *mapper) infoForObject(obj runtime.Object, typer runtime.ObjectTyper, preferredGVKs []schema.GroupVersionKind) (*Info, error) {
	groupVersionKinds, _, err := typer.ObjectKinds(obj)
	if err != nil {
		return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err)
	}

	gvk := groupVersionKinds[0]
	if len(groupVersionKinds) > 1 && len(preferredGVKs) > 0 {
		gvk = preferredObjectKind(groupVersionKinds, preferredGVKs)
	}

	name, _ := metadataAccessor.Name(obj)
	namespace, _ := metadataAccessor.Namespace(obj)
	resourceVersion, _ := metadataAccessor.ResourceVersion(obj)
	ret := &Info{
		Namespace:       namespace,
		Name:            name,
		ResourceVersion: resourceVersion,

		Object: obj,
	}

	if m.localFn == nil || !m.localFn() {
		restMapper, err := m.restMapperFn()
		if err != nil {
			return nil, err
		}
		mapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
		if err != nil {
			return nil, fmt.Errorf("unable to recognize %v", err)
		}
		ret.Mapping = mapping

		client, err := m.clientFn(gvk.GroupVersion())
		if err != nil {
			return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
		}
		ret.Client = client
	}

	return ret, nil
}


preferredObjectKind

// preferredObjectKind picks the possibility that most closely matches the priority list in this order:
// GroupVersionKind matches (exact match)
// GroupKind matches
// Group matches
func preferredObjectKind(possibilities []schema.GroupVersionKind, preferences []schema.GroupVersionKind) schema.GroupVersionKind {
	// Exact match
	for _, priority := range preferences {
		for _, possibility := range possibilities {
			if possibility == priority {
				return possibility
			}
		}
	}

	// GroupKind match
	for _, priority := range preferences {
		for _, possibility := range possibilities {
			if possibility.GroupKind() == priority.GroupKind() {
				return possibility
			}
		}
	}

	// Group match
	for _, priority := range preferences {
		for _, possibility := range possibilities {
			if possibility.Group == priority.Group {
				return possibility
			}
		}
	}

	// Just pick the first
	return possibilities[0]
}


Decode

func (m *metadataValidatingDecoder) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
	obj, gvk, err := m.decoder.Decode(data, defaults, into)

	// if we already errored, return
	if err != nil {
		return obj, gvk, err
	}

	// if we're not unstructured, return
	if _, isUnstructured := obj.(runtime.Unstructured); !isUnstructured {
		return obj, gvk, err
	}

	// make sure the data can decode into ObjectMeta before we return,
	// so we don't silently truncate schema errors in metadata later with accesser get/set calls
	v := &metadataOnlyObject{}
	if typedErr := utiljson.Unmarshal(data, v); typedErr != nil {
		return obj, gvk, typedErr
	}
	return obj, gvk, err
}


NewQueryParamVerifier

func NewQueryParamVerifier(dynamicClient dynamic.Interface, openAPIGetter discovery.OpenAPISchemaInterface, queryParam VerifiableQueryParam) *QueryParamVerifier {
	return &QueryParamVerifier{
		finder:        NewCRDFinder(CRDFromDynamic(dynamicClient)),
		openAPIGetter: openAPIGetter,
		queryParam:    queryParam,
	}
}


HasSupport

// HasSupport checks if the given gvk supports the query param configured on v
func (v *QueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error {
	if (gvk == schema.GroupVersionKind{Version: "v1", Kind: "List"}) {
		return NewParamUnsupportedError(gvk, v.queryParam)
	}

	oapi, err := v.openAPIGetter.OpenAPISchema()
	if err != nil {
		return fmt.Errorf("failed to download openapi: %v", err)
	}
	supports, err := supportsQueryParam(oapi, gvk, v.queryParam)
	if err != nil {
		// We assume that we couldn't find the type, then check for namespace:
		supports, _ = supportsQueryParam(oapi, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}, v.queryParam)
		// If namespace supports the query param, then we will support the query param for CRDs only.
		if supports {
			supports, err = v.finder.HasCRD(gvk.GroupKind())
			if err != nil {
				return fmt.Errorf("failed to check CRD: %v", err)
			}
		}
	}
	if !supports {
		return NewParamUnsupportedError(gvk, v.queryParam)
	}
	return nil
}


NewParamUnsupportedError

func NewParamUnsupportedError(gvk schema.GroupVersionKind, param VerifiableQueryParam) error {
	return &paramUnsupportedError{
		gvk:   gvk,
		param: param,
	}
}


Error

func (e *paramUnsupportedError) Error() string {
	return fmt.Sprintf("%v doesn't support %s", e.gvk, e.param)
}


IsParamUnsupportedError

func IsParamUnsupportedError(err error) bool {
	if err == nil {
		return false
	}
	_, ok := err.(*paramUnsupportedError)
	return ok
}


hasGVKExtension

func hasGVKExtension(extensions []*openapi_v2.NamedAny, gvk schema.GroupVersionKind) bool {
	for _, extension := range extensions {
		if extension.GetValue().GetYaml() == "" ||
			extension.GetName() != "x-kubernetes-group-version-kind" {
			continue
		}
		var value map[string]string
		err := yaml.Unmarshal([]byte(extension.GetValue().GetYaml()), &value)
		if err != nil {
			continue
		}

		if value["group"] == gvk.Group && value["kind"] == gvk.Kind && value["version"] == gvk.Version {
			return true
		}
		return false
	}
	return false
}


supportsQueryParam

// supportsQueryParam is a method that let's us look in the OpenAPI if the
// specific group-version-kind supports the specific query parameter for
// the PATCH end-point.
func supportsQueryParam(doc *openapi_v2.Document, gvk schema.GroupVersionKind, queryParam VerifiableQueryParam) (bool, error) {
	for _, path := range doc.GetPaths().GetPath() {
		// Is this describing the gvk we're looking for?
		if !hasGVKExtension(path.GetValue().GetPatch().GetVendorExtension(), gvk) {
			continue
		}
		for _, param := range path.GetValue().GetPatch().GetParameters() {
			if param.GetParameter().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == string(queryParam) {
				return true, nil
			}
		}
		return false, nil
	}

	return false, errors.New("couldn't find GVK in openapi")
}


// NewQueryParamVerifierV3 returns a pointer to the created queryParamVerifier3 struct,
// which implements the Verifier interface. The caching characteristics of the
// OpenAPI V3 specs are determined by the passed oapiClient. For memory caching, the
// client should be wrapped beforehand as: cached.NewClient(oapiClient). The disk
// caching is determined by the discovery client the oapiClient is created from.
func NewQueryParamVerifierV3(dynamicClient dynamic.Interface, oapiClient openapi.Client, queryParam VerifiableQueryParam) Verifier {
	return &queryParamVerifierV3{
		finder:     NewCRDFinder(CRDFromDynamic(dynamicClient)),
		root:       openapi3.NewRoot(oapiClient),
		queryParam: queryParam,
	}
}


HasSupport

// HasSupport returns nil error if the passed GVK supports the parameter
// (stored in struct; usually "fieldValidation") for Patch endpoint.
// Returns an error if the passed GVK does not support the query param,
// or if another error occurred. If the Open API V3 spec for a CRD is not
// found, then the spec for Namespace is checked for query param support instead.
func (v *queryParamVerifierV3) HasSupport(gvk schema.GroupVersionKind) error {
	if (gvk == schema.GroupVersionKind{Version: "v1", Kind: "List"}) {
		return NewParamUnsupportedError(gvk, v.queryParam)
	}
	gvSpec, err := v.root.GVSpec(gvk.GroupVersion())
	if err == nil {
		return supportsQueryParamV3(gvSpec, gvk, v.queryParam)
	}
	if _, isErr := err.(*openapi3.GroupVersionNotFoundError); !isErr {
		return err
	}
	// If the spec for the passed GVK is not found, then check if it is a CRD.
	// For CRD's substitute Namespace OpenAPI V3 spec to check if query param is supported.
	if found, _ := v.finder.HasCRD(gvk.GroupKind()); found {
		namespaceSpec, err := v.root.GVSpec(namespaceGVK.GroupVersion())
		if err != nil {
			// If error retrieving Namespace spec, propagate error.
			return err
		}
		return supportsQueryParamV3(namespaceSpec, namespaceGVK, v.queryParam)
	}
	return NewParamUnsupportedError(gvk, v.queryParam)
}


// hasGVKExtensionV3 returns true if the passed OpenAPI extensions map contains
// the passed GVK; false otherwise.
func hasGVKExtensionV3(extensions spec.Extensions, gvk schema.GroupVersionKind) bool {
	var oapiGVK map[string]string
	err := extensions.GetObject("x-kubernetes-group-version-kind", &oapiGVK)
	if err != nil {
		return false
	}
	if oapiGVK["group"] == gvk.Group &&
		oapiGVK["version"] == gvk.Version &&
		oapiGVK["kind"] == gvk.Kind {
		return true
	}
	return false
}


// supportsQueryParam is a method that let's us look in the OpenAPI if the
// specific group-version-kind supports the specific query parameter for
// the PATCH end-point. Returns nil if the passed GVK supports the passed
// query parameter; otherwise, a "paramUnsupportedError" is returned (except
// when an invalid document error is returned when an invalid OpenAPI V3
// is passed in).
func supportsQueryParamV3(doc *spec3.OpenAPI, gvk schema.GroupVersionKind, queryParam VerifiableQueryParam) error {
	if doc == nil || doc.Paths == nil {
		return fmt.Errorf("Invalid OpenAPI V3 document")
	}
	for _, path := range doc.Paths.Paths {
		// If operation is not PATCH, then continue.
		if path == nil {
			continue
		}
		op := path.PathProps.Patch
		if op == nil {
			continue
		}
		// Is this PATCH operation for the passed GVK?
		if !hasGVKExtensionV3(op.VendorExtensible.Extensions, gvk) {
			continue
		}
		// Now look for the query parameter among the parameters
		// for the PATCH operation.
		for _, param := range op.OperationProps.Parameters {
			if param.ParameterProps.Name == string(queryParam) {
				return nil
			}
		}
		return NewParamUnsupportedError(gvk, queryParam)
	}
	return fmt.Errorf("Path not found for GVK (%s) in OpenAPI V3 doc", gvk)
}


withError

// withError allows a fluent style for internal result code.
func (r *Result) withError(err error) *Result {
	r.err = err
	return r
}


TargetsSingleItems

// TargetsSingleItems returns true if any of the builder arguments pointed
// to non-list calls (if the user explicitly asked for any object by name).
// This includes directories, streams, URLs, and resource name tuples.
func (r *Result) TargetsSingleItems() bool {
	return r.targetsSingleItems
}


IgnoreErrors

// IgnoreErrors will filter errors that occur when by visiting the result
// (but not errors that occur by creating the result in the first place),
// eliminating any that match fns. This is best used in combination with
// Builder.ContinueOnError(), where the visitors accumulate errors and return
// them after visiting as a slice of errors. If no errors remain after
// filtering, the various visitor methods on Result will return nil for
// err.
func (r *Result) IgnoreErrors(fns ...ErrMatchFunc) *Result {
	for _, fn := range fns {
		r.ignoreErrors = append(r.ignoreErrors, utilerrors.Matcher(fn))
	}
	return r
}


Mapper

// Mapper returns a copy of the builder's mapper.
func (r *Result) Mapper() *mapper {
	return r.mapper
}


Err

// Err returns one or more errors (via a util.ErrorList) that occurred prior
// to visiting the elements in the visitor. To see all errors including those
// that occur during visitation, invoke Infos().
func (r *Result) Err() error {
	return r.err
}


Visit

// Visit implements the Visitor interface on the items described in the Builder.
// Note that some visitor sources are not traversable more than once, or may
// return different results.  If you wish to operate on the same set of resources
// multiple times, use the Infos() method.
func (r *Result) Visit(fn VisitorFunc) error {
	if r.err != nil {
		return r.err
	}
	err := r.visitor.Visit(fn)
	return utilerrors.FilterOut(err, r.ignoreErrors...)
}


IntoSingleItemImplied

// IntoSingleItemImplied sets the provided boolean pointer to true if the Builder input
// implies a single item, or multiple.
func (r *Result) IntoSingleItemImplied(b *bool) *Result {
	*b = r.singleItemImplied
	return r
}


Infos

// Infos returns an array of all of the resource infos retrieved via traversal.
// Will attempt to traverse the entire set of visitors only once, and will return
// a cached list on subsequent calls.
func (r *Result) Infos() ([]*Info, error) {
	if r.err != nil {
		return nil, r.err
	}
	if r.info != nil {
		return r.info, nil
	}

	infos := []*Info{}
	err := r.visitor.Visit(func(info *Info, err error) error {
		if err != nil {
			return err
		}
		infos = append(infos, info)
		return nil
	})
	err = utilerrors.FilterOut(err, r.ignoreErrors...)

	r.info, r.err = infos, err
	return infos, err
}


Object

// Object returns a single object representing the output of a single visit to all
// found resources.  If the Builder was a singular context (expected to return a
// single resource by user input) and only a single resource was found, the resource
// will be returned as is.  Otherwise, the returned resources will be part of an
// v1.List. The ResourceVersion of the v1.List will be set only if it is identical
// across all infos returned.
func (r *Result) Object() (runtime.Object, error) {
	infos, err := r.Infos()
	if err != nil {
		return nil, err
	}

	versions := sets.String{}
	objects := []runtime.Object{}
	for _, info := range infos {
		if info.Object != nil {
			objects = append(objects, info.Object)
			versions.Insert(info.ResourceVersion)
		}
	}

	if len(objects) == 1 {
		if r.singleItemImplied {
			return objects[0], nil
		}
		// if the item is a list already, don't create another list
		if meta.IsListType(objects[0]) {
			return objects[0], nil
		}
	}

	version := ""
	if len(versions) == 1 {
		version = versions.List()[0]
	}

	return toV1List(objects, version), err
}


// toV1List takes a slice of Objects + their version, and returns
// a v1.List Object containing the objects in the Items field
func toV1List(objects []runtime.Object, version string) runtime.Object {
	raw := []runtime.RawExtension{}
	for _, o := range objects {
		raw = append(raw, runtime.RawExtension{Object: o})
	}
	return &v1.List{
		ListMeta: metav1.ListMeta{
			ResourceVersion: version,
		},
		Items: raw,
	}
}