mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +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 (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -34,13 +33,14 @@ import (
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/latest"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/conversion"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/util/jsonpath"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
@ -66,7 +66,10 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
|
||||
case "yaml":
|
||||
printer = &YAMLPrinter{}
|
||||
case "name":
|
||||
printer = &NamePrinter{}
|
||||
printer = &NamePrinter{
|
||||
Typer: runtime.ObjectTyperToTyper(api.Scheme),
|
||||
Decoder: latest.Codecs.UniversalDecoder(),
|
||||
}
|
||||
case "template", "go-template":
|
||||
if len(formatArgument) == 0 {
|
||||
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.
|
||||
type NamePrinter struct {
|
||||
Decoder runtime.Decoder
|
||||
Typer runtime.Typer
|
||||
}
|
||||
|
||||
// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object
|
||||
// and print "resource/name" pair. If the object is a List, print all items in it.
|
||||
func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
objvalue := reflect.ValueOf(obj).Elem()
|
||||
kindString := objvalue.FieldByName("Kind")
|
||||
groupVersionString := objvalue.FieldByName("APIVersion")
|
||||
kind := unversioned.GroupVersionKind{}
|
||||
if !kindString.IsValid() {
|
||||
kindString = reflect.ValueOf("<unknown>")
|
||||
}
|
||||
kind.Kind = kindString.String()
|
||||
gvk, _, _ := p.Typer.ObjectKind(obj)
|
||||
|
||||
if !groupVersionString.IsValid() {
|
||||
groupVersionString = reflect.ValueOf("<unknown>/<unknown>")
|
||||
}
|
||||
gv, err := unversioned.ParseGroupVersion(groupVersionString.String())
|
||||
if err != nil {
|
||||
kind.Group = gv.Group
|
||||
kind.Version = gv.Version
|
||||
}
|
||||
|
||||
if kind.Kind == "List" {
|
||||
items := objvalue.FieldByName("Items")
|
||||
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)
|
||||
if meta.IsListType(obj) {
|
||||
items, err := meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if errs := runtime.DecodeList(items, p.Decoder, runtime.UnstructuredJSONScheme); len(errs) > 0 {
|
||||
return utilerrors.NewAggregate(errs)
|
||||
}
|
||||
for _, obj := range items {
|
||||
if err := p.PrintObj(obj, w); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New("the list object contains unrecognized items.")
|
||||
}
|
||||
} else {
|
||||
name := objvalue.FieldByName("Name")
|
||||
if !name.IsValid() {
|
||||
name = reflect.ValueOf("<unknown>")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
} else {
|
||||
fmt.Fprintf(w, "<unknown>/%s\n", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -1742,9 +1730,8 @@ func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
|
||||
|
||||
// PrintObj formats the obj with the JSONPath Template.
|
||||
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
||||
var queryObj interface{}
|
||||
switch obj.(type) {
|
||||
case *v1.List, *api.List:
|
||||
var queryObj interface{} = obj
|
||||
if meta.IsListType(obj) {
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
queryObj = obj
|
||||
}
|
||||
|
||||
if err := j.JSONPath.Execute(w, queryObj); err != nil {
|
||||
|
@ -27,12 +27,14 @@ import (
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/latest"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
yamlserializer "k8s.io/kubernetes/pkg/runtime/serializer/yaml"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
|
||||
@ -121,17 +123,17 @@ func TestPrinter(t *testing.T) {
|
||||
{"test jsonpath", "jsonpath", "{.metadata.name}", podTest, "foo"},
|
||||
{"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, "foo bar"},
|
||||
{"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"},
|
||||
}
|
||||
for _, test := range printerTests {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
printer, found, err := GetPrinter(test.Format, test.FormatArgument)
|
||||
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 {
|
||||
t.Errorf("unexpected error: %#v", err)
|
||||
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||
}
|
||||
if buf.String() != test.Expect {
|
||||
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.
|
||||
poutput = kubectltesting.TestStruct{}
|
||||
err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &poutput)
|
||||
if err != nil {
|
||||
s := yamlserializer.NewDecodingSerializer(testapi.Default.Codec())
|
||||
if err := runtime.DecodeInto(s, buf.Bytes(), &poutput); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
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.
|
||||
objOut = api.Pod{}
|
||||
err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &objOut)
|
||||
if err != nil {
|
||||
if err := runtime.DecodeInto(s, buf.Bytes(), &objOut); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(obj, &objOut) {
|
||||
@ -463,7 +464,10 @@ func TestPrinters(t *testing.T) {
|
||||
"template": templatePrinter,
|
||||
"template2": templatePrinter2,
|
||||
"jsonpath": jsonpathPrinter,
|
||||
"name": &NamePrinter{},
|
||||
"name": &NamePrinter{
|
||||
Typer: runtime.ObjectTyperToTyper(api.Scheme),
|
||||
Decoder: latest.Codecs.UniversalDecoder(),
|
||||
},
|
||||
}
|
||||
objects := map[string]runtime.Object{
|
||||
"pod": &api.Pod{ObjectMeta: om("pod")},
|
||||
|
@ -22,7 +22,6 @@ import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/meta"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
@ -36,6 +35,7 @@ import (
|
||||
type SortingPrinter struct {
|
||||
SortField string
|
||||
Delegate ResourcePrinter
|
||||
Decoder runtime.Decoder
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
sorter, err := SortObjects(objs, s.SortField)
|
||||
sorter, err := SortObjects(s.Decoder, objs, s.SortField)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -80,7 +80,7 @@ func (s *SortingPrinter) sortObj(obj runtime.Object) error {
|
||||
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")
|
||||
|
||||
field, err := massageJSONPath(fieldInput)
|
||||
@ -97,7 +97,7 @@ func SortObjects(objs []runtime.Object, fieldInput string) (*RuntimeSort, error)
|
||||
switch u := item.(type) {
|
||||
case *runtime.Unknown:
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,13 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/latest"
|
||||
api "k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
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 {
|
||||
panic(err.Error())
|
||||
}
|
||||
@ -223,7 +224,7 @@ func TestSortingPrinter(t *testing.T) {
|
||||
},
|
||||
}
|
||||
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 {
|
||||
t.Errorf("unexpected error: %v (%s)", err, test.name)
|
||||
continue
|
||||
|
Loading…
Reference in New Issue
Block a user