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

View File

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

View File

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

View File

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