From d9f07925be40db7b9cfe9c6aa81bbec857a58792 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Mon, 20 Jun 2016 12:47:10 -0700 Subject: [PATCH] let dynamic client handle non-registered ListOptions; register ListOptions for apis/policy --- pkg/api/serialization_test.go | 14 +++++++++++ pkg/apis/apps/v1alpha1/register.go | 1 + pkg/apis/autoscaling/v1/register.go | 1 + pkg/apis/batch/v1/register.go | 1 + pkg/apis/batch/v2alpha1/register.go | 1 + pkg/apis/policy/v1alpha1/register.go | 3 +++ pkg/client/typed/dynamic/client.go | 23 +++++++++++++++++++ .../garbagecollector/garbagecollector.go | 14 +++++------ 8 files changed, 50 insertions(+), 8 deletions(-) diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 4dbd1430c4de5..e0b470c4304bb 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -174,6 +174,20 @@ var nonRoundTrippableTypes = sets.NewString( "WatchEvent", ) +var commonKinds = []string{"ListOptions", "DeleteOptions"} + +// verify all external group/versions have the common kinds like the ListOptions, DeleteOptions are registered. +func TestCommonKindsRegistered(t *testing.T) { + for _, kind := range commonKinds { + for _, group := range testapi.Groups { + gv := group.GroupVersion() + if _, err := api.Scheme.New(gv.WithKind(kind)); err != nil { + t.Error(err) + } + } + } +} + var nonInternalRoundTrippableTypes = sets.NewString("List", "ListOptions", "ExportOptions") var nonRoundTrippableTypesByVersion = map[string][]string{} diff --git a/pkg/apis/apps/v1alpha1/register.go b/pkg/apis/apps/v1alpha1/register.go index e069807754b84..9ab37dfb0df8a 100644 --- a/pkg/apis/apps/v1alpha1/register.go +++ b/pkg/apis/apps/v1alpha1/register.go @@ -41,6 +41,7 @@ func addKnownTypes(scheme *runtime.Scheme) { &PetSet{}, &PetSetList{}, &v1.ListOptions{}, + &v1.DeleteOptions{}, ) versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion) } diff --git a/pkg/apis/autoscaling/v1/register.go b/pkg/apis/autoscaling/v1/register.go index bed584f0d1191..fed2cdf48cfff 100644 --- a/pkg/apis/autoscaling/v1/register.go +++ b/pkg/apis/autoscaling/v1/register.go @@ -41,6 +41,7 @@ func addKnownTypes(scheme *runtime.Scheme) { &HorizontalPodAutoscalerList{}, &Scale{}, &v1.ListOptions{}, + &v1.DeleteOptions{}, ) versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion) } diff --git a/pkg/apis/batch/v1/register.go b/pkg/apis/batch/v1/register.go index 3a5e1cfe38939..d8c087f1bb96f 100644 --- a/pkg/apis/batch/v1/register.go +++ b/pkg/apis/batch/v1/register.go @@ -41,6 +41,7 @@ func addKnownTypes(scheme *runtime.Scheme) { &Job{}, &JobList{}, &v1.ListOptions{}, + &v1.DeleteOptions{}, ) versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion) } diff --git a/pkg/apis/batch/v2alpha1/register.go b/pkg/apis/batch/v2alpha1/register.go index 1c33ab6782f9e..00142f018a72f 100644 --- a/pkg/apis/batch/v2alpha1/register.go +++ b/pkg/apis/batch/v2alpha1/register.go @@ -44,6 +44,7 @@ func addKnownTypes(scheme *runtime.Scheme) { &ScheduledJob{}, &ScheduledJobList{}, &v1.ListOptions{}, + &v1.DeleteOptions{}, ) versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion) } diff --git a/pkg/apis/policy/v1alpha1/register.go b/pkg/apis/policy/v1alpha1/register.go index dd6c2dd14c32a..a6a94d96d007a 100644 --- a/pkg/apis/policy/v1alpha1/register.go +++ b/pkg/apis/policy/v1alpha1/register.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" versionedwatch "k8s.io/kubernetes/pkg/watch/versioned" ) @@ -41,6 +42,8 @@ func addKnownTypes(scheme *runtime.Scheme) { scheme.AddKnownTypes(SchemeGroupVersion, &PodDisruptionBudget{}, &PodDisruptionBudgetList{}, + &v1.ListOptions{}, + &v1.DeleteOptions{}, ) // Add the watch version that applies versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/pkg/client/typed/dynamic/client.go b/pkg/client/typed/dynamic/client.go index 5d690877f0733..26369bd582340 100644 --- a/pkg/client/typed/dynamic/client.go +++ b/pkg/client/typed/dynamic/client.go @@ -270,3 +270,26 @@ func (parameterCodec) DecodeParameters(parameters url.Values, from unversioned.G } var defaultParameterEncoder runtime.ParameterCodec = parameterCodec{} + +type versionedParameterEncoderWithV1Fallback struct{} + +func (versionedParameterEncoderWithV1Fallback) EncodeParameters(obj runtime.Object, to unversioned.GroupVersion) (url.Values, error) { + ret, err := api.ParameterCodec.EncodeParameters(obj, to) + if err != nil && runtime.IsNotRegisteredError(err) { + // fallback to v1 + return api.ParameterCodec.EncodeParameters(obj, v1.SchemeGroupVersion) + } + return ret, err +} + +func (versionedParameterEncoderWithV1Fallback) DecodeParameters(parameters url.Values, from unversioned.GroupVersion, into runtime.Object) error { + return errors.New("DecodeParameters not implemented on versionedParameterEncoderWithV1Fallback") +} + +// VersionedParameterEncoderWithV1Fallback is useful for encoding query +// parameters for thirdparty resources. It tries to convert object to the +// specified version before converting it to query parameters, and falls back to +// converting to v1 if the object is not registered in the specified version. +// For the record, currently API server always treats query parameters sent to a +// thirdparty resource endpoint as v1. +var VersionedParameterEncoderWithV1Fallback runtime.ParameterCodec = versionedParameterEncoderWithV1Fallback{} diff --git a/pkg/controller/garbagecollector/garbagecollector.go b/pkg/controller/garbagecollector/garbagecollector.go index 0b7431278fae4..cbd0ba29e76e4 100644 --- a/pkg/controller/garbagecollector/garbagecollector.go +++ b/pkg/controller/garbagecollector/garbagecollector.go @@ -443,11 +443,9 @@ func gcListWatcher(client *dynamic.Client, resource unversioned.GroupVersionReso // namespaces if it's namespace scoped, so leave // APIResource.Namespaced as false is all right. apiResource := unversioned.APIResource{Name: resource.Resource} - // The default parameter codec used by the dynamic client cannot - // encode api.ListOptions. - // TODO: api.ParameterCodec doesn't support thirdparty objects. - // We need a generic parameter codec. - return client.ParameterCodec(api.ParameterCodec).Resource(&apiResource, api.NamespaceAll).List(&options) + return client.ParameterCodec(dynamic.VersionedParameterEncoderWithV1Fallback). + Resource(&apiResource, api.NamespaceAll). + List(&options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { // APIResource.Kind is not used by the dynamic client, so @@ -455,9 +453,9 @@ func gcListWatcher(client *dynamic.Client, resource unversioned.GroupVersionReso // namespaces if it's namespace scoped, so leave // APIResource.Namespaced as false is all right. apiResource := unversioned.APIResource{Name: resource.Resource} - // The default parameter codec used by the dynamic client cannot - // encode api.ListOptions. - return client.ParameterCodec(api.ParameterCodec).Resource(&apiResource, api.NamespaceAll).Watch(&options) + return client.ParameterCodec(dynamic.VersionedParameterEncoderWithV1Fallback). + Resource(&apiResource, api.NamespaceAll). + Watch(&options) }, } }