mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
resource_printer was not leveraging runtime interfaces
In general, everything in kubectl/* needs to be ignorant of api/* unless it deals with a concrete type - this change forces resource_printer to accept interface abstractions (that are already part of kubectl).
This commit is contained in:
parent
efe88e0818
commit
fb4ea845f1
@ -19,7 +19,6 @@ package kubectl
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -34,13 +33,14 @@ import (
|
|||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/latest"
|
||||||
"k8s.io/kubernetes/pkg/api/meta"
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
"k8s.io/kubernetes/pkg/conversion"
|
"k8s.io/kubernetes/pkg/conversion"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||||
"k8s.io/kubernetes/pkg/util/jsonpath"
|
"k8s.io/kubernetes/pkg/util/jsonpath"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
)
|
)
|
||||||
@ -66,7 +66,10 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
|
|||||||
case "yaml":
|
case "yaml":
|
||||||
printer = &YAMLPrinter{}
|
printer = &YAMLPrinter{}
|
||||||
case "name":
|
case "name":
|
||||||
printer = &NamePrinter{}
|
printer = &NamePrinter{
|
||||||
|
Typer: runtime.ObjectTyperToTyper(api.Scheme),
|
||||||
|
Decoder: latest.Codecs.UniversalDecoder(),
|
||||||
|
}
|
||||||
case "template", "go-template":
|
case "template", "go-template":
|
||||||
if len(formatArgument) == 0 {
|
if len(formatArgument) == 0 {
|
||||||
return nil, false, fmt.Errorf("template format specified but no template given")
|
return nil, false, fmt.Errorf("template format specified but no template given")
|
||||||
@ -197,62 +200,47 @@ func (p *VersionedPrinter) HandledResources() []string {
|
|||||||
|
|
||||||
// NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object.
|
// NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object.
|
||||||
type NamePrinter struct {
|
type NamePrinter struct {
|
||||||
|
Decoder runtime.Decoder
|
||||||
|
Typer runtime.Typer
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object
|
// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object
|
||||||
// and print "resource/name" pair. If the object is a List, print all items in it.
|
// and print "resource/name" pair. If the object is a List, print all items in it.
|
||||||
func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||||
objvalue := reflect.ValueOf(obj).Elem()
|
gvk, _, _ := p.Typer.ObjectKind(obj)
|
||||||
kindString := objvalue.FieldByName("Kind")
|
|
||||||
groupVersionString := objvalue.FieldByName("APIVersion")
|
|
||||||
kind := unversioned.GroupVersionKind{}
|
|
||||||
if !kindString.IsValid() {
|
|
||||||
kindString = reflect.ValueOf("<unknown>")
|
|
||||||
}
|
|
||||||
kind.Kind = kindString.String()
|
|
||||||
|
|
||||||
if !groupVersionString.IsValid() {
|
if meta.IsListType(obj) {
|
||||||
groupVersionString = reflect.ValueOf("<unknown>/<unknown>")
|
items, err := meta.ExtractList(obj)
|
||||||
}
|
if err != nil {
|
||||||
gv, err := unversioned.ParseGroupVersion(groupVersionString.String())
|
return err
|
||||||
if err != nil {
|
}
|
||||||
kind.Group = gv.Group
|
if errs := runtime.DecodeList(items, p.Decoder, runtime.UnstructuredJSONScheme); len(errs) > 0 {
|
||||||
kind.Version = gv.Version
|
return utilerrors.NewAggregate(errs)
|
||||||
}
|
}
|
||||||
|
for _, obj := range items {
|
||||||
if kind.Kind == "List" {
|
if err := p.PrintObj(obj, w); err != nil {
|
||||||
items := objvalue.FieldByName("Items")
|
return err
|
||||||
if items.Type().String() == "[]runtime.RawExtension" {
|
|
||||||
for i := 0; i < items.Len(); i++ {
|
|
||||||
rawObj := items.Index(i).FieldByName("RawJSON").Interface().([]byte)
|
|
||||||
scheme := api.Scheme
|
|
||||||
groupVersionKind, err := scheme.DataKind(rawObj)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
decodedObj, err := scheme.DecodeToVersion(rawObj, unversioned.GroupVersion{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tpmeta := unversioned.TypeMeta{
|
|
||||||
APIVersion: groupVersionKind.GroupVersion().String(),
|
|
||||||
Kind: groupVersionKind.Kind,
|
|
||||||
}
|
|
||||||
s := reflect.ValueOf(decodedObj).Elem()
|
|
||||||
s.FieldByName("TypeMeta").Set(reflect.ValueOf(tpmeta))
|
|
||||||
p.PrintObj(decodedObj, w)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return errors.New("the list object contains unrecognized items.")
|
|
||||||
}
|
}
|
||||||
} else {
|
return nil
|
||||||
name := objvalue.FieldByName("Name")
|
}
|
||||||
if !name.IsValid() {
|
|
||||||
name = reflect.ValueOf("<unknown>")
|
// TODO: this is wrong, runtime.Unknown and runtime.Unstructured are not handled properly here.
|
||||||
|
|
||||||
|
name := "<unknown>"
|
||||||
|
if acc, err := meta.Accessor(obj); err == nil {
|
||||||
|
if n := acc.GetName(); len(n) > 0 {
|
||||||
|
name = n
|
||||||
}
|
}
|
||||||
_, resource := meta.KindToResource(kind, false)
|
}
|
||||||
|
|
||||||
|
if gvk != nil {
|
||||||
|
// TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper
|
||||||
|
_, resource := meta.KindToResource(*gvk, false)
|
||||||
|
|
||||||
fmt.Fprintf(w, "%s/%s\n", resource.Resource, name)
|
fmt.Fprintf(w, "%s/%s\n", resource.Resource, name)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "<unknown>/%s\n", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -1742,9 +1730,8 @@ func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
|
|||||||
|
|
||||||
// PrintObj formats the obj with the JSONPath Template.
|
// PrintObj formats the obj with the JSONPath Template.
|
||||||
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||||
var queryObj interface{}
|
var queryObj interface{} = obj
|
||||||
switch obj.(type) {
|
if meta.IsListType(obj) {
|
||||||
case *v1.List, *api.List:
|
|
||||||
data, err := json.Marshal(obj)
|
data, err := json.Marshal(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1753,8 +1740,6 @@ func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
|||||||
if err := json.Unmarshal(data, &queryObj); err != nil {
|
if err := json.Unmarshal(data, &queryObj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
queryObj = obj
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := j.JSONPath.Execute(w, queryObj); err != nil {
|
if err := j.JSONPath.Execute(w, queryObj); err != nil {
|
||||||
|
@ -27,12 +27,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/latest"
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing"
|
kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
yamlserializer "k8s.io/kubernetes/pkg/runtime/serializer/yaml"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
|
|
||||||
@ -121,17 +123,17 @@ func TestPrinter(t *testing.T) {
|
|||||||
{"test jsonpath", "jsonpath", "{.metadata.name}", podTest, "foo"},
|
{"test jsonpath", "jsonpath", "{.metadata.name}", podTest, "foo"},
|
||||||
{"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, "foo bar"},
|
{"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, "foo bar"},
|
||||||
{"test jsonpath empty list", "jsonpath", "{.items[*].metadata.name}", emptyListTest, ""},
|
{"test jsonpath empty list", "jsonpath", "{.items[*].metadata.name}", emptyListTest, ""},
|
||||||
{"test name", "name", "", podTest, "/foo\n"},
|
{"test name", "name", "", podTest, "pod/foo\n"},
|
||||||
{"emits versioned objects", "template", "{{.kind}}", testapi, "Pod"},
|
{"emits versioned objects", "template", "{{.kind}}", testapi, "Pod"},
|
||||||
}
|
}
|
||||||
for _, test := range printerTests {
|
for _, test := range printerTests {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
printer, found, err := GetPrinter(test.Format, test.FormatArgument)
|
printer, found, err := GetPrinter(test.Format, test.FormatArgument)
|
||||||
if err != nil || !found {
|
if err != nil || !found {
|
||||||
t.Errorf("unexpected error: %#v", err)
|
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||||
}
|
}
|
||||||
if err := printer.PrintObj(test.Input, buf); err != nil {
|
if err := printer.PrintObj(test.Input, buf); err != nil {
|
||||||
t.Errorf("unexpected error: %#v", err)
|
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||||
}
|
}
|
||||||
if buf.String() != test.Expect {
|
if buf.String() != test.Expect {
|
||||||
t.Errorf("in %s, expect %q, got %q", test.Name, test.Expect, buf.String())
|
t.Errorf("in %s, expect %q, got %q", test.Name, test.Expect, buf.String())
|
||||||
@ -175,8 +177,8 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data
|
|||||||
}
|
}
|
||||||
// Use real decode function to undo the versioning process.
|
// Use real decode function to undo the versioning process.
|
||||||
poutput = kubectltesting.TestStruct{}
|
poutput = kubectltesting.TestStruct{}
|
||||||
err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &poutput)
|
s := yamlserializer.NewDecodingSerializer(testapi.Default.Codec())
|
||||||
if err != nil {
|
if err := runtime.DecodeInto(s, buf.Bytes(), &poutput); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(testData, poutput) {
|
if !reflect.DeepEqual(testData, poutput) {
|
||||||
@ -196,8 +198,7 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data
|
|||||||
}
|
}
|
||||||
// Use real decode function to undo the versioning process.
|
// Use real decode function to undo the versioning process.
|
||||||
objOut = api.Pod{}
|
objOut = api.Pod{}
|
||||||
err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &objOut)
|
if err := runtime.DecodeInto(s, buf.Bytes(), &objOut); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(obj, &objOut) {
|
if !reflect.DeepEqual(obj, &objOut) {
|
||||||
@ -463,7 +464,10 @@ func TestPrinters(t *testing.T) {
|
|||||||
"template": templatePrinter,
|
"template": templatePrinter,
|
||||||
"template2": templatePrinter2,
|
"template2": templatePrinter2,
|
||||||
"jsonpath": jsonpathPrinter,
|
"jsonpath": jsonpathPrinter,
|
||||||
"name": &NamePrinter{},
|
"name": &NamePrinter{
|
||||||
|
Typer: runtime.ObjectTyperToTyper(api.Scheme),
|
||||||
|
Decoder: latest.Codecs.UniversalDecoder(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
objects := map[string]runtime.Object{
|
objects := map[string]runtime.Object{
|
||||||
"pod": &api.Pod{ObjectMeta: om("pod")},
|
"pod": &api.Pod{ObjectMeta: om("pod")},
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
|
||||||
"k8s.io/kubernetes/pkg/api/meta"
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
@ -36,6 +35,7 @@ import (
|
|||||||
type SortingPrinter struct {
|
type SortingPrinter struct {
|
||||||
SortField string
|
SortField string
|
||||||
Delegate ResourcePrinter
|
Delegate ResourcePrinter
|
||||||
|
Decoder runtime.Decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||||
@ -63,7 +63,7 @@ func (s *SortingPrinter) sortObj(obj runtime.Object) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sorter, err := SortObjects(objs, s.SortField)
|
sorter, err := SortObjects(s.Decoder, objs, s.SortField)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ func (s *SortingPrinter) sortObj(obj runtime.Object) error {
|
|||||||
return meta.SetList(obj, objs)
|
return meta.SetList(obj, objs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SortObjects(objs []runtime.Object, fieldInput string) (*RuntimeSort, error) {
|
func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) {
|
||||||
parser := jsonpath.New("sorting")
|
parser := jsonpath.New("sorting")
|
||||||
|
|
||||||
field, err := massageJSONPath(fieldInput)
|
field, err := massageJSONPath(fieldInput)
|
||||||
@ -97,7 +97,7 @@ func SortObjects(objs []runtime.Object, fieldInput string) (*RuntimeSort, error)
|
|||||||
switch u := item.(type) {
|
switch u := item.(type) {
|
||||||
case *runtime.Unknown:
|
case *runtime.Unknown:
|
||||||
var err error
|
var err error
|
||||||
if objs[ix], err = api.Codec.Decode(u.RawJSON); err != nil {
|
if objs[ix], _, err = decoder.Decode(u.RawJSON, nil, nil); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,13 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api/latest"
|
||||||
api "k8s.io/kubernetes/pkg/api/v1"
|
api "k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func encodeOrDie(obj runtime.Object) []byte {
|
func encodeOrDie(obj runtime.Object) []byte {
|
||||||
data, err := api.Codec.Encode(obj)
|
data, err := runtime.Encode(latest.Codecs.LegacyCodec(api.SchemeGroupVersion), obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
@ -223,7 +224,7 @@ func TestSortingPrinter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
sort := &SortingPrinter{SortField: test.field}
|
sort := &SortingPrinter{SortField: test.field, Decoder: latest.Codecs.UniversalDecoder()}
|
||||||
if err := sort.sortObj(test.obj); err != nil {
|
if err := sort.sortObj(test.obj); err != nil {
|
||||||
t.Errorf("unexpected error: %v (%s)", err, test.name)
|
t.Errorf("unexpected error: %v (%s)", err, test.name)
|
||||||
continue
|
continue
|
||||||
|
Loading…
Reference in New Issue
Block a user