diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index bd5a4266361..637fc1d7ba7 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -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("") - } - kind.Kind = kindString.String() + gvk, _, _ := p.Typer.ObjectKind(obj) - if !groupVersionString.IsValid() { - groupVersionString = reflect.ValueOf("/") - } - 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("") + return nil + } + + // TODO: this is wrong, runtime.Unknown and runtime.Unstructured are not handled properly here. + + name := "" + 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, "/%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 { diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index 1f71995a264..3dc43870b75 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -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")}, diff --git a/pkg/kubectl/sorting_printer.go b/pkg/kubectl/sorting_printer.go index c1f3c54ae45..49e36b882e9 100644 --- a/pkg/kubectl/sorting_printer.go +++ b/pkg/kubectl/sorting_printer.go @@ -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 } } diff --git a/pkg/kubectl/sorting_printer_test.go b/pkg/kubectl/sorting_printer_test.go index 8aed8a5b0af..d62acacf07c 100644 --- a/pkg/kubectl/sorting_printer_test.go +++ b/pkg/kubectl/sorting_printer_test.go @@ -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