From bf4ab78ce95f8490c329010da0eb71980c47de88 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 3 Mar 2015 21:12:24 -0500 Subject: [PATCH 1/7] Stop round trip testing in latest --- pkg/api/latest/latest_test.go | 44 ----------------------------------- 1 file changed, 44 deletions(-) diff --git a/pkg/api/latest/latest_test.go b/pkg/api/latest/latest_test.go index f0582b5a18d..253eb7cf82a 100644 --- a/pkg/api/latest/latest_test.go +++ b/pkg/api/latest/latest_test.go @@ -18,57 +18,13 @@ package latest import ( "encoding/json" - "math/rand" "testing" internal "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - apitesting "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testing" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) -func TestInternalRoundTrip(t *testing.T) { - latest := "v1beta2" - - seed := rand.Int63() - apiObjectFuzzer := apitesting.FuzzerFor(t, "", rand.NewSource(seed)) - for k := range internal.Scheme.KnownTypes("") { - obj, err := internal.Scheme.New("", k) - if err != nil { - t.Errorf("%s: unexpected error: %v", k, err) - continue - } - apiObjectFuzzer.Fuzz(obj) - - newer, err := internal.Scheme.New(latest, k) - if err != nil { - t.Errorf("%s: unexpected error: %v", k, err) - continue - } - - if err := internal.Scheme.Convert(obj, newer); err != nil { - t.Errorf("unable to convert %#v to %#v: %v", obj, newer, err) - continue - } - - actual, err := internal.Scheme.New("", k) - if err != nil { - t.Errorf("%s: unexpected error: %v", k, err) - continue - } - - if err := internal.Scheme.Convert(newer, actual); err != nil { - t.Errorf("unable to convert %#v to %#v: %v", newer, actual, err) - continue - } - - if !internal.Semantic.DeepEqual(obj, actual) { - t.Errorf("%s: diff %s", k, util.ObjectDiff(obj, actual)) - } - } -} - func TestResourceVersioner(t *testing.T) { pod := internal.Pod{ObjectMeta: internal.ObjectMeta{ResourceVersion: "10"}} version, err := ResourceVersioner.ResourceVersion(&pod) From 3beea3b660ddbf8e93cb8d1da4670d4b0e44741d Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 3 Mar 2015 21:12:50 -0500 Subject: [PATCH 2/7] Skip types that are not registered on an API version --- pkg/apiserver/api_installer.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index 9ccb315aa54..db4ef1adba7 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -26,6 +26,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" + "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/emicklei/go-restful" @@ -106,6 +107,9 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage RESTStorage return err } versionedPtr, err := api.Scheme.New(a.version, kind) + if conversion.IsNotRegisteredError(err) { + return nil + } if err != nil { return err } From 576bbb565ebf2912616c2277aa6c283caaa5a6fb Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 3 Mar 2015 21:13:09 -0500 Subject: [PATCH 3/7] Stop logging stack traces for 4xx requests --- pkg/httplog/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/httplog/log.go b/pkg/httplog/log.go index ec26de718e9..21f01519184 100644 --- a/pkg/httplog/log.go +++ b/pkg/httplog/log.go @@ -74,7 +74,7 @@ func (passthroughLogger) Addf(format string, data ...interface{}) { // DefaultStacktracePred is the default implementation of StacktracePred. func DefaultStacktracePred(status int) bool { - return (status < http.StatusOK || status >= http.StatusBadRequest) && status != http.StatusSwitchingProtocols + return (status < http.StatusOK || status >= http.StatusInternalServerError) && status != http.StatusSwitchingProtocols } // NewLogged turns a normal response writer into a logged response writer. From a52b0f26198f9378837ff6b967d5ebdb090b39a0 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 3 Mar 2015 21:14:15 -0500 Subject: [PATCH 4/7] Switch List/Watch to ListPredicate/WatchPredicate --- pkg/registry/event/rest.go | 4 +-- pkg/registry/generic/etcd/etcd.go | 37 ++++++++++++++++++++------ pkg/registry/generic/etcd/etcd_test.go | 4 +-- pkg/registry/generic/registry.go | 4 +-- pkg/registry/limitrange/rest.go | 4 +-- pkg/registry/namespace/rest.go | 4 +-- pkg/registry/pod/etcd/etcd.go | 8 ++++-- pkg/registry/registrytest/generic.go | 4 +-- pkg/registry/resourcequota/rest.go | 4 +-- pkg/registry/secret/rest.go | 4 +-- 10 files changed, 51 insertions(+), 26 deletions(-) diff --git a/pkg/registry/event/rest.go b/pkg/registry/event/rest.go index 07d8a61f8cf..f4ec1981e00 100644 --- a/pkg/registry/event/rest.go +++ b/pkg/registry/event/rest.go @@ -131,13 +131,13 @@ func (rs *REST) getAttrs(obj runtime.Object) (objLabels, objFields labels.Set, e } func (rs *REST) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) { - return rs.registry.List(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) + return rs.registry.ListPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) } // Watch returns Events events via a watch.Interface. // It implements apiserver.ResourceWatcher. func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) { - return rs.registry.Watch(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) + return rs.registry.WatchPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) } // New returns a new api.Event diff --git a/pkg/registry/generic/etcd/etcd.go b/pkg/registry/generic/etcd/etcd.go index b48797b7d42..30516cead1f 100644 --- a/pkg/registry/generic/etcd/etcd.go +++ b/pkg/registry/generic/etcd/etcd.go @@ -23,6 +23,7 @@ import ( kubeerr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest" + "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" @@ -70,6 +71,9 @@ type Etcd struct { // is an operation against an existing object. TTLFunc func(obj runtime.Object, update bool) (uint64, error) + // Returns a matcher corresponding to the provided labels and fields. + PredicateFunc func(label, field labels.Selector) generic.Matcher + // Called on all objects returned from the underlying store, after // the exit hooks are invoked. Decorators are intended for integrations // that are above etcd and should only be used for specific cases where @@ -119,10 +123,23 @@ func NamespaceKeyFunc(ctx api.Context, prefix string, name string) (string, erro return key, nil } -// List returns a list of all the items matching m. -// TODO: rename this to ListPredicate, take the default predicate function on the constructor, and -// introduce a List method that uses the default predicate function -func (e *Etcd) List(ctx api.Context, m generic.Matcher) (runtime.Object, error) { +// New implements RESTStorage +func (e *Etcd) New() runtime.Object { + return e.NewFunc() +} + +// NewList implements RESTLister +func (e *Etcd) NewList() runtime.Object { + return e.NewListFunc() +} + +// List returns a list of items matching labels and field +func (e *Etcd) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) { + return e.ListPredicate(ctx, e.PredicateFunc(label, field)) +} + +// ListPredicate returns a list of all the items matching m. +func (e *Etcd) ListPredicate(ctx api.Context, m generic.Matcher) (runtime.Object, error) { list := e.NewListFunc() err := e.Helper.ExtractToList(e.KeyRootFunc(ctx), list) if err != nil { @@ -339,11 +356,15 @@ func (e *Etcd) Delete(ctx api.Context, name string) (runtime.Object, error) { return &api.Status{Status: api.StatusSuccess}, nil } -// Watch starts a watch for the items that m matches. +// WatchPredicate starts a watch for the items that m matches. // TODO: Detect if m references a single object instead of a list. -// TODO: rename this to WatchPredicate, take the default predicate function on the constructor, and -// introduce a Watch method that uses the default predicate function -func (e *Etcd) Watch(ctx api.Context, m generic.Matcher, resourceVersion string) (watch.Interface, error) { +func (e *Etcd) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) { + return e.WatchPredicate(ctx, e.PredicateFunc(label, field), resourceVersion) +} + +// WatchPredicate starts a watch for the items that m matches. +// TODO: Detect if m references a single object instead of a list. +func (e *Etcd) WatchPredicate(ctx api.Context, m generic.Matcher, resourceVersion string) (watch.Interface, error) { version, err := tools.ParseWatchResourceVersion(resourceVersion, e.EndpointName) if err != nil { return nil, err diff --git a/pkg/registry/generic/etcd/etcd_test.go b/pkg/registry/generic/etcd/etcd_test.go index 59d9ee1782c..9c052014938 100644 --- a/pkg/registry/generic/etcd/etcd_test.go +++ b/pkg/registry/generic/etcd/etcd_test.go @@ -172,7 +172,7 @@ func TestEtcdList(t *testing.T) { for name, item := range table { fakeClient, registry := NewTestGenericEtcdRegistry(t) fakeClient.Data[registry.KeyRootFunc(api.NewContext())] = item.in - list, err := registry.List(api.NewContext(), item.m) + list, err := registry.ListPredicate(api.NewContext(), item.m) if e, a := item.succeed, err == nil; e != a { t.Errorf("%v: expected %v, got %v", name, e, a) continue @@ -660,7 +660,7 @@ func TestEtcdWatch(t *testing.T) { } fakeClient, registry := NewTestGenericEtcdRegistry(t) - wi, err := registry.Watch(api.NewContext(), EverythingMatcher{}, "1") + wi, err := registry.WatchPredicate(api.NewContext(), EverythingMatcher{}, "1") if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/registry/generic/registry.go b/pkg/registry/generic/registry.go index f801b754d75..c194208a25d 100644 --- a/pkg/registry/generic/registry.go +++ b/pkg/registry/generic/registry.go @@ -75,12 +75,12 @@ type DecoratorFunc func(obj runtime.Object) error // layer. // DEPRECATED: replace with direct implementation of RESTStorage type Registry interface { - List(api.Context, Matcher) (runtime.Object, error) + ListPredicate(api.Context, Matcher) (runtime.Object, error) CreateWithName(ctx api.Context, id string, obj runtime.Object) error UpdateWithName(ctx api.Context, id string, obj runtime.Object) error Get(ctx api.Context, id string) (runtime.Object, error) Delete(ctx api.Context, id string) (runtime.Object, error) - Watch(ctx api.Context, m Matcher, resourceVersion string) (watch.Interface, error) + WatchPredicate(ctx api.Context, m Matcher, resourceVersion string) (watch.Interface, error) } // FilterList filters any list object that conforms to the api conventions, diff --git a/pkg/registry/limitrange/rest.go b/pkg/registry/limitrange/rest.go index 26637e19e2d..7d9bf8c5f58 100644 --- a/pkg/registry/limitrange/rest.go +++ b/pkg/registry/limitrange/rest.go @@ -135,11 +135,11 @@ func (rs *REST) getAttrs(obj runtime.Object) (objLabels, objFields labels.Set, e } func (rs *REST) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) { - return rs.registry.List(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) + return rs.registry.ListPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) } func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) { - return rs.registry.Watch(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) + return rs.registry.WatchPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) } // New returns a new api.LimitRange diff --git a/pkg/registry/namespace/rest.go b/pkg/registry/namespace/rest.go index 055dfe1f79d..db71a3a5cc9 100644 --- a/pkg/registry/namespace/rest.go +++ b/pkg/registry/namespace/rest.go @@ -109,11 +109,11 @@ func (rs *REST) getAttrs(obj runtime.Object) (objLabels, objFields labels.Set, e } func (rs *REST) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) { - return rs.registry.List(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) + return rs.registry.ListPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) } func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) { - return rs.registry.Watch(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) + return rs.registry.WatchPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) } // New returns a new api.Namespace diff --git a/pkg/registry/pod/etcd/etcd.go b/pkg/registry/pod/etcd/etcd.go index 9bedb080854..2ca4e738446 100644 --- a/pkg/registry/pod/etcd/etcd.go +++ b/pkg/registry/pod/etcd/etcd.go @@ -24,6 +24,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest" "github.com/GoogleCloudPlatform/kubernetes/pkg/constraint" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic" etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" @@ -53,6 +54,9 @@ func NewREST(h tools.EtcdHelper, factory pod.BoundPodFactory) (*REST, *BindingRE ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.Pod).Name, nil }, + PredicateFunc: func(label, field labels.Selector) generic.Matcher { + return pod.MatchPod(label, field) + }, EndpointName: "pods", Helper: h, @@ -92,12 +96,12 @@ func (r *REST) NewList() runtime.Object { // List obtains a list of pods with labels that match selector. func (r *REST) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) { - return r.store.List(ctx, pod.MatchPod(label, field)) + return r.store.List(ctx, label, field) } // Watch begins watching for new, changed, or deleted pods. func (r *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) { - return r.store.Watch(ctx, pod.MatchPod(label, field), resourceVersion) + return r.store.Watch(ctx, label, field, resourceVersion) } // Get gets a specific pod specified by its ID. diff --git a/pkg/registry/registrytest/generic.go b/pkg/registry/registrytest/generic.go index 820528f4aee..3cecc0a10a9 100644 --- a/pkg/registry/registrytest/generic.go +++ b/pkg/registry/registrytest/generic.go @@ -42,7 +42,7 @@ func NewGeneric(list runtime.Object) *GenericRegistry { } } -func (r *GenericRegistry) List(ctx api.Context, m generic.Matcher) (runtime.Object, error) { +func (r *GenericRegistry) ListPredicate(ctx api.Context, m generic.Matcher) (runtime.Object, error) { r.Lock() defer r.Unlock() if r.Err != nil { @@ -51,7 +51,7 @@ func (r *GenericRegistry) List(ctx api.Context, m generic.Matcher) (runtime.Obje return generic.FilterList(r.ObjectList, m, nil) } -func (r *GenericRegistry) Watch(ctx api.Context, m generic.Matcher, resourceVersion string) (watch.Interface, error) { +func (r *GenericRegistry) WatchPredicate(ctx api.Context, m generic.Matcher, resourceVersion string) (watch.Interface, error) { // TODO: wire filter down into the mux; it needs access to current and previous state :( return r.Broadcaster.Watch(), nil } diff --git a/pkg/registry/resourcequota/rest.go b/pkg/registry/resourcequota/rest.go index 62597c47d49..98616f2a351 100644 --- a/pkg/registry/resourcequota/rest.go +++ b/pkg/registry/resourcequota/rest.go @@ -138,11 +138,11 @@ func (rs *REST) getAttrs(obj runtime.Object) (objLabels, objFields labels.Set, e } func (rs *REST) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) { - return rs.registry.List(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) + return rs.registry.ListPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) } func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) { - return rs.registry.Watch(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) + return rs.registry.WatchPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) } // New returns a new api.ResourceQuota diff --git a/pkg/registry/secret/rest.go b/pkg/registry/secret/rest.go index 8bdec0de328..265d23c5544 100644 --- a/pkg/registry/secret/rest.go +++ b/pkg/registry/secret/rest.go @@ -145,11 +145,11 @@ func (rs *REST) getAttrs(obj runtime.Object) (objLabels, objFields labels.Set, e } func (rs *REST) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) { - return rs.registry.List(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) + return rs.registry.ListPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}) } func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) { - return rs.registry.Watch(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) + return rs.registry.WatchPredicate(ctx, &generic.SelectionPredicate{label, field, rs.getAttrs}, resourceVersion) } // New returns a new api.Secret From b799e8cd3a2904a3b8b1ff329e668b770435e8bd Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 3 Mar 2015 22:18:15 -0500 Subject: [PATCH 5/7] Enumerate all versions when looking for an output conversion Ensures that all objects can be printed, even if they don't match output-version (because they are only implemented in a newer API version). --- pkg/kubectl/cmd/get.go | 25 ++++++++++++++++--------- pkg/kubectl/resource_printer.go | 22 ++++++++++++++++------ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 8e06ff0f6f7..860f3d91e71 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -138,18 +138,25 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) { // the outermost object will be converted to the output-version version := util.OutputVersion(cmd, defaultVersion) - if len(version) == 0 { - // TODO: add a new ResourceBuilder mode for Object() that attempts to ensure the objects - // are in the appropriate version if one exists (and if not, use the best effort). - // TODO: ensure api-version is set with the default preferred api version by the client - // builder on initialization - version = latest.Version - } - printer = kubectl.NewVersionedPrinter(printer, api.Scheme, version) - obj, err := b.Flatten().Do().Object() + r := b.Flatten().Do() + obj, err := r.Object() checkErr(err) + // try conversion to all the possible versions + // TODO: simplify by adding a ResourceBuilder mode + versions := []string{version, latest.Version} + infos, _ := r.Infos() + for _, info := range infos { + versions = append(versions, info.Mapping.APIVersion) + } + + // TODO: add a new ResourceBuilder mode for Object() that attempts to ensure the objects + // are in the appropriate version if one exists (and if not, use the best effort). + // TODO: ensure api-version is set with the default preferred api version by the client + // builder on initialization + printer := kubectl.NewVersionedPrinter(printer, api.Scheme, versions...) + err = printer.PrintObj(obj, out) checkErr(err) return diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index ee2a385acd2..340c444ced0 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -32,6 +32,7 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/docker/docker/pkg/units" "github.com/ghodss/yaml" @@ -98,11 +99,11 @@ func (fn ResourcePrinterFunc) PrintObj(obj runtime.Object, w io.Writer) error { type VersionedPrinter struct { printer ResourcePrinter convertor runtime.ObjectConvertor - version string + version []string } // NewVersionedPrinter wraps a printer to convert objects to a known API version prior to printing. -func NewVersionedPrinter(printer ResourcePrinter, convertor runtime.ObjectConvertor, version string) ResourcePrinter { +func NewVersionedPrinter(printer ResourcePrinter, convertor runtime.ObjectConvertor, version ...string) ResourcePrinter { return &VersionedPrinter{ printer: printer, convertor: convertor, @@ -115,11 +116,20 @@ func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error { if len(p.version) == 0 { return fmt.Errorf("no version specified, object cannot be converted") } - converted, err := p.convertor.ConvertToVersion(obj, p.version) - if err != nil { - return err + for _, version := range p.version { + if len(version) == 0 { + continue + } + converted, err := p.convertor.ConvertToVersion(obj, version) + if conversion.IsNotRegisteredError(err) { + continue + } + if err != nil { + return err + } + return p.printer.PrintObj(converted, w) } - return p.printer.PrintObj(converted, w) + return fmt.Errorf("the object cannot be converted to any of the versions: %v", p.version) } // JSONPrinter is an implementation of ResourcePrinter which outputs an object as JSON. From a97635e643ae638b19fbfcb02bbe1b3a586e1e13 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 3 Mar 2015 22:18:57 -0500 Subject: [PATCH 6/7] Use IsListType instead of checking ItemPtr in resourcebuilder --- pkg/kubectl/resource/result.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kubectl/resource/result.go b/pkg/kubectl/resource/result.go index 9ceeda5a840..13e5b89638b 100644 --- a/pkg/kubectl/resource/result.go +++ b/pkg/kubectl/resource/result.go @@ -133,7 +133,7 @@ func (r *Result) Object() (runtime.Object, error) { return objects[0], nil } // if the item is a list already, don't create another list - if _, err := runtime.GetItemsPtr(objects[0]); err == nil { + if runtime.IsListType(objects[0]) { return objects[0], nil } } From 17f7dd8d6db2ed687422257d0a9140796bdd54bb Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 3 Mar 2015 22:56:31 -0500 Subject: [PATCH 7/7] Only insert outputversion as a preferred mapping type if no error occurs Some types will be looked up based on their internal type, which is passed directly to RESTMapping(). RESTMapping() will search the entire version list if no external types are passed, but OutputVersionMapper was bypassing that because the default type was always present. Change OutputVersionMapper to collapse empty versions, and when no external type is specified, try with the preferred version and then try without. --- pkg/kubectl/kubectl.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index b2b5e74bbee..166f4876485 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -63,7 +63,20 @@ type OutputVersionMapper struct { // RESTMapping implements meta.RESTMapper by prepending the output version to the preferred version list. func (m OutputVersionMapper) RESTMapping(kind string, versions ...string) (*meta.RESTMapping, error) { - preferred := append([]string{m.OutputVersion}, versions...) + preferred := []string{m.OutputVersion} + for _, version := range versions { + if len(version) > 0 { + preferred = append(preferred, version) + } + } + // if the caller wants to use the default version list, try with the preferred version, and on + // error, use the default behavior. + if len(preferred) == 1 { + if m, err := m.RESTMapper.RESTMapping(kind, preferred...); err == nil { + return m, nil + } + preferred = nil + } return m.RESTMapper.RESTMapping(kind, preferred...) }