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:
Clayton Coleman 2015-12-21 00:34:11 -05:00
parent efe88e0818
commit fb4ea845f1
4 changed files with 57 additions and 67 deletions

View File

@ -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 {

View File

@ -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")},

View File

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

View File

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