diff --git a/pkg/cmd/get/get.go b/pkg/cmd/get/get.go index 8c148316d..59dabac4f 100644 --- a/pkg/cmd/get/get.go +++ b/pkg/cmd/get/get.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "net/url" + "strings" "github.com/spf13/cobra" @@ -351,7 +352,7 @@ func (r *RuntimeSorter) Sort() error { return nil } if len(r.objects) == 1 { - _, isTable := r.objects[0].(*metav1beta1.Table) + _, isTable := r.objects[0].(*metav1.Table) if !isTable { return nil } @@ -362,7 +363,7 @@ func (r *RuntimeSorter) Sort() error { for _, obj := range r.objects { switch t := obj.(type) { - case *metav1beta1.Table: + case *metav1.Table: includesTable = true if sorter, err := NewTableSorter(t, r.field); err != nil { @@ -433,11 +434,11 @@ func (o *GetOptions) transformRequests(req *rest.Request) { return } - 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) + req.SetHeader("Accept", strings.Join([]string{ + fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1.SchemeGroupVersion.Version, metav1.GroupName), + fmt.Sprintf("application/json;as=Table;v=%s;g=%s", metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName), + "application/json", + }, ",")) // if sorting, ensure we receive the full object in order to introspect its fields via jsonpath if o.Sort { diff --git a/pkg/cmd/get/get_test.go b/pkg/cmd/get/get_test.go index 9e7ff0da2..7b3b1b974 100644 --- a/pkg/cmd/get/get_test.go +++ b/pkg/cmd/get/get_test.go @@ -18,6 +18,7 @@ package get import ( "bytes" + "encoding/json" encjson "encoding/json" "fmt" "io" @@ -267,6 +268,31 @@ foo 0/0 0 } } +func TestGetV1TableObjects(t *testing.T) { + pods, _, _ := cmdtesting.TestData() + + tf := cmdtesting.NewTestFactory().WithNamespace("test") + defer tf.Cleanup() + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + + tf.UnstructuredClient = &fake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: podV1TableObjBody(codec, pods.Items[0])}, + } + + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdGet("kubectl", tf, streams) + cmd.SetOutput(buf) + cmd.Run(cmd, []string{"pods", "foo"}) + + expected := `NAME READY STATUS RESTARTS AGE +foo 0/0 0 +` + if e, a := expected, buf.String(); e != a { + t.Errorf("expected\n%v\ngot\n%v", e, a) + } +} + func TestGetObjectsShowKind(t *testing.T) { pods, _, _ := cmdtesting.TestData() @@ -2706,7 +2732,32 @@ var podColumns = []metav1.TableColumnDefinition{ // build a meta table response from a pod list func podTableObjBody(codec runtime.Codec, pods ...corev1.Pod) io.ReadCloser { + table := &metav1beta1.Table{ + TypeMeta: metav1.TypeMeta{APIVersion: "meta.k8s.io/v1beta1", Kind: "Table"}, + ColumnDefinitions: podColumns, + } + for i := range pods { + b := bytes.NewBuffer(nil) + codec.Encode(&pods[i], b) + table.Rows = append(table.Rows, metav1beta1.TableRow{ + Object: runtime.RawExtension{Raw: b.Bytes()}, + Cells: []interface{}{pods[i].Name, "0/0", "", int64(0), "", "", "", "", ""}, + }) + } + data, err := json.Marshal(table) + if err != nil { + panic(err) + } + if !strings.Contains(string(data), `"meta.k8s.io/v1beta1"`) { + panic("expected v1beta1, got " + string(data)) + } + return cmdtesting.BytesBody(data) +} + +// build a meta table response from a pod list +func podV1TableObjBody(codec runtime.Codec, pods ...corev1.Pod) io.ReadCloser { table := &metav1.Table{ + TypeMeta: metav1.TypeMeta{APIVersion: "meta.k8s.io/v1", Kind: "Table"}, ColumnDefinitions: podColumns, } for i := range pods { @@ -2717,7 +2768,14 @@ func podTableObjBody(codec runtime.Codec, pods ...corev1.Pod) io.ReadCloser { Cells: []interface{}{pods[i].Name, "0/0", "", int64(0), "", "", "", "", ""}, }) } - return cmdtesting.ObjBody(codec, table) + data, err := json.Marshal(table) + if err != nil { + panic(err) + } + if !strings.Contains(string(data), `"meta.k8s.io/v1"`) { + panic("expected v1, got " + string(data)) + } + return cmdtesting.BytesBody(data) } // build meta table watch events from pod watch events diff --git a/pkg/cmd/get/skip_printer.go b/pkg/cmd/get/skip_printer.go index f02883cb1..95a804315 100644 --- a/pkg/cmd/get/skip_printer.go +++ b/pkg/cmd/get/skip_printer.go @@ -19,7 +19,7 @@ package get import ( "io" - metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/printers" ) @@ -37,7 +37,7 @@ func (p *skipPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { return p.delegate.PrintObj(obj, writer) } - table, isTable := obj.(*metav1beta1.Table) + table, isTable := obj.(*metav1.Table) if !isTable { return nil } diff --git a/pkg/cmd/get/sorter.go b/pkg/cmd/get/sorter.go index 599b4acd1..d5a2baa64 100644 --- a/pkg/cmd/get/sorter.go +++ b/pkg/cmd/get/sorter.go @@ -28,7 +28,6 @@ import ( "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/printers" "k8s.io/client-go/util/jsonpath" @@ -46,7 +45,7 @@ type SortingPrinter struct { } func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error { - if table, isTable := obj.(*metav1beta1.Table); isTable && len(table.Rows) > 1 { + if table, isTable := obj.(*metav1.Table); isTable && len(table.Rows) > 1 { parsedField, err := RelaxedJSONPathExpression(s.SortField) if err != nil { parsedField = s.SortField @@ -322,7 +321,7 @@ func (r *RuntimeSort) OriginalPosition(ix int) int { type TableSorter struct { field string - obj *metav1beta1.Table + obj *metav1.Table parsedRows [][][]reflect.Value } @@ -361,7 +360,7 @@ func (t *TableSorter) Sort() error { return nil } -func NewTableSorter(table *metav1beta1.Table, field string) (*TableSorter, error) { +func NewTableSorter(table *metav1.Table, field string) (*TableSorter, error) { var parsedRows [][][]reflect.Value parser := jsonpath.New("sorting").AllowMissingKeys(true) diff --git a/pkg/cmd/get/table_printer.go b/pkg/cmd/get/table_printer.go index 1534c3d7f..e096d9669 100644 --- a/pkg/cmd/get/table_printer.go +++ b/pkg/cmd/get/table_printer.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/printers" "k8s.io/klog" ) @@ -45,24 +46,26 @@ func (t *TablePrinter) PrintObj(obj runtime.Object, writer io.Writer) error { return t.Delegate.PrintObj(obj, writer) } +var recognizedTableVersions = map[schema.GroupVersionKind]bool{ + metav1beta1.SchemeGroupVersion.WithKind("Table"): true, + metav1.SchemeGroupVersion.WithKind("Table"): true, +} + func decodeIntoTable(obj runtime.Object) (runtime.Object, error) { event, isEvent := obj.(*metav1.WatchEvent) if isEvent { obj = event.Object.Object } - if obj.GetObjectKind().GroupVersionKind().Group != metav1beta1.GroupName { - return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table") - } - if obj.GetObjectKind().GroupVersionKind().Kind != "Table" { - return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table") + if !recognizedTableVersions[obj.GetObjectKind().GroupVersionKind()] { + return nil, fmt.Errorf("attempt to decode non-Table object") } unstr, ok := obj.(*unstructured.Unstructured) if !ok { return nil, fmt.Errorf("attempt to decode non-Unstructured object") } - table := &metav1beta1.Table{} + table := &metav1.Table{} if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr.Object, table); err != nil { return nil, err }