mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 10:53:34 +00:00
Fresh dep ensure
This commit is contained in:
84
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/BUILD
generated
vendored
84
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/BUILD
generated
vendored
@ -17,47 +17,58 @@ filegroup(
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"customcolumn.go",
|
||||
"customcolumn_flags.go",
|
||||
"get.go",
|
||||
"get_flags.go",
|
||||
"humanreadable_flags.go",
|
||||
"sorter.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/get",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/kubectl:go_default_library",
|
||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/resource:go_default_library",
|
||||
"//pkg/kubectl/scheme:go_default_library",
|
||||
"//pkg/kubectl/util/i18n:go_default_library",
|
||||
"//pkg/kubectl/util/printers:go_default_library",
|
||||
"//pkg/kubectl/util/templates:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/printers/internalversion:go_default_library",
|
||||
"//pkg/util/interrupt:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/printers:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/watch:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/integer:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/jsonpath:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/vbom.ml/util/sortorder:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"customcolumn_flags_test.go",
|
||||
"customcolumn_test.go",
|
||||
"get_test.go",
|
||||
"humanreadable_flags_test.go",
|
||||
"sorter_test.go",
|
||||
],
|
||||
data = [
|
||||
"//api/openapi-spec:swagger-spec",
|
||||
@ -66,28 +77,33 @@ go_test(
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/testing:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/v1:go_default_library",
|
||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||
"//pkg/kubectl/cmd/util/openapi/testing:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions/resource:go_default_library",
|
||||
"//pkg/kubectl/scheme:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest/watch:go_default_library",
|
||||
"//pkg/kubectl/util/printers:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest/fake:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest/watch:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
|
||||
],
|
||||
)
|
||||
|
243
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/customcolumn.go
generated
vendored
Normal file
243
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/customcolumn.go
generated
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package get
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
|
||||
"k8s.io/client-go/util/jsonpath"
|
||||
utilprinters "k8s.io/kubernetes/pkg/kubectl/util/printers"
|
||||
)
|
||||
|
||||
var jsonRegexp = regexp.MustCompile("^\\{\\.?([^{}]+)\\}$|^\\.?([^{}]+)$")
|
||||
|
||||
// RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:
|
||||
// * metadata.name (no leading '.' or curly braces '{...}'
|
||||
// * {metadata.name} (no leading '.')
|
||||
// * .metadata.name (no curly braces '{...}')
|
||||
// * {.metadata.name} (complete expression)
|
||||
// And transforms them all into a valid jsonpath expression:
|
||||
// {.metadata.name}
|
||||
func RelaxedJSONPathExpression(pathExpression string) (string, error) {
|
||||
if len(pathExpression) == 0 {
|
||||
return pathExpression, nil
|
||||
}
|
||||
submatches := jsonRegexp.FindStringSubmatch(pathExpression)
|
||||
if submatches == nil {
|
||||
return "", fmt.Errorf("unexpected path string, expected a 'name1.name2' or '.name1.name2' or '{name1.name2}' or '{.name1.name2}'")
|
||||
}
|
||||
if len(submatches) != 3 {
|
||||
return "", fmt.Errorf("unexpected submatch list: %v", submatches)
|
||||
}
|
||||
var fieldSpec string
|
||||
if len(submatches[1]) != 0 {
|
||||
fieldSpec = submatches[1]
|
||||
} else {
|
||||
fieldSpec = submatches[2]
|
||||
}
|
||||
return fmt.Sprintf("{.%s}", fieldSpec), nil
|
||||
}
|
||||
|
||||
// NewCustomColumnsPrinterFromSpec creates a custom columns printer from a comma separated list of <header>:<jsonpath-field-spec> pairs.
|
||||
// e.g. NAME:metadata.name,API_VERSION:apiVersion creates a printer that prints:
|
||||
//
|
||||
// NAME API_VERSION
|
||||
// foo bar
|
||||
func NewCustomColumnsPrinterFromSpec(spec string, decoder runtime.Decoder, noHeaders bool) (*CustomColumnsPrinter, error) {
|
||||
if len(spec) == 0 {
|
||||
return nil, fmt.Errorf("custom-columns format specified but no custom columns given")
|
||||
}
|
||||
parts := strings.Split(spec, ",")
|
||||
columns := make([]Column, len(parts))
|
||||
for ix := range parts {
|
||||
colSpec := strings.Split(parts[ix], ":")
|
||||
if len(colSpec) != 2 {
|
||||
return nil, fmt.Errorf("unexpected custom-columns spec: %s, expected <header>:<json-path-expr>", parts[ix])
|
||||
}
|
||||
spec, err := RelaxedJSONPathExpression(colSpec[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns[ix] = Column{Header: colSpec[0], FieldSpec: spec}
|
||||
}
|
||||
return &CustomColumnsPrinter{Columns: columns, Decoder: decoder, NoHeaders: noHeaders}, nil
|
||||
}
|
||||
|
||||
func splitOnWhitespace(line string) []string {
|
||||
lineScanner := bufio.NewScanner(bytes.NewBufferString(line))
|
||||
lineScanner.Split(bufio.ScanWords)
|
||||
result := []string{}
|
||||
for lineScanner.Scan() {
|
||||
result = append(result, lineScanner.Text())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NewCustomColumnsPrinterFromTemplate creates a custom columns printer from a template stream. The template is expected
|
||||
// to consist of two lines, whitespace separated. The first line is the header line, the second line is the jsonpath field spec
|
||||
// For example, the template below:
|
||||
// NAME API_VERSION
|
||||
// {metadata.name} {apiVersion}
|
||||
func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader, decoder runtime.Decoder) (*CustomColumnsPrinter, error) {
|
||||
scanner := bufio.NewScanner(templateReader)
|
||||
if !scanner.Scan() {
|
||||
return nil, fmt.Errorf("invalid template, missing header line. Expected format is one line of space separated headers, one line of space separated column specs.")
|
||||
}
|
||||
headers := splitOnWhitespace(scanner.Text())
|
||||
|
||||
if !scanner.Scan() {
|
||||
return nil, fmt.Errorf("invalid template, missing spec line. Expected format is one line of space separated headers, one line of space separated column specs.")
|
||||
}
|
||||
specs := splitOnWhitespace(scanner.Text())
|
||||
|
||||
if len(headers) != len(specs) {
|
||||
return nil, fmt.Errorf("number of headers (%d) and field specifications (%d) don't match", len(headers), len(specs))
|
||||
}
|
||||
|
||||
columns := make([]Column, len(headers))
|
||||
for ix := range headers {
|
||||
spec, err := RelaxedJSONPathExpression(specs[ix])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns[ix] = Column{
|
||||
Header: headers[ix],
|
||||
FieldSpec: spec,
|
||||
}
|
||||
}
|
||||
return &CustomColumnsPrinter{Columns: columns, Decoder: decoder, NoHeaders: false}, nil
|
||||
}
|
||||
|
||||
// Column represents a user specified column
|
||||
type Column struct {
|
||||
// The header to print above the column, general style is ALL_CAPS
|
||||
Header string
|
||||
// The pointer to the field in the object to print in JSONPath form
|
||||
// e.g. {.ObjectMeta.Name}, see pkg/util/jsonpath for more details.
|
||||
FieldSpec string
|
||||
}
|
||||
|
||||
// CustomColumnPrinter is a printer that knows how to print arbitrary columns
|
||||
// of data from templates specified in the `Columns` array
|
||||
type CustomColumnsPrinter struct {
|
||||
Columns []Column
|
||||
Decoder runtime.Decoder
|
||||
NoHeaders bool
|
||||
// lastType records type of resource printed last so that we don't repeat
|
||||
// header while printing same type of resources.
|
||||
lastType reflect.Type
|
||||
}
|
||||
|
||||
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out 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 printers.InternalObjectPreventer.IsForbidden(reflect.Indirect(reflect.ValueOf(obj)).Type().PkgPath()) {
|
||||
return fmt.Errorf(printers.InternalObjectPrinterErr)
|
||||
}
|
||||
|
||||
if w, found := out.(*tabwriter.Writer); !found {
|
||||
w = utilprinters.GetNewTabWriter(out)
|
||||
out = w
|
||||
defer w.Flush()
|
||||
}
|
||||
|
||||
t := reflect.TypeOf(obj)
|
||||
if !s.NoHeaders && t != s.lastType {
|
||||
headers := make([]string, len(s.Columns))
|
||||
for ix := range s.Columns {
|
||||
headers[ix] = s.Columns[ix].Header
|
||||
}
|
||||
fmt.Fprintln(out, strings.Join(headers, "\t"))
|
||||
s.lastType = t
|
||||
}
|
||||
parsers := make([]*jsonpath.JSONPath, len(s.Columns))
|
||||
for ix := range s.Columns {
|
||||
parsers[ix] = jsonpath.New(fmt.Sprintf("column%d", ix)).AllowMissingKeys(true)
|
||||
if err := parsers[ix].Parse(s.Columns[ix].FieldSpec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if meta.IsListType(obj) {
|
||||
objs, err := meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for ix := range objs {
|
||||
if err := s.printOneObject(objs[ix], parsers, out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := s.printOneObject(obj, parsers, out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jsonpath.JSONPath, out io.Writer) error {
|
||||
columns := make([]string, len(parsers))
|
||||
switch u := obj.(type) {
|
||||
case *runtime.Unknown:
|
||||
if len(u.Raw) > 0 {
|
||||
var err error
|
||||
if obj, err = runtime.Decode(s.Decoder, u.Raw); err != nil {
|
||||
return fmt.Errorf("can't decode object for printing: %v (%s)", err, u.Raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ix := range parsers {
|
||||
parser := parsers[ix]
|
||||
|
||||
var values [][]reflect.Value
|
||||
var err error
|
||||
if unstructured, ok := obj.(runtime.Unstructured); ok {
|
||||
values, err = parser.FindResults(unstructured.UnstructuredContent())
|
||||
} else {
|
||||
values, err = parser.FindResults(reflect.ValueOf(obj).Elem().Interface())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
valueStrings := []string{}
|
||||
if len(values) == 0 || len(values[0]) == 0 {
|
||||
valueStrings = append(valueStrings, "<none>")
|
||||
}
|
||||
for arrIx := range values {
|
||||
for valIx := range values[arrIx] {
|
||||
valueStrings = append(valueStrings, fmt.Sprintf("%v", values[arrIx][valIx].Interface()))
|
||||
}
|
||||
}
|
||||
columns[ix] = strings.Join(valueStrings, ",")
|
||||
}
|
||||
fmt.Fprintln(out, strings.Join(columns, "\t"))
|
||||
return nil
|
||||
}
|
111
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/customcolumn_flags.go
generated
vendored
Normal file
111
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/customcolumn_flags.go
generated
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package get
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
)
|
||||
|
||||
var columnsFormats = map[string]bool{
|
||||
"custom-columns-file": true,
|
||||
"custom-columns": true,
|
||||
}
|
||||
|
||||
// CustomColumnsPrintFlags provides default flags necessary for printing
|
||||
// custom resource columns from an inline-template or file.
|
||||
type CustomColumnsPrintFlags struct {
|
||||
NoHeaders bool
|
||||
TemplateArgument string
|
||||
}
|
||||
|
||||
func (f *CustomColumnsPrintFlags) AllowedFormats() []string {
|
||||
formats := make([]string, 0, len(columnsFormats))
|
||||
for format := range columnsFormats {
|
||||
formats = append(formats, format)
|
||||
}
|
||||
return formats
|
||||
}
|
||||
|
||||
// ToPrinter receives an templateFormat and returns a printer capable of
|
||||
// handling custom-column printing.
|
||||
// Returns false if the specified templateFormat does not match a supported format.
|
||||
// Supported format types can be found in pkg/printers/printers.go
|
||||
func (f *CustomColumnsPrintFlags) ToPrinter(templateFormat string) (printers.ResourcePrinter, error) {
|
||||
if len(templateFormat) == 0 {
|
||||
return nil, genericclioptions.NoCompatiblePrinterError{}
|
||||
}
|
||||
|
||||
templateValue := ""
|
||||
|
||||
if len(f.TemplateArgument) == 0 {
|
||||
for format := range columnsFormats {
|
||||
format = format + "="
|
||||
if strings.HasPrefix(templateFormat, format) {
|
||||
templateValue = templateFormat[len(format):]
|
||||
templateFormat = format[:len(format)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
templateValue = f.TemplateArgument
|
||||
}
|
||||
|
||||
if _, supportedFormat := columnsFormats[templateFormat]; !supportedFormat {
|
||||
return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &templateFormat, AllowedFormats: f.AllowedFormats()}
|
||||
}
|
||||
|
||||
if len(templateValue) == 0 {
|
||||
return nil, fmt.Errorf("custom-columns format specified but no custom columns given")
|
||||
}
|
||||
|
||||
// UniversalDecoder call must specify parameter versions; otherwise it will decode to internal versions.
|
||||
decoder := scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
if templateFormat == "custom-columns-file" {
|
||||
file, err := os.Open(templateValue)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading template %s, %v\n", templateValue, err)
|
||||
}
|
||||
defer file.Close()
|
||||
p, err := NewCustomColumnsPrinterFromTemplate(file, decoder)
|
||||
return p, err
|
||||
}
|
||||
|
||||
return NewCustomColumnsPrinterFromSpec(templateValue, decoder, f.NoHeaders)
|
||||
}
|
||||
|
||||
// AddFlags receives a *cobra.Command reference and binds
|
||||
// flags related to custom-columns printing
|
||||
func (f *CustomColumnsPrintFlags) AddFlags(c *cobra.Command) {}
|
||||
|
||||
// NewCustomColumnsPrintFlags returns flags associated with
|
||||
// custom-column printing, with default values set.
|
||||
// NoHeaders and TemplateArgument should be set by callers.
|
||||
func NewCustomColumnsPrintFlags() *CustomColumnsPrintFlags {
|
||||
return &CustomColumnsPrintFlags{
|
||||
NoHeaders: false,
|
||||
TemplateArgument: "",
|
||||
}
|
||||
}
|
139
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/customcolumn_flags_test.go
generated
vendored
Normal file
139
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/customcolumn_flags_test.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package get
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
)
|
||||
|
||||
func TestPrinterSupportsExpectedCustomColumnFormats(t *testing.T) {
|
||||
testObject := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||
|
||||
customColumnsFile, err := ioutil.TempFile("", "printers_jsonpath_flags")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer func(tempFile *os.File) {
|
||||
tempFile.Close()
|
||||
os.Remove(tempFile.Name())
|
||||
}(customColumnsFile)
|
||||
|
||||
fmt.Fprintf(customColumnsFile, "NAME\n.metadata.name")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
outputFormat string
|
||||
templateArg string
|
||||
expectedError string
|
||||
expectedParseError string
|
||||
expectedOutput string
|
||||
expectNoMatch bool
|
||||
}{
|
||||
{
|
||||
name: "valid output format also containing the custom-columns argument succeeds",
|
||||
outputFormat: "custom-columns=NAME:.metadata.name",
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "valid output format and no --template argument results in an error",
|
||||
outputFormat: "custom-columns",
|
||||
expectedError: "custom-columns format specified but no custom columns given",
|
||||
},
|
||||
{
|
||||
name: "valid output format and --template argument succeeds",
|
||||
outputFormat: "custom-columns",
|
||||
templateArg: "NAME:.metadata.name",
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "custom-columns template file should match, and successfully return correct value",
|
||||
outputFormat: "custom-columns-file",
|
||||
templateArg: customColumnsFile.Name(),
|
||||
expectedOutput: "foo",
|
||||
},
|
||||
{
|
||||
name: "valid output format and invalid --template argument results in a parsing error from the printer",
|
||||
outputFormat: "custom-columns",
|
||||
templateArg: "invalid",
|
||||
expectedError: "unexpected custom-columns spec: invalid, expected <header>:<json-path-expr>",
|
||||
},
|
||||
{
|
||||
name: "no printer is matched on an invalid outputFormat",
|
||||
outputFormat: "invalid",
|
||||
expectNoMatch: true,
|
||||
},
|
||||
{
|
||||
name: "custom-columns printer should not match on any other format supported by another printer",
|
||||
outputFormat: "go-template",
|
||||
expectNoMatch: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
printFlags := CustomColumnsPrintFlags{
|
||||
TemplateArgument: tc.templateArg,
|
||||
}
|
||||
|
||||
p, err := printFlags.ToPrinter(tc.outputFormat)
|
||||
if tc.expectNoMatch {
|
||||
if !genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
|
||||
}
|
||||
return
|
||||
}
|
||||
if genericclioptions.IsNoCompatiblePrinterError(err) {
|
||||
t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
|
||||
}
|
||||
|
||||
if len(tc.expectedError) > 0 {
|
||||
if err == nil || !strings.Contains(err.Error(), tc.expectedError) {
|
||||
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
err = p.PrintObj(testObject, out)
|
||||
if len(tc.expectedParseError) > 0 {
|
||||
if err == nil || !strings.Contains(err.Error(), tc.expectedParseError) {
|
||||
t.Errorf("expecting error %q, got %v", tc.expectedError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(out.String(), tc.expectedOutput) {
|
||||
t.Errorf("unexpected output: expecting %q, got %q", tc.expectedOutput, out.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
375
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/customcolumn_test.go
generated
vendored
Normal file
375
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/customcolumn_test.go
generated
vendored
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package get
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/printers"
|
||||
)
|
||||
|
||||
// UniversalDecoder call must specify parameter versions; otherwise it will decode to internal versions.
|
||||
var decoder = scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
func TestMassageJSONPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
expectErr bool
|
||||
}{
|
||||
{input: "foo.bar", expectedOutput: "{.foo.bar}"},
|
||||
{input: "{foo.bar}", expectedOutput: "{.foo.bar}"},
|
||||
{input: ".foo.bar", expectedOutput: "{.foo.bar}"},
|
||||
{input: "{.foo.bar}", expectedOutput: "{.foo.bar}"},
|
||||
{input: "", expectedOutput: ""},
|
||||
{input: "{foo.bar", expectErr: true},
|
||||
{input: "foo.bar}", expectErr: true},
|
||||
{input: "{foo.bar}}", expectErr: true},
|
||||
{input: "{{foo.bar}", expectErr: true},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
output, err := RelaxedJSONPathExpression(test.input)
|
||||
if err != nil && !test.expectErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
}
|
||||
return
|
||||
}
|
||||
if output != test.expectedOutput {
|
||||
t.Errorf("input: %s, expected: %s, saw: %s", test.input, test.expectedOutput, output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewColumnPrinterFromSpec(t *testing.T) {
|
||||
tests := []struct {
|
||||
spec string
|
||||
expectedColumns []Column
|
||||
expectErr bool
|
||||
name string
|
||||
noHeaders bool
|
||||
}{
|
||||
{
|
||||
spec: "",
|
||||
expectErr: true,
|
||||
name: "empty",
|
||||
},
|
||||
{
|
||||
spec: "invalid",
|
||||
expectErr: true,
|
||||
name: "invalid1",
|
||||
},
|
||||
{
|
||||
spec: "invalid=foobar",
|
||||
expectErr: true,
|
||||
name: "invalid2",
|
||||
},
|
||||
{
|
||||
spec: "invalid,foobar:blah",
|
||||
expectErr: true,
|
||||
name: "invalid3",
|
||||
},
|
||||
{
|
||||
spec: "NAME:metadata.name,API_VERSION:apiVersion",
|
||||
name: "ok",
|
||||
expectedColumns: []Column{
|
||||
{
|
||||
Header: "NAME",
|
||||
FieldSpec: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
Header: "API_VERSION",
|
||||
FieldSpec: "{.apiVersion}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
spec: "API_VERSION:apiVersion",
|
||||
name: "no-headers",
|
||||
noHeaders: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
printer, err := NewCustomColumnsPrinterFromSpec(test.spec, decoder, test.noHeaders)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Errorf("[%s] unexpected non-error", test.name)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
||||
return
|
||||
}
|
||||
if test.noHeaders {
|
||||
buffer := &bytes.Buffer{}
|
||||
|
||||
printer.PrintObj(&corev1.Pod{}, buffer)
|
||||
if err != nil {
|
||||
t.Fatalf("An error occurred printing Pod: %#v", err)
|
||||
}
|
||||
|
||||
if contains(strings.Fields(buffer.String()), "API_VERSION") {
|
||||
t.Errorf("unexpected header API_VERSION")
|
||||
}
|
||||
|
||||
} else if !reflect.DeepEqual(test.expectedColumns, printer.Columns) {
|
||||
t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v\n", test.name, test.expectedColumns, printer.Columns)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func contains(arr []string, s string) bool {
|
||||
for i := range arr {
|
||||
if arr[i] == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const exampleTemplateOne = `NAME API_VERSION
|
||||
{metadata.name} {apiVersion}`
|
||||
|
||||
const exampleTemplateTwo = `NAME API_VERSION
|
||||
{metadata.name} {apiVersion}`
|
||||
|
||||
func TestNewColumnPrinterFromTemplate(t *testing.T) {
|
||||
tests := []struct {
|
||||
spec string
|
||||
expectedColumns []Column
|
||||
expectErr bool
|
||||
name string
|
||||
}{
|
||||
{
|
||||
spec: "",
|
||||
expectErr: true,
|
||||
name: "empty",
|
||||
},
|
||||
{
|
||||
spec: "invalid",
|
||||
expectErr: true,
|
||||
name: "invalid1",
|
||||
},
|
||||
{
|
||||
spec: "invalid=foobar",
|
||||
expectErr: true,
|
||||
name: "invalid2",
|
||||
},
|
||||
{
|
||||
spec: "invalid,foobar:blah",
|
||||
expectErr: true,
|
||||
name: "invalid3",
|
||||
},
|
||||
{
|
||||
spec: exampleTemplateOne,
|
||||
name: "ok",
|
||||
expectedColumns: []Column{
|
||||
{
|
||||
Header: "NAME",
|
||||
FieldSpec: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
Header: "API_VERSION",
|
||||
FieldSpec: "{.apiVersion}",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
spec: exampleTemplateTwo,
|
||||
name: "ok-2",
|
||||
expectedColumns: []Column{
|
||||
{
|
||||
Header: "NAME",
|
||||
FieldSpec: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
Header: "API_VERSION",
|
||||
FieldSpec: "{.apiVersion}",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
reader := bytes.NewBufferString(test.spec)
|
||||
printer, err := NewCustomColumnsPrinterFromTemplate(reader, decoder)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Errorf("[%s] unexpected non-error", test.name)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("[%s] unexpected error: %v", test.name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedColumns, printer.Columns) {
|
||||
t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v\n", test.name, test.expectedColumns, printer.Columns)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestColumnPrint(t *testing.T) {
|
||||
tests := []struct {
|
||||
columns []Column
|
||||
obj runtime.Object
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
columns: []Column{
|
||||
{
|
||||
Header: "NAME",
|
||||
FieldSpec: "{.metadata.name}",
|
||||
},
|
||||
},
|
||||
obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
|
||||
expectedOutput: `NAME
|
||||
foo
|
||||
`,
|
||||
},
|
||||
{
|
||||
columns: []Column{
|
||||
{
|
||||
Header: "NAME",
|
||||
FieldSpec: "{.metadata.name}",
|
||||
},
|
||||
},
|
||||
obj: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "bar"}},
|
||||
},
|
||||
},
|
||||
expectedOutput: `NAME
|
||||
foo
|
||||
bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
columns: []Column{
|
||||
{
|
||||
Header: "NAME",
|
||||
FieldSpec: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
Header: "API_VERSION",
|
||||
FieldSpec: "{.apiVersion}",
|
||||
},
|
||||
},
|
||||
obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
|
||||
expectedOutput: `NAME API_VERSION
|
||||
foo baz
|
||||
`,
|
||||
},
|
||||
{
|
||||
columns: []Column{
|
||||
{
|
||||
Header: "NAME",
|
||||
FieldSpec: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
Header: "API_VERSION",
|
||||
FieldSpec: "{.apiVersion}",
|
||||
},
|
||||
{
|
||||
Header: "NOT_FOUND",
|
||||
FieldSpec: "{.notFound}",
|
||||
},
|
||||
},
|
||||
obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
|
||||
expectedOutput: `NAME API_VERSION NOT_FOUND
|
||||
foo baz <none>
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.expectedOutput, func(t *testing.T) {
|
||||
printer := &CustomColumnsPrinter{
|
||||
Columns: test.columns,
|
||||
Decoder: decoder,
|
||||
}
|
||||
buffer := &bytes.Buffer{}
|
||||
if err := printer.PrintObj(test.obj, buffer); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if buffer.String() != test.expectedOutput {
|
||||
t.Errorf("\nexpected:\n'%s'\nsaw\n'%s'\n", test.expectedOutput, buffer.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// this mimics how resource/get.go calls the customcolumn printer
|
||||
func TestIndividualPrintObjOnExistingTabWriter(t *testing.T) {
|
||||
columns := []Column{
|
||||
{
|
||||
Header: "NAME",
|
||||
FieldSpec: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
Header: "LONG COLUMN NAME", // name is longer than all values of label1
|
||||
FieldSpec: "{.metadata.labels.label1}",
|
||||
},
|
||||
{
|
||||
Header: "LABEL 2",
|
||||
FieldSpec: "{.metadata.labels.label2}",
|
||||
},
|
||||
}
|
||||
objects := []*corev1.Pod{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"label1": "foo", "label2": "foo"}}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{"label1": "bar", "label2": "bar"}}},
|
||||
}
|
||||
expectedOutput := `NAME LONG COLUMN NAME LABEL 2
|
||||
foo foo foo
|
||||
bar bar bar
|
||||
`
|
||||
|
||||
buffer := &bytes.Buffer{}
|
||||
tabWriter := printers.GetNewTabWriter(buffer)
|
||||
printer := &CustomColumnsPrinter{
|
||||
Columns: columns,
|
||||
Decoder: decoder,
|
||||
}
|
||||
for _, obj := range objects {
|
||||
if err := printer.PrintObj(obj, tabWriter); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
tabWriter.Flush()
|
||||
if buffer.String() != expectedOutput {
|
||||
t.Errorf("\nexpected:\n'%s'\nsaw\n'%s'\n", expectedOutput, buffer.String())
|
||||
}
|
||||
}
|
253
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/get.go
generated
vendored
253
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/get.go
generated
vendored
@ -17,14 +17,16 @@ limitations under the License.
|
||||
package get
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kapierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -35,23 +37,23 @@ import (
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
|
||||
"k8s.io/client-go/rest"
|
||||
watchtools "k8s.io/client-go/tools/watch"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
utilprinters "k8s.io/kubernetes/pkg/kubectl/util/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/templates"
|
||||
"k8s.io/kubernetes/pkg/util/interrupt"
|
||||
)
|
||||
|
||||
// GetOptions contains the input to the get command.
|
||||
type GetOptions struct {
|
||||
PrintFlags *PrintFlags
|
||||
ToPrinter func(*meta.RESTMapping, bool) (printers.ResourcePrinterFunc, error)
|
||||
ToPrinter func(*meta.RESTMapping, bool, bool) (printers.ResourcePrinterFunc, error)
|
||||
IsHumanReadablePrinter bool
|
||||
PrintWithOpenAPICols bool
|
||||
|
||||
@ -148,11 +150,11 @@ func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStr
|
||||
o := NewGetOptions(parent, streams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [(-o|--output=)json|yaml|wide|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags]",
|
||||
Use: "get [(-o|--output=)json|yaml|wide|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags]",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Display one or many resources"),
|
||||
Long: getLong + "\n\n" + cmdutil.SuggestApiResources(parent),
|
||||
Example: getExample,
|
||||
Short: i18n.T("Display one or many resources"),
|
||||
Long: getLong + "\n\n" + cmdutil.SuggestApiResources(parent),
|
||||
Example: getExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
cmdutil.CheckErr(o.Validate(cmd))
|
||||
@ -205,10 +207,10 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
||||
|
||||
o.NoHeaders = cmdutil.GetFlagBool(cmd, "no-headers")
|
||||
|
||||
// TODO (soltysh): currently we don't support sorting and custom columns
|
||||
// TODO (soltysh): currently we don't support custom columns
|
||||
// with server side print. So in these cases force the old behavior.
|
||||
outputOption := cmd.Flags().Lookup("output").Value.String()
|
||||
if o.Sort && outputOption == "custom-columns" {
|
||||
if outputOption == "custom-columns" {
|
||||
o.ServerPrint = false
|
||||
}
|
||||
|
||||
@ -224,10 +226,7 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
||||
|
||||
o.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
|
||||
|
||||
if resource.MultipleTypesRequested(args) {
|
||||
o.PrintFlags.EnsureWithKind()
|
||||
}
|
||||
o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinterFunc, error) {
|
||||
o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
|
||||
// make a new copy of current flags / opts before mutating
|
||||
printFlags := o.PrintFlags.Copy()
|
||||
|
||||
@ -242,6 +241,9 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
||||
if withNamespace {
|
||||
printFlags.EnsureWithNamespace()
|
||||
}
|
||||
if withKind {
|
||||
printFlags.EnsureWithKind()
|
||||
}
|
||||
|
||||
printer, err := printFlags.ToPrinter()
|
||||
if err != nil {
|
||||
@ -294,6 +296,99 @@ func (o *GetOptions) Validate(cmd *cobra.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type OriginalPositioner interface {
|
||||
OriginalPosition(int) int
|
||||
}
|
||||
|
||||
type NopPositioner struct{}
|
||||
|
||||
func (t *NopPositioner) OriginalPosition(ix int) int {
|
||||
return ix
|
||||
}
|
||||
|
||||
type RuntimeSorter struct {
|
||||
field string
|
||||
decoder runtime.Decoder
|
||||
objects []runtime.Object
|
||||
positioner OriginalPositioner
|
||||
}
|
||||
|
||||
func (r *RuntimeSorter) Sort() error {
|
||||
// a list is only considered "sorted" if there are 0 or 1 items in it
|
||||
// AND (if 1 item) the item is not a Table object
|
||||
if len(r.objects) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(r.objects) == 1 {
|
||||
_, isTable := r.objects[0].(*metav1beta1.Table)
|
||||
if !isTable {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
includesTable := false
|
||||
includesRuntimeObjs := false
|
||||
|
||||
for _, obj := range r.objects {
|
||||
switch t := obj.(type) {
|
||||
case *metav1beta1.Table:
|
||||
includesTable = true
|
||||
|
||||
if err := NewTableSorter(t, r.field).Sort(); err != nil {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
includesRuntimeObjs = true
|
||||
}
|
||||
}
|
||||
|
||||
// we use a NopPositioner when dealing with Table objects
|
||||
// because the objects themselves are not swapped, but rather
|
||||
// the rows in each object are swapped / sorted.
|
||||
r.positioner = &NopPositioner{}
|
||||
|
||||
if includesRuntimeObjs && includesTable {
|
||||
return fmt.Errorf("sorting is not supported on mixed Table and non-Table object lists")
|
||||
}
|
||||
if includesTable {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if not dealing with a Table response from the server, assume
|
||||
// all objects are runtime.Object as usual, and sort using old method.
|
||||
var err error
|
||||
if r.positioner, err = SortObjects(r.decoder, r.objects, r.field); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RuntimeSorter) OriginalPosition(ix int) int {
|
||||
if r.positioner == nil {
|
||||
return 0
|
||||
}
|
||||
return r.positioner.OriginalPosition(ix)
|
||||
}
|
||||
|
||||
// allows custom decoder to be set for testing
|
||||
func (r *RuntimeSorter) WithDecoder(decoder runtime.Decoder) *RuntimeSorter {
|
||||
r.decoder = decoder
|
||||
return r
|
||||
}
|
||||
|
||||
func NewRuntimeSorter(objects []runtime.Object, sortBy string) *RuntimeSorter {
|
||||
parsedField, err := RelaxedJSONPathExpression(sortBy)
|
||||
if err != nil {
|
||||
parsedField = sortBy
|
||||
}
|
||||
|
||||
return &RuntimeSorter{
|
||||
field: parsedField,
|
||||
decoder: legacyscheme.Codecs.UniversalDecoder(),
|
||||
objects: objects,
|
||||
}
|
||||
}
|
||||
|
||||
// Run performs the get operation.
|
||||
// TODO: remove the need to pass these arguments, like other commands.
|
||||
func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
@ -304,6 +399,18 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
return o.watch(f, cmd, args)
|
||||
}
|
||||
|
||||
// openapi printing is mutually exclusive with server side printing
|
||||
if o.PrintWithOpenAPICols && o.ServerPrint {
|
||||
fmt.Fprintf(o.IOStreams.ErrOut, "warning: --%s requested, --%s will be ignored\n", useOpenAPIPrintColumnFlagLabel, useServerPrintColumns)
|
||||
}
|
||||
|
||||
chunkSize := o.ChunkSize
|
||||
if o.Sort {
|
||||
// TODO(juanvallejo): in the future, we could have the client use chunking
|
||||
// to gather all results, then sort them all at the end to reduce server load.
|
||||
chunkSize = 0
|
||||
}
|
||||
|
||||
r := f.NewBuilder().
|
||||
Unstructured().
|
||||
NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
|
||||
@ -311,7 +418,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
LabelSelectorParam(o.LabelSelector).
|
||||
FieldSelectorParam(o.FieldSelector).
|
||||
ExportParam(o.Export).
|
||||
RequestChunksOf(o.ChunkSize).
|
||||
RequestChunksOf(chunkSize).
|
||||
IncludeUninitialized(o.IncludeUninitialized).
|
||||
ResourceTypeOrNameArgs(true, args...).
|
||||
ContinueOnError().
|
||||
@ -322,12 +429,19 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
if o.PrintWithOpenAPICols {
|
||||
return
|
||||
}
|
||||
if o.ServerPrint && o.IsHumanReadablePrinter && !o.Sort {
|
||||
group := metav1beta1.GroupName
|
||||
version := metav1beta1.SchemeGroupVersion.Version
|
||||
if !o.ServerPrint || !o.IsHumanReadablePrinter {
|
||||
return
|
||||
}
|
||||
|
||||
tableParam := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
|
||||
req.SetHeader("Accept", tableParam)
|
||||
group := metav1beta1.GroupName
|
||||
version := metav1beta1.SchemeGroupVersion.Version
|
||||
|
||||
tableParam := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
|
||||
req.SetHeader("Accept", tableParam)
|
||||
|
||||
// if sorting, ensure we receive the full object in order to introspect its fields via jsonpath
|
||||
if o.Sort {
|
||||
req.Param("includeObject", "Object")
|
||||
}
|
||||
}).
|
||||
Do()
|
||||
@ -349,6 +463,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
printWithKind := multipleGVKsRequested(infos)
|
||||
|
||||
objs := make([]runtime.Object, len(infos))
|
||||
for ix := range infos {
|
||||
@ -359,7 +474,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
} else {
|
||||
// if we are unable to decode server response into a v1beta1.Table,
|
||||
// fallback to client-side printing with whatever info the server returned.
|
||||
glog.V(2).Infof("Unable to decode server response into a Table. Falling back to hardcoded types: %v", err)
|
||||
klog.V(2).Infof("Unable to decode server response into a Table. Falling back to hardcoded types: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,23 +485,25 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sorter *kubectl.RuntimeSort
|
||||
if o.Sort && len(objs) > 1 {
|
||||
// TODO: questionable
|
||||
if sorter, err = kubectl.SortObjects(cmdutil.InternalVersionDecoder(), objs, sorting); err != nil {
|
||||
|
||||
var positioner OriginalPositioner
|
||||
if o.Sort {
|
||||
sorter := NewRuntimeSorter(objs, sorting)
|
||||
if err := sorter.Sort(); err != nil {
|
||||
return err
|
||||
}
|
||||
positioner = sorter
|
||||
}
|
||||
|
||||
var printer printers.ResourcePrinter
|
||||
var lastMapping *meta.RESTMapping
|
||||
nonEmptyObjCount := 0
|
||||
w := printers.GetNewTabWriter(o.Out)
|
||||
w := utilprinters.GetNewTabWriter(o.Out)
|
||||
for ix := range objs {
|
||||
var mapping *meta.RESTMapping
|
||||
var info *resource.Info
|
||||
if sorter != nil {
|
||||
info = infos[sorter.OriginalPosition(ix)]
|
||||
if positioner != nil {
|
||||
info = infos[positioner.OriginalPosition(ix)]
|
||||
mapping = info.Mapping
|
||||
} else {
|
||||
info = infos[ix]
|
||||
@ -404,6 +521,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
nonEmptyObjCount++
|
||||
|
||||
printWithNamespace := o.AllNamespaces
|
||||
|
||||
if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
|
||||
printWithNamespace = false
|
||||
}
|
||||
@ -418,7 +536,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
fmt.Fprintln(o.ErrOut)
|
||||
}
|
||||
|
||||
printer, err = o.ToPrinter(mapping, printWithNamespace)
|
||||
printer, err = o.ToPrinter(mapping, printWithNamespace, printWithKind)
|
||||
if err != nil {
|
||||
if !errs.Has(err.Error()) {
|
||||
errs.Insert(err.Error())
|
||||
@ -440,14 +558,14 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
internalObj, err := legacyscheme.Scheme.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion())
|
||||
if err != nil {
|
||||
// if there's an error, try to print what you have (mirrors old behavior).
|
||||
glog.V(1).Info(err)
|
||||
klog.V(1).Info(err)
|
||||
printer.PrintObj(info.Object, w)
|
||||
} else {
|
||||
printer.PrintObj(internalObj, w)
|
||||
}
|
||||
}
|
||||
w.Flush()
|
||||
if nonEmptyObjCount == 0 && !o.IgnoreNotFound {
|
||||
if nonEmptyObjCount == 0 && !o.IgnoreNotFound && len(allErrs) == 0 {
|
||||
fmt.Fprintln(o.ErrOut, "No resources found.")
|
||||
}
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
@ -497,30 +615,13 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(infos) > 1 {
|
||||
gvk := infos[0].Mapping.GroupVersionKind
|
||||
uniqueGVKs := 1
|
||||
|
||||
// If requesting a resource count greater than a request's --chunk-size,
|
||||
// we will end up making multiple requests to the server, with each
|
||||
// request producing its own "Info" object. Although overall we are
|
||||
// dealing with a single resource type, we will end up with multiple
|
||||
// infos returned by the builder. To handle this case, only fail if we
|
||||
// have at least one info with a different GVK than the others.
|
||||
for _, info := range infos {
|
||||
if info.Mapping.GroupVersionKind != gvk {
|
||||
uniqueGVKs++
|
||||
}
|
||||
}
|
||||
|
||||
if uniqueGVKs > 1 {
|
||||
return i18n.Errorf("watch is only supported on individual resources and resource collections - %d resources were found", uniqueGVKs)
|
||||
}
|
||||
if multipleGVKsRequested(infos) {
|
||||
return i18n.Errorf("watch is only supported on individual resources and resource collections - more than 1 resources were found")
|
||||
}
|
||||
|
||||
info := infos[0]
|
||||
mapping := info.ResourceMapping()
|
||||
printer, err := o.ToPrinter(mapping, o.AllNamespaces)
|
||||
printer, err := o.ToPrinter(mapping, o.AllNamespaces, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -547,7 +648,7 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
// print the current object
|
||||
if !o.WatchOnly {
|
||||
var objsToPrint []runtime.Object
|
||||
writer := printers.GetNewTabWriter(o.Out)
|
||||
writer := utilprinters.GetNewTabWriter(o.Out)
|
||||
|
||||
if isList {
|
||||
objsToPrint, _ = meta.ExtractList(obj)
|
||||
@ -574,9 +675,11 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
}
|
||||
|
||||
first := true
|
||||
intr := interrupt.New(nil, w.Stop)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
intr := interrupt.New(nil, cancel)
|
||||
intr.Run(func() error {
|
||||
_, err := watch.Until(0, w, func(e watch.Event) (bool, error) {
|
||||
_, err := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
|
||||
if !isList && first {
|
||||
// drop the initial watch event in the single resource case
|
||||
first = false
|
||||
@ -585,8 +688,12 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
|
||||
// printing always takes the internal version, but the watch event uses externals
|
||||
// TODO fix printing to use server-side or be version agnostic
|
||||
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
|
||||
if err := printer.PrintObj(attemptToConvertToInternal(e.Object, legacyscheme.Scheme, internalGV), o.Out); err != nil {
|
||||
objToPrint := e.Object
|
||||
if o.IsHumanReadablePrinter {
|
||||
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
|
||||
objToPrint = attemptToConvertToInternal(e.Object, legacyscheme.Scheme, internalGV)
|
||||
}
|
||||
if err := printer.PrintObj(objToPrint, o.Out); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, nil
|
||||
@ -600,7 +707,7 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
func attemptToConvertToInternal(obj runtime.Object, converter runtime.ObjectConvertor, targetVersion schema.GroupVersion) runtime.Object {
|
||||
internalObject, err := converter.ConvertToVersion(obj, targetVersion)
|
||||
if err != nil {
|
||||
glog.V(1).Infof("Unable to convert %T to %v: err", obj, targetVersion, err)
|
||||
klog.V(1).Infof("Unable to convert %T to %v: %v", obj, targetVersion, err)
|
||||
return obj
|
||||
}
|
||||
return internalObject
|
||||
@ -654,7 +761,7 @@ func (o *GetOptions) printGeneric(r *resource.Result) error {
|
||||
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
|
||||
}
|
||||
|
||||
printer, err := o.ToPrinter(nil, false)
|
||||
printer, err := o.ToPrinter(nil, false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -664,8 +771,8 @@ func (o *GetOptions) printGeneric(r *resource.Result) error {
|
||||
// we have more than one item, so coerce all items into a list.
|
||||
// we don't want an *unstructured.Unstructured list yet, as we
|
||||
// may be dealing with non-unstructured objects. Compose all items
|
||||
// into an api.List, and then decode using an unstructured scheme.
|
||||
list := api.List{
|
||||
// into an corev1.List, and then decode using an unstructured scheme.
|
||||
list := corev1.List{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "List",
|
||||
APIVersion: "v1",
|
||||
@ -673,7 +780,7 @@ func (o *GetOptions) printGeneric(r *resource.Result) error {
|
||||
ListMeta: metav1.ListMeta{},
|
||||
}
|
||||
for _, info := range infos {
|
||||
list.Items = append(list.Items, info.Object)
|
||||
list.Items = append(list.Items, runtime.RawExtension{Object: info.Object})
|
||||
}
|
||||
|
||||
listData, err := json.Marshal(list)
|
||||
@ -731,6 +838,7 @@ func (o *GetOptions) printGeneric(r *resource.Result) error {
|
||||
|
||||
func addOpenAPIPrintColumnFlags(cmd *cobra.Command, opt *GetOptions) {
|
||||
cmd.Flags().BoolVar(&opt.PrintWithOpenAPICols, useOpenAPIPrintColumnFlagLabel, opt.PrintWithOpenAPICols, "If true, use x-kubernetes-print-column metadata (if present) from the OpenAPI schema for displaying a resource.")
|
||||
cmd.Flags().MarkDeprecated(useOpenAPIPrintColumnFlagLabel, "deprecated in favor of server-side printing")
|
||||
}
|
||||
|
||||
func addServerPrintColumnFlags(cmd *cobra.Command, opt *GetOptions) {
|
||||
@ -747,10 +855,23 @@ func cmdSpecifiesOutputFmt(cmd *cobra.Command) bool {
|
||||
|
||||
func maybeWrapSortingPrinter(printer printers.ResourcePrinter, sortBy string) printers.ResourcePrinter {
|
||||
if len(sortBy) != 0 {
|
||||
return &kubectl.SortingPrinter{
|
||||
return &SortingPrinter{
|
||||
Delegate: printer,
|
||||
SortField: fmt.Sprintf("%s", sortBy),
|
||||
}
|
||||
}
|
||||
return printer
|
||||
}
|
||||
|
||||
func multipleGVKsRequested(infos []*resource.Info) bool {
|
||||
if len(infos) < 2 {
|
||||
return false
|
||||
}
|
||||
gvk := infos[0].Mapping.GroupVersionKind
|
||||
for _, info := range infos {
|
||||
if info.Mapping.GroupVersionKind != gvk {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
8
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/get_flags.go
generated
vendored
8
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/get_flags.go
generated
vendored
@ -23,9 +23,9 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
// PrintFlags composes common printer flag structs
|
||||
@ -33,7 +33,7 @@ import (
|
||||
type PrintFlags struct {
|
||||
JSONYamlPrintFlags *genericclioptions.JSONYamlPrintFlags
|
||||
NamePrintFlags *genericclioptions.NamePrintFlags
|
||||
CustomColumnsFlags *printers.CustomColumnsPrintFlags
|
||||
CustomColumnsFlags *CustomColumnsPrintFlags
|
||||
HumanReadableFlags *HumanPrintFlags
|
||||
TemplateFlags *genericclioptions.KubeTemplatePrintFlags
|
||||
|
||||
@ -185,6 +185,6 @@ func NewGetPrintFlags() *PrintFlags {
|
||||
TemplateFlags: genericclioptions.NewKubeTemplatePrintFlags(),
|
||||
|
||||
HumanReadableFlags: NewHumanPrintFlags(),
|
||||
CustomColumnsFlags: printers.NewCustomColumnsPrintFlags(),
|
||||
CustomColumnsFlags: NewCustomColumnsPrintFlags(),
|
||||
}
|
||||
}
|
||||
|
827
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/get_test.go
generated
vendored
827
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/get_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
2
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/humanreadable_flags.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/humanreadable_flags.go
generated
vendored
@ -18,7 +18,7 @@ package get
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
|
6
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/humanreadable_flags_test.go
generated
vendored
6
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/humanreadable_flags_test.go
generated
vendored
@ -24,8 +24,8 @@ import (
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
)
|
||||
|
||||
func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
|
||||
@ -61,7 +61,7 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
|
||||
{
|
||||
name: "\"wide\" output format prints",
|
||||
outputFormat: "wide",
|
||||
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\n",
|
||||
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +IP\\ +NODE\\ +NOMINATED NODE\\ +READINESS GATES\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
|
||||
},
|
||||
{
|
||||
name: "no-headers prints output with no headers",
|
||||
@ -72,7 +72,7 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
|
||||
name: "no-headers and a \"wide\" output format prints output with no headers and additional columns",
|
||||
outputFormat: "wide",
|
||||
noHeaders: true,
|
||||
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\n",
|
||||
expectedOutput: "foo\\ +0/0\\ +0\\ +<unknown>\\ +<none>\\ +<none>\\ +<none>\\ +<none>\n",
|
||||
},
|
||||
{
|
||||
name: "show-kind displays the resource's kind, even when printing a single type of resource",
|
||||
|
373
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/sorter.go
generated
vendored
Normal file
373
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/sorter.go
generated
vendored
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package get
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
|
||||
"k8s.io/client-go/util/integer"
|
||||
"k8s.io/client-go/util/jsonpath"
|
||||
|
||||
"vbom.ml/util/sortorder"
|
||||
)
|
||||
|
||||
// SortingPrinter sorts list types before delegating to another printer.
|
||||
// Non-list types are simply passed through
|
||||
type SortingPrinter struct {
|
||||
SortField string
|
||||
Delegate printers.ResourcePrinter
|
||||
Decoder runtime.Decoder
|
||||
}
|
||||
|
||||
func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||
if !meta.IsListType(obj) {
|
||||
return s.Delegate.PrintObj(obj, out)
|
||||
}
|
||||
|
||||
if err := s.sortObj(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Delegate.PrintObj(obj, out)
|
||||
}
|
||||
|
||||
func (s *SortingPrinter) sortObj(obj runtime.Object) error {
|
||||
objs, err := meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(objs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sorter, err := SortObjects(s.Decoder, objs, s.SortField)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch list := obj.(type) {
|
||||
case *corev1.List:
|
||||
outputList := make([]runtime.RawExtension, len(objs))
|
||||
for ix := range objs {
|
||||
outputList[ix] = list.Items[sorter.OriginalPosition(ix)]
|
||||
}
|
||||
list.Items = outputList
|
||||
return nil
|
||||
}
|
||||
return meta.SetList(obj, objs)
|
||||
}
|
||||
|
||||
func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) {
|
||||
for ix := range objs {
|
||||
item := objs[ix]
|
||||
switch u := item.(type) {
|
||||
case *runtime.Unknown:
|
||||
var err error
|
||||
// decode runtime.Unknown to runtime.Unstructured for sorting.
|
||||
// we don't actually want the internal versions of known types.
|
||||
if objs[ix], _, err = decoder.Decode(u.Raw, nil, &unstructured.Unstructured{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field, err := RelaxedJSONPathExpression(fieldInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parser := jsonpath.New("sorting").AllowMissingKeys(true)
|
||||
if err := parser.Parse(field); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We don't do any model validation here, so we traverse all objects to be sorted
|
||||
// and, if the field is valid to at least one of them, we consider it to be a
|
||||
// valid field; otherwise error out.
|
||||
// Note that this requires empty fields to be considered later, when sorting.
|
||||
var fieldFoundOnce bool
|
||||
for _, obj := range objs {
|
||||
values, err := findJSONPathResults(parser, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(values) > 0 && len(values[0]) > 0 {
|
||||
fieldFoundOnce = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !fieldFoundOnce {
|
||||
return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
|
||||
}
|
||||
|
||||
sorter := NewRuntimeSort(field, objs)
|
||||
sort.Sort(sorter)
|
||||
return sorter, nil
|
||||
}
|
||||
|
||||
// RuntimeSort is an implementation of the golang sort interface that knows how to sort
|
||||
// lists of runtime.Object
|
||||
type RuntimeSort struct {
|
||||
field string
|
||||
objs []runtime.Object
|
||||
origPosition []int
|
||||
}
|
||||
|
||||
func NewRuntimeSort(field string, objs []runtime.Object) *RuntimeSort {
|
||||
sorter := &RuntimeSort{field: field, objs: objs, origPosition: make([]int, len(objs))}
|
||||
for ix := range objs {
|
||||
sorter.origPosition[ix] = ix
|
||||
}
|
||||
return sorter
|
||||
}
|
||||
|
||||
func (r *RuntimeSort) Len() int {
|
||||
return len(r.objs)
|
||||
}
|
||||
|
||||
func (r *RuntimeSort) Swap(i, j int) {
|
||||
r.objs[i], r.objs[j] = r.objs[j], r.objs[i]
|
||||
r.origPosition[i], r.origPosition[j] = r.origPosition[j], r.origPosition[i]
|
||||
}
|
||||
|
||||
func isLess(i, j reflect.Value) (bool, error) {
|
||||
switch i.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return i.Int() < j.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return i.Uint() < j.Uint(), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return i.Float() < j.Float(), nil
|
||||
case reflect.String:
|
||||
return sortorder.NaturalLess(i.String(), j.String()), nil
|
||||
case reflect.Ptr:
|
||||
return isLess(i.Elem(), j.Elem())
|
||||
case reflect.Struct:
|
||||
// sort metav1.Time
|
||||
in := i.Interface()
|
||||
if t, ok := in.(metav1.Time); ok {
|
||||
time := j.Interface().(metav1.Time)
|
||||
return t.Before(&time), nil
|
||||
}
|
||||
// fallback to the fields comparison
|
||||
for idx := 0; idx < i.NumField(); idx++ {
|
||||
less, err := isLess(i.Field(idx), j.Field(idx))
|
||||
if err != nil || !less {
|
||||
return less, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
case reflect.Array, reflect.Slice:
|
||||
// note: the length of i and j may be different
|
||||
for idx := 0; idx < integer.IntMin(i.Len(), j.Len()); idx++ {
|
||||
less, err := isLess(i.Index(idx), j.Index(idx))
|
||||
if err != nil || !less {
|
||||
return less, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
|
||||
case reflect.Interface:
|
||||
switch itype := i.Interface().(type) {
|
||||
case uint8:
|
||||
if jtype, ok := j.Interface().(uint8); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case uint16:
|
||||
if jtype, ok := j.Interface().(uint16); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case uint32:
|
||||
if jtype, ok := j.Interface().(uint32); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case uint64:
|
||||
if jtype, ok := j.Interface().(uint64); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int8:
|
||||
if jtype, ok := j.Interface().(int8); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int16:
|
||||
if jtype, ok := j.Interface().(int16); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int32:
|
||||
if jtype, ok := j.Interface().(int32); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int64:
|
||||
if jtype, ok := j.Interface().(int64); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case uint:
|
||||
if jtype, ok := j.Interface().(uint); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case int:
|
||||
if jtype, ok := j.Interface().(int); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case float32:
|
||||
if jtype, ok := j.Interface().(float32); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case float64:
|
||||
if jtype, ok := j.Interface().(float64); ok {
|
||||
return itype < jtype, nil
|
||||
}
|
||||
case string:
|
||||
if jtype, ok := j.Interface().(string); ok {
|
||||
return sortorder.NaturalLess(itype, jtype), nil
|
||||
}
|
||||
default:
|
||||
return false, fmt.Errorf("unsortable type: %T", itype)
|
||||
}
|
||||
return false, fmt.Errorf("unsortable interface: %v", i.Kind())
|
||||
|
||||
default:
|
||||
return false, fmt.Errorf("unsortable type: %v", i.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuntimeSort) Less(i, j int) bool {
|
||||
iObj := r.objs[i]
|
||||
jObj := r.objs[j]
|
||||
|
||||
var iValues [][]reflect.Value
|
||||
var jValues [][]reflect.Value
|
||||
var err error
|
||||
|
||||
parser := jsonpath.New("sorting").AllowMissingKeys(true)
|
||||
err = parser.Parse(r.field)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
iValues, err = findJSONPathResults(parser, iObj)
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed to get i values for %#v using %s (%#v)", iObj, r.field, err)
|
||||
}
|
||||
|
||||
jValues, err = findJSONPathResults(parser, jObj)
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed to get j values for %#v using %s (%v)", jObj, r.field, err)
|
||||
}
|
||||
|
||||
if len(iValues) == 0 || len(iValues[0]) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(jValues) == 0 || len(jValues[0]) == 0 {
|
||||
return false
|
||||
}
|
||||
iField := iValues[0][0]
|
||||
jField := jValues[0][0]
|
||||
|
||||
less, err := isLess(iField, jField)
|
||||
if err != nil {
|
||||
klog.Fatalf("Field %s in %T is an unsortable type: %s, err: %v", r.field, iObj, iField.Kind().String(), err)
|
||||
}
|
||||
return less
|
||||
}
|
||||
|
||||
// OriginalPosition returns the starting (original) position of a particular index.
|
||||
// e.g. If OriginalPosition(0) returns 5 than the
|
||||
// the item currently at position 0 was at position 5 in the original unsorted array.
|
||||
func (r *RuntimeSort) OriginalPosition(ix int) int {
|
||||
if ix < 0 || ix > len(r.origPosition) {
|
||||
return -1
|
||||
}
|
||||
return r.origPosition[ix]
|
||||
}
|
||||
|
||||
type TableSorter struct {
|
||||
field string
|
||||
obj *metav1beta1.Table
|
||||
parsedRows [][][]reflect.Value
|
||||
}
|
||||
|
||||
func (t *TableSorter) Len() int {
|
||||
return len(t.obj.Rows)
|
||||
}
|
||||
|
||||
func (t *TableSorter) Swap(i, j int) {
|
||||
t.obj.Rows[i], t.obj.Rows[j] = t.obj.Rows[j], t.obj.Rows[i]
|
||||
}
|
||||
|
||||
func (t *TableSorter) Less(i, j int) bool {
|
||||
iValues := t.parsedRows[i]
|
||||
jValues := t.parsedRows[j]
|
||||
if len(iValues) == 0 || len(iValues[0]) == 0 || len(jValues) == 0 || len(jValues[0]) == 0 {
|
||||
klog.Fatalf("couldn't find any field with path %q in the list of objects", t.field)
|
||||
}
|
||||
|
||||
iField := iValues[0][0]
|
||||
jField := jValues[0][0]
|
||||
|
||||
less, err := isLess(iField, jField)
|
||||
if err != nil {
|
||||
klog.Fatalf("Field %s in %T is an unsortable type: %s, err: %v", t.field, t.parsedRows, iField.Kind().String(), err)
|
||||
}
|
||||
return less
|
||||
}
|
||||
|
||||
func (t *TableSorter) Sort() error {
|
||||
sort.Sort(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTableSorter(table *metav1beta1.Table, field string) *TableSorter {
|
||||
var parsedRows [][][]reflect.Value
|
||||
|
||||
parser := jsonpath.New("sorting").AllowMissingKeys(true)
|
||||
err := parser.Parse(field)
|
||||
if err != nil {
|
||||
klog.Fatalf("sorting error: %v\n", err)
|
||||
}
|
||||
|
||||
for i := range table.Rows {
|
||||
parsedRow, err := findJSONPathResults(parser, table.Rows[i].Object.Object)
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed to get values for %#v using %s (%#v)", parsedRow, field, err)
|
||||
}
|
||||
parsedRows = append(parsedRows, parsedRow)
|
||||
}
|
||||
|
||||
return &TableSorter{
|
||||
obj: table,
|
||||
field: field,
|
||||
parsedRows: parsedRows,
|
||||
}
|
||||
}
|
||||
func findJSONPathResults(parser *jsonpath.JSONPath, from runtime.Object) ([][]reflect.Value, error) {
|
||||
if unstructuredObj, ok := from.(*unstructured.Unstructured); ok {
|
||||
return parser.FindResults(unstructuredObj.Object)
|
||||
}
|
||||
return parser.FindResults(reflect.ValueOf(from).Elem().Interface())
|
||||
}
|
477
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/sorter_test.go
generated
vendored
Normal file
477
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/get/sorter_test.go
generated
vendored
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package get
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
)
|
||||
|
||||
func encodeOrDie(obj runtime.Object) []byte {
|
||||
data, err := runtime.Encode(scheme.Codecs.LegacyCodec(corev1.SchemeGroupVersion), obj)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func TestSortingPrinter(t *testing.T) {
|
||||
intPtr := func(val int32) *int32 { return &val }
|
||||
|
||||
a := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
},
|
||||
}
|
||||
|
||||
b := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "b",
|
||||
},
|
||||
}
|
||||
|
||||
c := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
obj runtime.Object
|
||||
sort runtime.Object
|
||||
field string
|
||||
name string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "in-order-already",
|
||||
obj: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "b",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sort: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "b",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
field: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
name: "reverse-order",
|
||||
obj: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "b",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sort: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "b",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
field: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
name: "random-order-timestamp",
|
||||
obj: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: metav1.Unix(300, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: metav1.Unix(100, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: metav1.Unix(200, 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sort: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: metav1.Unix(100, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: metav1.Unix(200, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: metav1.Unix(300, 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
field: "{.metadata.creationTimestamp}",
|
||||
},
|
||||
{
|
||||
name: "random-order-numbers",
|
||||
obj: &corev1.ReplicationControllerList{
|
||||
Items: []corev1.ReplicationController{
|
||||
{
|
||||
Spec: corev1.ReplicationControllerSpec{
|
||||
Replicas: intPtr(5),
|
||||
},
|
||||
},
|
||||
{
|
||||
Spec: corev1.ReplicationControllerSpec{
|
||||
Replicas: intPtr(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
Spec: corev1.ReplicationControllerSpec{
|
||||
Replicas: intPtr(9),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sort: &corev1.ReplicationControllerList{
|
||||
Items: []corev1.ReplicationController{
|
||||
{
|
||||
Spec: corev1.ReplicationControllerSpec{
|
||||
Replicas: intPtr(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
Spec: corev1.ReplicationControllerSpec{
|
||||
Replicas: intPtr(5),
|
||||
},
|
||||
},
|
||||
{
|
||||
Spec: corev1.ReplicationControllerSpec{
|
||||
Replicas: intPtr(9),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
field: "{.spec.replicas}",
|
||||
},
|
||||
{
|
||||
name: "v1.List in order",
|
||||
obj: &corev1.List{
|
||||
Items: []runtime.RawExtension{
|
||||
{Raw: encodeOrDie(a)},
|
||||
{Raw: encodeOrDie(b)},
|
||||
{Raw: encodeOrDie(c)},
|
||||
},
|
||||
},
|
||||
sort: &corev1.List{
|
||||
Items: []runtime.RawExtension{
|
||||
{Raw: encodeOrDie(a)},
|
||||
{Raw: encodeOrDie(b)},
|
||||
{Raw: encodeOrDie(c)},
|
||||
},
|
||||
},
|
||||
field: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
name: "v1.List in reverse",
|
||||
obj: &corev1.List{
|
||||
Items: []runtime.RawExtension{
|
||||
{Raw: encodeOrDie(c)},
|
||||
{Raw: encodeOrDie(b)},
|
||||
{Raw: encodeOrDie(a)},
|
||||
},
|
||||
},
|
||||
sort: &corev1.List{
|
||||
Items: []runtime.RawExtension{
|
||||
{Raw: encodeOrDie(a)},
|
||||
{Raw: encodeOrDie(b)},
|
||||
{Raw: encodeOrDie(c)},
|
||||
},
|
||||
},
|
||||
field: "{.metadata.name}",
|
||||
},
|
||||
{
|
||||
name: "some-missing-fields",
|
||||
obj: &unstructured.UnstructuredList{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "List",
|
||||
"apiVersion": "v1",
|
||||
},
|
||||
Items: []unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"status": map[string]interface{}{
|
||||
"availableReplicas": 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"status": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"status": map[string]interface{}{
|
||||
"availableReplicas": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sort: &unstructured.UnstructuredList{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "List",
|
||||
"apiVersion": "v1",
|
||||
},
|
||||
Items: []unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"status": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"status": map[string]interface{}{
|
||||
"availableReplicas": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"status": map[string]interface{}{
|
||||
"availableReplicas": 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
field: "{.status.availableReplicas}",
|
||||
},
|
||||
{
|
||||
name: "all-missing-fields",
|
||||
obj: &unstructured.UnstructuredList{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "List",
|
||||
"apiVersion": "v1",
|
||||
},
|
||||
Items: []unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"status": map[string]interface{}{
|
||||
"replicas": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"status": map[string]interface{}{
|
||||
"replicas": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
field: "{.status.availableReplicas}",
|
||||
expectedErr: "couldn't find any field with path \"{.status.availableReplicas}\" in the list of objects",
|
||||
},
|
||||
{
|
||||
name: "model-invalid-fields",
|
||||
obj: &corev1.ReplicationControllerList{
|
||||
Items: []corev1.ReplicationController{
|
||||
{
|
||||
Status: corev1.ReplicationControllerStatus{},
|
||||
},
|
||||
{
|
||||
Status: corev1.ReplicationControllerStatus{},
|
||||
},
|
||||
{
|
||||
Status: corev1.ReplicationControllerStatus{},
|
||||
},
|
||||
},
|
||||
},
|
||||
field: "{.invalid}",
|
||||
expectedErr: "couldn't find any field with path \"{.invalid}\" in the list of objects",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sort := &SortingPrinter{SortField: tt.field, Decoder: scheme.Codecs.UniversalDecoder()}
|
||||
err := sort.sortObj(tt.obj)
|
||||
if err != nil {
|
||||
if len(tt.expectedErr) > 0 {
|
||||
if strings.Contains(err.Error(), tt.expectedErr) {
|
||||
return
|
||||
}
|
||||
t.Fatalf("%s: expected error containing: %q, got: \"%v\"", tt.name, tt.expectedErr, err)
|
||||
}
|
||||
t.Fatalf("%s: unexpected error: %v", tt.name, err)
|
||||
}
|
||||
if len(tt.expectedErr) > 0 {
|
||||
t.Fatalf("%s: expected error containing: %q, got none", tt.name, tt.expectedErr)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.obj, tt.sort) {
|
||||
t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v", tt.name, tt.sort, tt.obj)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeSortLess(t *testing.T) {
|
||||
var testobj runtime.Object
|
||||
|
||||
testobj = &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "b",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testobjs, err := meta.ExtractList(testobj)
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractList testobj got unexpected error: %v", err)
|
||||
}
|
||||
|
||||
testfield := "{.metadata.name}"
|
||||
testruntimeSort := NewRuntimeSort(testfield, testobjs)
|
||||
tests := []struct {
|
||||
name string
|
||||
runtimeSort *RuntimeSort
|
||||
i int
|
||||
j int
|
||||
expectResult bool
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "test less true",
|
||||
runtimeSort: testruntimeSort,
|
||||
i: 0,
|
||||
j: 1,
|
||||
expectResult: true,
|
||||
},
|
||||
{
|
||||
name: "test less false",
|
||||
runtimeSort: testruntimeSort,
|
||||
i: 1,
|
||||
j: 2,
|
||||
expectResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result := test.runtimeSort.Less(test.i, test.j)
|
||||
if result != test.expectResult {
|
||||
t.Errorf("case[%d]:%s Expected result: %v, Got result: %v", i, test.name, test.expectResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user