From d8924bc1c914c63fd7e93f7e562edfddd911d9d7 Mon Sep 17 00:00:00 2001 From: David Eads Date: Wed, 9 May 2018 12:54:24 -0400 Subject: [PATCH] move old dynamic client to deprecated-client --- hack/.golint_failures | 1 + .../garbagecollector/graph_builder.go | 3 - pkg/kubectl/BUILD | 1 + pkg/kubectl/cmd/BUILD | 1 - pkg/kubectl/cmd/create/BUILD | 2 +- pkg/kubectl/cmd/create/create_test.go | 4 +- pkg/kubectl/cmd/delete_test.go | 3 +- pkg/kubectl/cmd/get/BUILD | 2 +- pkg/kubectl/cmd/get/get_test.go | 4 +- .../cmd/util/factory_object_mapping.go | 2 +- pkg/kubectl/genericclioptions/resource/BUILD | 1 - .../genericclioptions/resource/client.go | 3 +- pkg/kubectl/resource/scheme.go | 79 +++ staging/BUILD | 1 + .../k8s.io/client-go/deprecated-dynamic/BUILD | 56 ++ .../bad_debt.go | 2 +- .../{dynamic => deprecated-dynamic}/client.go | 40 +- .../client_pool.go | 2 +- .../deprecated-dynamic/client_test.go | 623 ++++++++++++++++++ staging/src/k8s.io/client-go/dynamic/BUILD | 11 +- .../k8s.io/client-go/dynamic/dynamic_util.go | 87 --- .../client-go/dynamic/dynamic_util_test.go | 79 --- .../src/k8s.io/client-go/dynamic/fake/BUILD | 7 +- .../k8s.io/client-go/dynamic/fake/client.go | 163 ----- .../client-go/dynamic/fake/client_pool.go | 48 -- .../src/k8s.io/client-go/dynamic/interface.go | 34 + .../integration/client/dynamic_client_test.go | 31 +- 27 files changed, 843 insertions(+), 447 deletions(-) create mode 100644 pkg/kubectl/resource/scheme.go create mode 100644 staging/src/k8s.io/client-go/deprecated-dynamic/BUILD rename staging/src/k8s.io/client-go/{dynamic => deprecated-dynamic}/bad_debt.go (98%) rename staging/src/k8s.io/client-go/{dynamic => deprecated-dynamic}/client.go (73%) rename staging/src/k8s.io/client-go/{dynamic => deprecated-dynamic}/client_pool.go (99%) create mode 100644 staging/src/k8s.io/client-go/deprecated-dynamic/client_test.go delete mode 100644 staging/src/k8s.io/client-go/dynamic/dynamic_util.go delete mode 100644 staging/src/k8s.io/client-go/dynamic/dynamic_util_test.go delete mode 100644 staging/src/k8s.io/client-go/dynamic/fake/client.go delete mode 100644 staging/src/k8s.io/client-go/dynamic/fake/client_pool.go create mode 100644 staging/src/k8s.io/client-go/dynamic/interface.go diff --git a/hack/.golint_failures b/hack/.golint_failures index 85b97d89884..451b22ebbf2 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -581,6 +581,7 @@ staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/tokentest staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook +staging/src/k8s.io/client-go/deprecated-dynamic staging/src/k8s.io/client-go/discovery/cached staging/src/k8s.io/client-go/dynamic staging/src/k8s.io/client-go/dynamic/fake diff --git a/pkg/controller/garbagecollector/graph_builder.go b/pkg/controller/garbagecollector/graph_builder.go index 1fbb7a1ce7f..407af134504 100644 --- a/pkg/controller/garbagecollector/graph_builder.go +++ b/pkg/controller/garbagecollector/graph_builder.go @@ -92,9 +92,6 @@ type GraphBuilder struct { running bool dynamicClient dynamic.DynamicInterface - // metaOnlyClientPool uses a special codec, which removes fields except for - // apiVersion, kind, and metadata during decoding. - metaOnlyClientPool dynamic.ClientPool // monitors are the producer of the graphChanges queue, graphBuilder alters // the in-memory graph according to the changes. graphChanges workqueue.RateLimitingInterface diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index 3ce1e6d876d..a5d68729099 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -206,6 +206,7 @@ filegroup( "//pkg/kubectl/metricsutil:all-srcs", "//pkg/kubectl/plugins:all-srcs", "//pkg/kubectl/proxy:all-srcs", + "//pkg/kubectl/resource:all-srcs", "//pkg/kubectl/scheme:all-srcs", "//pkg/kubectl/util:all-srcs", "//pkg/kubectl/validation:all-srcs", diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index a865578827b..2f0427cc37f 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -225,7 +225,6 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch/testing:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apimachinery/pkg/version:go_default_library", - "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library", "//vendor/k8s.io/client-go/scale/fake:go_default_library", diff --git a/pkg/kubectl/cmd/create/BUILD b/pkg/kubectl/cmd/create/BUILD index 898a9374bc8..b7c813e772c 100644 --- a/pkg/kubectl/cmd/create/BUILD +++ b/pkg/kubectl/cmd/create/BUILD @@ -82,6 +82,7 @@ go_test( "//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library", + "//pkg/kubectl/resource:go_default_library", "//pkg/kubectl/scheme:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/k8s.io/api/batch/v1:go_default_library", @@ -94,7 +95,6 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", - "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library", diff --git a/pkg/kubectl/cmd/create/create_test.go b/pkg/kubectl/cmd/create/create_test.go index 0d5a956afac..e97fd06724c 100644 --- a/pkg/kubectl/cmd/create/create_test.go +++ b/pkg/kubectl/cmd/create/create_test.go @@ -22,7 +22,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" "k8s.io/client-go/rest/fake" "k8s.io/kubernetes/pkg/api/legacyscheme" apitesting "k8s.io/kubernetes/pkg/api/testing" @@ -30,6 +29,7 @@ import ( cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" + "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/scheme" ) @@ -158,7 +158,7 @@ func TestCreateDirectory(t *testing.T) { } } -var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer +var unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer func initTestErrorHandler(t *testing.T) { cmdutil.BehaviorOnFatal(func(str string, code int) { diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index 233d0a00910..38ce65a22c7 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -30,7 +30,6 @@ import ( "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/dynamic" "k8s.io/client-go/rest/fake" "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" @@ -42,7 +41,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/scheme" ) -var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer +var unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer func fakecmd() *cobra.Command { cmd := &cobra.Command{ diff --git a/pkg/kubectl/cmd/get/BUILD b/pkg/kubectl/cmd/get/BUILD index 2f0fcd3c506..a22ded79dc4 100644 --- a/pkg/kubectl/cmd/get/BUILD +++ b/pkg/kubectl/cmd/get/BUILD @@ -75,6 +75,7 @@ go_test( "//pkg/kubectl/cmd/util/openapi:go_default_library", "//pkg/kubectl/cmd/util/openapi/testing:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library", + "//pkg/kubectl/resource:go_default_library", "//pkg/kubectl/scheme:go_default_library", "//pkg/printers:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", @@ -85,7 +86,6 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", - "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library", "//vendor/k8s.io/client-go/rest/watch:go_default_library", diff --git a/pkg/kubectl/cmd/get/get_test.go b/pkg/kubectl/cmd/get/get_test.go index d63b8bf03cd..711e144cfdc 100644 --- a/pkg/kubectl/cmd/get/get_test.go +++ b/pkg/kubectl/cmd/get/get_test.go @@ -34,7 +34,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/apimachinery/pkg/runtime/serializer/streaming" "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/dynamic" restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" restclientwatch "k8s.io/client-go/rest/watch" @@ -49,6 +48,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" openapitesting "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing" "k8s.io/kubernetes/pkg/kubectl/genericclioptions" + "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/scheme" ) @@ -61,7 +61,7 @@ func init() { scheme.Scheme.AddConversionFuncs(v1.Convert_v1_PodSecurityContext_To_core_PodSecurityContext) } -var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer +var unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer func defaultHeader() http.Header { header := http.Header{} diff --git a/pkg/kubectl/cmd/util/factory_object_mapping.go b/pkg/kubectl/cmd/util/factory_object_mapping.go index fdfe158eb3f..0fbd425b90d 100644 --- a/pkg/kubectl/cmd/util/factory_object_mapping.go +++ b/pkg/kubectl/cmd/util/factory_object_mapping.go @@ -117,7 +117,7 @@ func (f *ring1Factory) UnstructuredClientForMapping(mapping *meta.RESTMapping) ( cfg.APIPath = "/api" } gv := mapping.GroupVersionKind.GroupVersion() - cfg.ContentConfig = dynamic.ContentConfig() + cfg.ContentConfig = resource.UnstructuredPlusDefaultContentConfig() cfg.GroupVersion = &gv return restclient.RESTClientFor(cfg) } diff --git a/pkg/kubectl/genericclioptions/resource/BUILD b/pkg/kubectl/genericclioptions/resource/BUILD index a8eda3b99a3..29e7119a146 100644 --- a/pkg/kubectl/genericclioptions/resource/BUILD +++ b/pkg/kubectl/genericclioptions/resource/BUILD @@ -36,7 +36,6 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/discovery:go_default_library", - "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/restmapper:go_default_library", ], diff --git a/pkg/kubectl/genericclioptions/resource/client.go b/pkg/kubectl/genericclioptions/resource/client.go index aa1d232a9aa..46380207f3e 100644 --- a/pkg/kubectl/genericclioptions/resource/client.go +++ b/pkg/kubectl/genericclioptions/resource/client.go @@ -19,7 +19,6 @@ package resource import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ) @@ -47,7 +46,7 @@ func (clientConfigFn ClientConfigFunc) unstructuredClientForGroupVersion(gv sche if err != nil { return nil, err } - cfg.ContentConfig = dynamic.ContentConfig() + cfg.ContentConfig = UnstructuredPlusDefaultContentConfig() cfg.GroupVersion = &gv if len(gv.Group) == 0 { cfg.APIPath = "/api" diff --git a/pkg/kubectl/resource/scheme.go b/pkg/kubectl/resource/scheme.go new file mode 100644 index 00000000000..fef6edfc1b0 --- /dev/null +++ b/pkg/kubectl/resource/scheme.go @@ -0,0 +1,79 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resource + +import ( + "encoding/json" + "io" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" +) + +// dynamicCodec is a codec that wraps the standard unstructured codec +// with special handling for Status objects. +// Deprecated only used by test code and its wrong +type dynamicCodec struct{} + +func (dynamicCodec) Decode(data []byte, gvk *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { + obj, gvk, err := unstructured.UnstructuredJSONScheme.Decode(data, gvk, obj) + if err != nil { + return nil, nil, err + } + + if _, ok := obj.(*metav1.Status); !ok && strings.ToLower(gvk.Kind) == "status" { + obj = &metav1.Status{} + err := json.Unmarshal(data, obj) + if err != nil { + return nil, nil, err + } + } + + return obj, gvk, nil +} + +func (dynamicCodec) Encode(obj runtime.Object, w io.Writer) error { + return unstructured.UnstructuredJSONScheme.Encode(obj, w) +} + +// ContentConfig returns a rest.ContentConfig for dynamic types. It includes enough codecs to act as a "normal" +// serializer for the rest.client with options, status and the like. +func UnstructuredPlusDefaultContentConfig() rest.ContentConfig { + var jsonInfo runtime.SerializerInfo + // TODO: scheme.Codecs here should become "pkg/apis/server/scheme" which is the minimal core you need + // to talk to a kubernetes server + for _, info := range scheme.Codecs.SupportedMediaTypes() { + if info.MediaType == runtime.ContentTypeJSON { + jsonInfo = info + break + } + } + + jsonInfo.Serializer = dynamicCodec{} + jsonInfo.PrettySerializer = nil + return rest.ContentConfig{ + AcceptContentTypes: runtime.ContentTypeJSON, + ContentType: runtime.ContentTypeJSON, + NegotiatedSerializer: serializer.NegotiatedSerializerWrapper(jsonInfo), + } +} diff --git a/staging/BUILD b/staging/BUILD index 15b7d66c27d..4e5d5a3efbd 100644 --- a/staging/BUILD +++ b/staging/BUILD @@ -99,6 +99,7 @@ filegroup( "//staging/src/k8s.io/apiserver/plugin/pkg/audit:all-srcs", "//staging/src/k8s.io/apiserver/plugin/pkg/authenticator:all-srcs", "//staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook:all-srcs", + "//staging/src/k8s.io/client-go/deprecated-dynamic:all-srcs", "//staging/src/k8s.io/client-go/discovery:all-srcs", "//staging/src/k8s.io/client-go/dynamic:all-srcs", "//staging/src/k8s.io/client-go/examples/create-update-delete-deployment:all-srcs", diff --git a/staging/src/k8s.io/client-go/deprecated-dynamic/BUILD b/staging/src/k8s.io/client-go/deprecated-dynamic/BUILD new file mode 100644 index 00000000000..530e34285d5 --- /dev/null +++ b/staging/src/k8s.io/client-go/deprecated-dynamic/BUILD @@ -0,0 +1,56 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "bad_debt.go", + "client.go", + "client_pool.go", + ], + importpath = "k8s.io/client-go/deprecated-dynamic", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["client_test.go"], + embed = [":go_default_library"], + deps = [ + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/rest/watch:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/client-go/dynamic/bad_debt.go b/staging/src/k8s.io/client-go/deprecated-dynamic/bad_debt.go similarity index 98% rename from staging/src/k8s.io/client-go/dynamic/bad_debt.go rename to staging/src/k8s.io/client-go/deprecated-dynamic/bad_debt.go index 8492d56afcc..51e4a5830ec 100644 --- a/staging/src/k8s.io/client-go/dynamic/bad_debt.go +++ b/staging/src/k8s.io/client-go/deprecated-dynamic/bad_debt.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package dynamic +package deprecated_dynamic import ( "encoding/json" diff --git a/staging/src/k8s.io/client-go/dynamic/client.go b/staging/src/k8s.io/client-go/deprecated-dynamic/client.go similarity index 73% rename from staging/src/k8s.io/client-go/dynamic/client.go rename to staging/src/k8s.io/client-go/deprecated-dynamic/client.go index 43db68c41f8..46c7535aab4 100644 --- a/staging/src/k8s.io/client-go/dynamic/client.go +++ b/staging/src/k8s.io/client-go/deprecated-dynamic/client.go @@ -17,7 +17,7 @@ limitations under the License. // Package dynamic provides a client interface to arbitrary Kubernetes // APIs that exposes common high level operations and exposes common // metadata. -package dynamic +package deprecated_dynamic import ( "strings" @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" restclient "k8s.io/client-go/rest" ) @@ -65,13 +66,13 @@ type ResourceInterface interface { // and manipulate metadata of a Kubernetes API group, and implements Interface. type Client struct { version schema.GroupVersion - delegate DynamicInterface + delegate dynamic.DynamicInterface } // NewClient returns a new client based on the passed in config. The // codec is ignored, as the dynamic client uses it's own codec. func NewClient(conf *restclient.Config, version schema.GroupVersion) (*Client, error) { - delegate, err := NewForConfig(conf) + delegate, err := dynamic.NewForConfig(conf) if err != nil { return nil, err } @@ -84,24 +85,41 @@ func NewClient(conf *restclient.Config, version schema.GroupVersion) (*Client, e // is ignored. The ResourceInterface inherits the parameter codec of c. func (c *Client) Resource(resource *metav1.APIResource, namespace string) ResourceInterface { resourceTokens := strings.SplitN(resource.Name, "/", 2) - subresource := "" + subresources := []string{} if len(resourceTokens) > 1 { - subresource = resourceTokens[1] + subresources = strings.Split(resourceTokens[1], "/") } if len(namespace) == 0 { - return oldResourceShim(c.delegate.ClusterSubresource(c.version.WithResource(resourceTokens[0]), subresource)) + return oldResourceShim(c.delegate.Resource(c.version.WithResource(resourceTokens[0])), subresources) } - return oldResourceShim(c.delegate.NamespacedSubresource(c.version.WithResource(resourceTokens[0]), subresource, namespace)) + return oldResourceShim(c.delegate.Resource(c.version.WithResource(resourceTokens[0])).Namespace(namespace), subresources) } // the old interfaces used the wrong type for lists. this fixes that -func oldResourceShim(in DynamicResourceInterface) ResourceInterface { - return oldResourceShimType{DynamicResourceInterface: in} +func oldResourceShim(in dynamic.DynamicResourceInterface, subresources []string) ResourceInterface { + return oldResourceShimType{DynamicResourceInterface: in, subresources: subresources} } type oldResourceShimType struct { - DynamicResourceInterface + dynamic.DynamicResourceInterface + subresources []string +} + +func (s oldResourceShimType) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return s.DynamicResourceInterface.Create(obj, s.subresources...) +} + +func (s oldResourceShimType) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return s.DynamicResourceInterface.Update(obj, s.subresources...) +} + +func (s oldResourceShimType) Delete(name string, opts *metav1.DeleteOptions) error { + return s.DynamicResourceInterface.Delete(name, opts, s.subresources...) +} + +func (s oldResourceShimType) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { + return s.DynamicResourceInterface.Get(name, opts, s.subresources...) } func (s oldResourceShimType) List(opts metav1.ListOptions) (runtime.Object, error) { @@ -109,5 +127,5 @@ func (s oldResourceShimType) List(opts metav1.ListOptions) (runtime.Object, erro } func (s oldResourceShimType) Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) { - return s.DynamicResourceInterface.Patch(name, pt, data) + return s.DynamicResourceInterface.Patch(name, pt, data, s.subresources...) } diff --git a/staging/src/k8s.io/client-go/dynamic/client_pool.go b/staging/src/k8s.io/client-go/deprecated-dynamic/client_pool.go similarity index 99% rename from staging/src/k8s.io/client-go/dynamic/client_pool.go rename to staging/src/k8s.io/client-go/deprecated-dynamic/client_pool.go index f4d258be8a7..36dc54ce487 100644 --- a/staging/src/k8s.io/client-go/dynamic/client_pool.go +++ b/staging/src/k8s.io/client-go/deprecated-dynamic/client_pool.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package dynamic +package deprecated_dynamic import ( "sync" diff --git a/staging/src/k8s.io/client-go/deprecated-dynamic/client_test.go b/staging/src/k8s.io/client-go/deprecated-dynamic/client_test.go new file mode 100644 index 00000000000..790474522a9 --- /dev/null +++ b/staging/src/k8s.io/client-go/deprecated-dynamic/client_test.go @@ -0,0 +1,623 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package deprecated_dynamic + +import ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "reflect" + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer/streaming" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + restclient "k8s.io/client-go/rest" + restclientwatch "k8s.io/client-go/rest/watch" +) + +func getJSON(version, kind, name string) []byte { + return []byte(fmt.Sprintf(`{"apiVersion": %q, "kind": %q, "metadata": {"name": %q}}`, version, kind, name)) +} + +func getListJSON(version, kind string, items ...[]byte) []byte { + json := fmt.Sprintf(`{"apiVersion": %q, "kind": %q, "items": [%s]}`, + version, kind, bytes.Join(items, []byte(","))) + return []byte(json) +} + +func getObject(version, kind, name string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": version, + "kind": kind, + "metadata": map[string]interface{}{ + "name": name, + }, + }, + } +} + +func getClientServer(gv *schema.GroupVersion, h func(http.ResponseWriter, *http.Request)) (Interface, *httptest.Server, error) { + srv := httptest.NewServer(http.HandlerFunc(h)) + cl, err := NewClient(&restclient.Config{ + Host: srv.URL, + ContentConfig: restclient.ContentConfig{GroupVersion: gv}, + }, *gv) + if err != nil { + srv.Close() + return nil, nil, err + } + return cl, srv, nil +} + +func TestList(t *testing.T) { + tcs := []struct { + name string + namespace string + path string + resp []byte + want *unstructured.UnstructuredList + }{ + { + name: "normal_list", + path: "/apis/gtest/vtest/rtest", + resp: getListJSON("vTest", "rTestList", + getJSON("vTest", "rTest", "item1"), + getJSON("vTest", "rTest", "item2")), + want: &unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "apiVersion": "vTest", + "kind": "rTestList", + }, + Items: []unstructured.Unstructured{ + *getObject("vTest", "rTest", "item1"), + *getObject("vTest", "rTest", "item2"), + }, + }, + }, + { + name: "namespaced_list", + namespace: "nstest", + path: "/apis/gtest/vtest/namespaces/nstest/rtest", + resp: getListJSON("vTest", "rTestList", + getJSON("vTest", "rTest", "item1"), + getJSON("vTest", "rTest", "item2")), + want: &unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "apiVersion": "vTest", + "kind": "rTestList", + }, + Items: []unstructured.Unstructured{ + *getObject("vTest", "rTest", "item1"), + *getObject("vTest", "rTest", "item2"), + }, + }, + }, + } + for _, tc := range tcs { + gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + t.Errorf("List(%q) got HTTP method %s. wanted GET", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("List(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + + w.Header().Set("Content-Type", runtime.ContentTypeJSON) + w.Write(tc.resp) + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + got, err := cl.Resource(resource, tc.namespace).List(metav1.ListOptions{}) + if err != nil { + t.Errorf("unexpected error when listing %q: %v", tc.name, err) + continue + } + + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("List(%q) want: %v\ngot: %v", tc.name, tc.want, got) + } + } +} + +func TestGet(t *testing.T) { + tcs := []struct { + resource string + namespace string + name string + path string + resp []byte + want *unstructured.Unstructured + }{ + { + resource: "rtest", + name: "normal_get", + path: "/apis/gtest/vtest/rtest/normal_get", + resp: getJSON("vTest", "rTest", "normal_get"), + want: getObject("vTest", "rTest", "normal_get"), + }, + { + resource: "rtest", + namespace: "nstest", + name: "namespaced_get", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_get", + resp: getJSON("vTest", "rTest", "namespaced_get"), + want: getObject("vTest", "rTest", "namespaced_get"), + }, + { + resource: "rtest/srtest", + name: "normal_subresource_get", + path: "/apis/gtest/vtest/rtest/normal_subresource_get/srtest", + resp: getJSON("vTest", "srTest", "normal_subresource_get"), + want: getObject("vTest", "srTest", "normal_subresource_get"), + }, + { + resource: "rtest/srtest", + namespace: "nstest", + name: "namespaced_subresource_get", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_get/srtest", + resp: getJSON("vTest", "srTest", "namespaced_subresource_get"), + want: getObject("vTest", "srTest", "namespaced_subresource_get"), + }, + } + for _, tc := range tcs { + gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + t.Errorf("Get(%q) got HTTP method %s. wanted GET", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("Get(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + + w.Header().Set("Content-Type", runtime.ContentTypeJSON) + w.Write(tc.resp) + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + got, err := cl.Resource(resource, tc.namespace).Get(tc.name, metav1.GetOptions{}) + if err != nil { + t.Errorf("unexpected error when getting %q: %v", tc.name, err) + continue + } + + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Get(%q) want: %v\ngot: %v", tc.name, tc.want, got) + } + } +} + +func TestDelete(t *testing.T) { + background := metav1.DeletePropagationBackground + uid := types.UID("uid") + + statusOK := &metav1.Status{ + TypeMeta: metav1.TypeMeta{Kind: "Status"}, + Status: metav1.StatusSuccess, + } + tcs := []struct { + namespace string + name string + path string + deleteOptions *metav1.DeleteOptions + }{ + { + name: "normal_delete", + path: "/apis/gtest/vtest/rtest/normal_delete", + }, + { + namespace: "nstest", + name: "namespaced_delete", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete", + }, + { + namespace: "nstest", + name: "namespaced_delete_with_options", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete_with_options", + deleteOptions: &metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &uid}, PropagationPolicy: &background}, + }, + } + for _, tc := range tcs { + gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "DELETE" { + t.Errorf("Delete(%q) got HTTP method %s. wanted DELETE", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("Delete(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + + w.Header().Set("Content-Type", runtime.ContentTypeJSON) + unstructured.UnstructuredJSONScheme.Encode(statusOK, w) + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + err = cl.Resource(resource, tc.namespace).Delete(tc.name, tc.deleteOptions) + if err != nil { + t.Errorf("unexpected error when deleting %q: %v", tc.name, err) + continue + } + } +} + +func TestDeleteCollection(t *testing.T) { + statusOK := &metav1.Status{ + TypeMeta: metav1.TypeMeta{Kind: "Status"}, + Status: metav1.StatusSuccess, + } + tcs := []struct { + namespace string + name string + path string + }{ + { + name: "normal_delete_collection", + path: "/apis/gtest/vtest/rtest", + }, + { + namespace: "nstest", + name: "namespaced_delete_collection", + path: "/apis/gtest/vtest/namespaces/nstest/rtest", + }, + } + for _, tc := range tcs { + gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "DELETE" { + t.Errorf("DeleteCollection(%q) got HTTP method %s. wanted DELETE", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("DeleteCollection(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + + w.Header().Set("Content-Type", runtime.ContentTypeJSON) + unstructured.UnstructuredJSONScheme.Encode(statusOK, w) + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + err = cl.Resource(resource, tc.namespace).DeleteCollection(nil, metav1.ListOptions{}) + if err != nil { + t.Errorf("unexpected error when deleting collection %q: %v", tc.name, err) + continue + } + } +} + +func TestCreate(t *testing.T) { + tcs := []struct { + resource string + name string + namespace string + obj *unstructured.Unstructured + path string + }{ + { + resource: "rtest", + name: "normal_create", + path: "/apis/gtest/vtest/rtest", + obj: getObject("gtest/vTest", "rTest", "normal_create"), + }, + { + resource: "rtest", + name: "namespaced_create", + namespace: "nstest", + path: "/apis/gtest/vtest/namespaces/nstest/rtest", + obj: getObject("gtest/vTest", "rTest", "namespaced_create"), + }, + } + for _, tc := range tcs { + gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + t.Errorf("Create(%q) got HTTP method %s. wanted POST", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("Create(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + + w.Header().Set("Content-Type", runtime.ContentTypeJSON) + data, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Create(%q) unexpected error reading body: %v", tc.name, err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Write(data) + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + got, err := cl.Resource(resource, tc.namespace).Create(tc.obj) + if err != nil { + t.Errorf("unexpected error when creating %q: %v", tc.name, err) + continue + } + + if !reflect.DeepEqual(got, tc.obj) { + t.Errorf("Create(%q) want: %v\ngot: %v", tc.name, tc.obj, got) + } + } +} + +func TestUpdate(t *testing.T) { + tcs := []struct { + resource string + name string + namespace string + obj *unstructured.Unstructured + path string + }{ + { + resource: "rtest", + name: "normal_update", + path: "/apis/gtest/vtest/rtest/normal_update", + obj: getObject("gtest/vTest", "rTest", "normal_update"), + }, + { + resource: "rtest", + name: "namespaced_update", + namespace: "nstest", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update", + obj: getObject("gtest/vTest", "rTest", "namespaced_update"), + }, + { + resource: "rtest/srtest", + name: "normal_subresource_update", + path: "/apis/gtest/vtest/rtest/normal_update/srtest", + obj: getObject("gtest/vTest", "srTest", "normal_update"), + }, + { + resource: "rtest/srtest", + name: "namespaced_subresource_update", + namespace: "nstest", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update/srtest", + obj: getObject("gtest/vTest", "srTest", "namespaced_update"), + }, + } + for _, tc := range tcs { + gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "PUT" { + t.Errorf("Update(%q) got HTTP method %s. wanted PUT", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("Update(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + + w.Header().Set("Content-Type", runtime.ContentTypeJSON) + data, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Update(%q) unexpected error reading body: %v", tc.name, err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Write(data) + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + got, err := cl.Resource(resource, tc.namespace).Update(tc.obj) + if err != nil { + t.Errorf("unexpected error when updating %q: %v", tc.name, err) + continue + } + + if !reflect.DeepEqual(got, tc.obj) { + t.Errorf("Update(%q) want: %v\ngot: %v", tc.name, tc.obj, got) + } + } +} + +func TestWatch(t *testing.T) { + tcs := []struct { + name string + namespace string + events []watch.Event + path string + query string + }{ + { + name: "normal_watch", + path: "/apis/gtest/vtest/rtest", + query: "watch=true", + events: []watch.Event{ + {Type: watch.Added, Object: getObject("gtest/vTest", "rTest", "normal_watch")}, + {Type: watch.Modified, Object: getObject("gtest/vTest", "rTest", "normal_watch")}, + {Type: watch.Deleted, Object: getObject("gtest/vTest", "rTest", "normal_watch")}, + }, + }, + { + name: "namespaced_watch", + namespace: "nstest", + path: "/apis/gtest/vtest/namespaces/nstest/rtest", + query: "watch=true", + events: []watch.Event{ + {Type: watch.Added, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")}, + {Type: watch.Modified, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")}, + {Type: watch.Deleted, Object: getObject("gtest/vTest", "rTest", "namespaced_watch")}, + }, + }, + } + for _, tc := range tcs { + gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + t.Errorf("Watch(%q) got HTTP method %s. wanted GET", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("Watch(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + if r.URL.RawQuery != tc.query { + t.Errorf("Watch(%q) got query %s. wanted %s", tc.name, r.URL.RawQuery, tc.query) + } + + enc := restclientwatch.NewEncoder(streaming.NewEncoder(w, dynamicCodec{}), dynamicCodec{}) + for _, e := range tc.events { + enc.Encode(&e) + } + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + watcher, err := cl.Resource(resource, tc.namespace).Watch(metav1.ListOptions{}) + if err != nil { + t.Errorf("unexpected error when watching %q: %v", tc.name, err) + continue + } + + for _, want := range tc.events { + got := <-watcher.ResultChan() + if !reflect.DeepEqual(got, want) { + t.Errorf("Watch(%q) want: %v\ngot: %v", tc.name, want, got) + } + } + } +} + +func TestPatch(t *testing.T) { + tcs := []struct { + resource string + name string + namespace string + patch []byte + want *unstructured.Unstructured + path string + }{ + { + resource: "rtest", + name: "normal_patch", + path: "/apis/gtest/vtest/rtest/normal_patch", + patch: getJSON("gtest/vTest", "rTest", "normal_patch"), + want: getObject("gtest/vTest", "rTest", "normal_patch"), + }, + { + resource: "rtest", + name: "namespaced_patch", + namespace: "nstest", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_patch", + patch: getJSON("gtest/vTest", "rTest", "namespaced_patch"), + want: getObject("gtest/vTest", "rTest", "namespaced_patch"), + }, + { + resource: "rtest/srtest", + name: "normal_subresource_patch", + path: "/apis/gtest/vtest/rtest/normal_subresource_patch/srtest", + patch: getJSON("gtest/vTest", "srTest", "normal_subresource_patch"), + want: getObject("gtest/vTest", "srTest", "normal_subresource_patch"), + }, + { + resource: "rtest/srtest", + name: "namespaced_subresource_patch", + namespace: "nstest", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_patch/srtest", + patch: getJSON("gtest/vTest", "srTest", "namespaced_subresource_patch"), + want: getObject("gtest/vTest", "srTest", "namespaced_subresource_patch"), + }, + } + for _, tc := range tcs { + gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} + resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} + cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) { + if r.Method != "PATCH" { + t.Errorf("Patch(%q) got HTTP method %s. wanted PATCH", tc.name, r.Method) + } + + if r.URL.Path != tc.path { + t.Errorf("Patch(%q) got path %s. wanted %s", tc.name, r.URL.Path, tc.path) + } + + content := r.Header.Get("Content-Type") + if content != string(types.StrategicMergePatchType) { + t.Errorf("Patch(%q) got Content-Type %s. wanted %s", tc.name, content, types.StrategicMergePatchType) + } + + data, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Patch(%q) unexpected error reading body: %v", tc.name, err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.Write(data) + }) + if err != nil { + t.Errorf("unexpected error when creating client: %v", err) + continue + } + defer srv.Close() + + got, err := cl.Resource(resource, tc.namespace).Patch(tc.name, types.StrategicMergePatchType, tc.patch) + if err != nil { + t.Errorf("unexpected error when patching %q: %v", tc.name, err) + continue + } + + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Patch(%q) want: %v\ngot: %v", tc.name, tc.want, got) + } + } +} diff --git a/staging/src/k8s.io/client-go/dynamic/BUILD b/staging/src/k8s.io/client-go/dynamic/BUILD index ddeb07d3c1b..90b9ab30117 100644 --- a/staging/src/k8s.io/client-go/dynamic/BUILD +++ b/staging/src/k8s.io/client-go/dynamic/BUILD @@ -8,10 +8,7 @@ load( go_test( name = "go_default_test", - srcs = [ - "client_test.go", - "dynamic_util_test.go", - ], + srcs = ["client_test.go"], embed = [":go_default_library"], deps = [ "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -29,10 +26,7 @@ go_test( go_library( name = "go_default_library", srcs = [ - "bad_debt.go", - "client.go", - "client_pool.go", - "dynamic_util.go", + "interface.go", "scheme.go", "simple.go", ], @@ -49,7 +43,6 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/versioning:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", - "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", ], ) diff --git a/staging/src/k8s.io/client-go/dynamic/dynamic_util.go b/staging/src/k8s.io/client-go/dynamic/dynamic_util.go deleted file mode 100644 index 570f9f174d2..00000000000 --- a/staging/src/k8s.io/client-go/dynamic/dynamic_util.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package dynamic - -import ( - "fmt" - - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// NewDiscoveryRESTMapper returns a RESTMapper based on discovery information. -func NewDiscoveryRESTMapper(resources []*metav1.APIResourceList) (*meta.DefaultRESTMapper, error) { - rm := meta.NewDefaultRESTMapper(nil) - for _, resourceList := range resources { - gv, err := schema.ParseGroupVersion(resourceList.GroupVersion) - if err != nil { - return nil, err - } - - for _, resource := range resourceList.APIResources { - gvk := gv.WithKind(resource.Kind) - scope := meta.RESTScopeRoot - if resource.Namespaced { - scope = meta.RESTScopeNamespace - } - rm.Add(gvk, scope) - } - } - return rm, nil -} - -// ObjectTyper provides an ObjectTyper implementation for -// unstructured.Unstructured object based on discovery information. -type ObjectTyper struct { - registered map[schema.GroupVersionKind]bool -} - -// NewObjectTyper constructs an ObjectTyper from discovery information. -func NewObjectTyper(resources []*metav1.APIResourceList) (runtime.ObjectTyper, error) { - ot := &ObjectTyper{registered: make(map[schema.GroupVersionKind]bool)} - for _, resourceList := range resources { - gv, err := schema.ParseGroupVersion(resourceList.GroupVersion) - if err != nil { - return nil, err - } - - for _, resource := range resourceList.APIResources { - ot.registered[gv.WithKind(resource.Kind)] = true - } - } - return ot, nil -} - -// ObjectKinds returns a slice of one element with the -// group,version,kind of the provided object, or an error if the -// object is not *unstructured.Unstructured or has no group,version,kind -// information. -func (ot *ObjectTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) { - if _, ok := obj.(*unstructured.Unstructured); !ok { - return nil, false, fmt.Errorf("type %T is invalid for determining dynamic object types", obj) - } - return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil -} - -// Recognizes returns true if the provided group,version,kind was in -// the discovery information. -func (ot *ObjectTyper) Recognizes(gvk schema.GroupVersionKind) bool { - return ot.registered[gvk] -} diff --git a/staging/src/k8s.io/client-go/dynamic/dynamic_util_test.go b/staging/src/k8s.io/client-go/dynamic/dynamic_util_test.go deleted file mode 100644 index 37113d4a4ed..00000000000 --- a/staging/src/k8s.io/client-go/dynamic/dynamic_util_test.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package dynamic - -import ( - "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -func TestDiscoveryRESTMapper(t *testing.T) { - resources := []*metav1.APIResourceList{ - { - GroupVersion: "test/beta1", - APIResources: []metav1.APIResource{ - { - Name: "test_kinds", - Namespaced: true, - Kind: "test_kind", - }, - }, - }, - } - - gvk := schema.GroupVersionKind{ - Group: "test", - Version: "beta1", - Kind: "test_kind", - } - - mapper, err := NewDiscoveryRESTMapper(resources) - if err != nil { - t.Fatalf("unexpected error creating mapper: %s", err) - } - - for _, res := range []schema.GroupVersionResource{ - { - Group: "test", - Version: "beta1", - Resource: "test_kinds", - }, - { - Version: "beta1", - Resource: "test_kinds", - }, - { - Group: "test", - Resource: "test_kinds", - }, - { - Resource: "test_kinds", - }, - } { - got, err := mapper.KindFor(res) - if err != nil { - t.Errorf("KindFor(%#v) unexpected error: %s", res, err) - continue - } - - if got != gvk { - t.Errorf("KindFor(%#v) = %#v; want %#v", res, got, gvk) - } - } -} diff --git a/staging/src/k8s.io/client-go/dynamic/fake/BUILD b/staging/src/k8s.io/client-go/dynamic/fake/BUILD index fa3b624da45..e4d949f71fb 100644 --- a/staging/src/k8s.io/client-go/dynamic/fake/BUILD +++ b/staging/src/k8s.io/client-go/dynamic/fake/BUILD @@ -7,11 +7,7 @@ load( go_library( name = "go_default_library", - srcs = [ - "client.go", - "client_pool.go", - "simple.go", - ], + srcs = ["simple.go"], importpath = "k8s.io/client-go/dynamic/fake", deps = [ "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", @@ -25,7 +21,6 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library", - "//vendor/k8s.io/client-go/util/flowcontrol:go_default_library", ], ) diff --git a/staging/src/k8s.io/client-go/dynamic/fake/client.go b/staging/src/k8s.io/client-go/dynamic/fake/client.go deleted file mode 100644 index 8399076c20b..00000000000 --- a/staging/src/k8s.io/client-go/dynamic/fake/client.go +++ /dev/null @@ -1,163 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package fake provides a fake client interface to arbitrary Kubernetes -// APIs that exposes common high level operations and exposes common -// metadata. -package fake - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/testing" - "k8s.io/client-go/util/flowcontrol" -) - -// FakeClient is a fake implementation of dynamic.Interface. -type FakeClient struct { - GroupVersion schema.GroupVersion - - *testing.Fake -} - -// GetRateLimiter returns the rate limiter for this client. -func (c *FakeClient) GetRateLimiter() flowcontrol.RateLimiter { - return nil -} - -// Resource returns an API interface to the specified resource for this client's -// group and version. If resource is not a namespaced resource, then namespace -// is ignored. The ResourceClient inherits the parameter codec of this client -func (c *FakeClient) Resource(resource *metav1.APIResource, namespace string) dynamic.ResourceInterface { - return &FakeResourceClient{ - Resource: c.GroupVersion.WithResource(resource.Name), - Kind: c.GroupVersion.WithKind(resource.Kind), - Namespace: namespace, - - Fake: c.Fake, - } -} - -// ParameterCodec returns a client with the provided parameter codec. -func (c *FakeClient) ParameterCodec(parameterCodec runtime.ParameterCodec) dynamic.Interface { - return &FakeClient{ - Fake: c.Fake, - } -} - -// FakeResourceClient is a fake implementation of dynamic.ResourceInterface -type FakeResourceClient struct { - Resource schema.GroupVersionResource - Kind schema.GroupVersionKind - Namespace string - - *testing.Fake -} - -// List returns a list of objects for this resource. -func (c *FakeResourceClient) List(opts metav1.ListOptions) (runtime.Object, error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(c.Resource, c.Kind, c.Namespace, opts), &unstructured.UnstructuredList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &unstructured.UnstructuredList{} - for _, item := range obj.(*unstructured.UnstructuredList).Items { - if label.Matches(labels.Set(item.GetLabels())) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Get gets the resource with the specified name. -func (c *FakeResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(c.Resource, c.Namespace, name), &unstructured.Unstructured{}) - - if obj == nil { - return nil, err - } - - return obj.(*unstructured.Unstructured), err -} - -// Delete deletes the resource with the specified name. -func (c *FakeResourceClient) Delete(name string, opts *metav1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(c.Resource, c.Namespace, name), &unstructured.Unstructured{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeResourceClient) DeleteCollection(deleteOptions *metav1.DeleteOptions, listOptions metav1.ListOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteCollectionAction(c.Resource, c.Namespace, listOptions), &unstructured.Unstructured{}) - - return err -} - -// Create creates the provided resource. -func (c *FakeResourceClient) Create(inObj *unstructured.Unstructured) (*unstructured.Unstructured, error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(c.Resource, c.Namespace, inObj), &unstructured.Unstructured{}) - - if obj == nil { - return nil, err - } - return obj.(*unstructured.Unstructured), err -} - -// Update updates the provided resource. -func (c *FakeResourceClient) Update(inObj *unstructured.Unstructured) (*unstructured.Unstructured, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(c.Resource, c.Namespace, inObj), &unstructured.Unstructured{}) - - if obj == nil { - return nil, err - } - return obj.(*unstructured.Unstructured), err -} - -// Watch returns a watch.Interface that watches the resource. -func (c *FakeResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(c.Resource, c.Namespace, opts)) -} - -// Patch patches the provided resource. -func (c *FakeResourceClient) Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) { - obj, err := c.Fake. - Invokes(testing.NewPatchAction(c.Resource, c.Namespace, name, data), &unstructured.Unstructured{}) - - if obj == nil { - return nil, err - } - return obj.(*unstructured.Unstructured), err -} diff --git a/staging/src/k8s.io/client-go/dynamic/fake/client_pool.go b/staging/src/k8s.io/client-go/dynamic/fake/client_pool.go deleted file mode 100644 index 7ec114892ea..00000000000 --- a/staging/src/k8s.io/client-go/dynamic/fake/client_pool.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package fake provides a fake client interface to arbitrary Kubernetes -// APIs that exposes common high level operations and exposes common -// metadata. -package fake - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/testing" -) - -// FakeClientPool provides a fake implementation of dynamic.ClientPool. -// It assumes resource GroupVersions are the same as their corresponding kind GroupVersions. -type FakeClientPool struct { - testing.Fake -} - -// ClientForGroupVersionKind returns a client configured for the specified groupVersionResource. -// Resource may be empty. -func (p *FakeClientPool) ClientForGroupVersionResource(resource schema.GroupVersionResource) (dynamic.Interface, error) { - return p.ClientForGroupVersionKind(resource.GroupVersion().WithKind("")) -} - -// ClientForGroupVersionKind returns a client configured for the specified groupVersionKind. -// Kind may be empty. -func (p *FakeClientPool) ClientForGroupVersionKind(kind schema.GroupVersionKind) (dynamic.Interface, error) { - // we can just create a new client every time for testing purposes - return &FakeClient{ - GroupVersion: kind.GroupVersion(), - Fake: &p.Fake, - }, nil -} diff --git a/staging/src/k8s.io/client-go/dynamic/interface.go b/staging/src/k8s.io/client-go/dynamic/interface.go new file mode 100644 index 00000000000..4503af6b543 --- /dev/null +++ b/staging/src/k8s.io/client-go/dynamic/interface.go @@ -0,0 +1,34 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dynamic + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// APIPathResolverFunc knows how to convert a groupVersion to its API path. The Kind field is optional. +// TODO find a better place to move this for existing callers +type APIPathResolverFunc func(kind schema.GroupVersionKind) string + +// LegacyAPIPathResolverFunc can resolve paths properly with the legacy API. +// TODO find a better place to move this for existing callers +func LegacyAPIPathResolverFunc(kind schema.GroupVersionKind) string { + if len(kind.Group) == 0 { + return "/api" + } + return "/apis" +} diff --git a/test/integration/client/dynamic_client_test.go b/test/integration/client/dynamic_client_test.go index f7d776512b0..787e1fb7ba4 100644 --- a/test/integration/client/dynamic_client_test.go +++ b/test/integration/client/dynamic_client_test.go @@ -46,29 +46,12 @@ func TestDynamicClient(t *testing.T) { } client := clientset.NewForConfigOrDie(config) - dynamicClient, err := dynamic.NewClient(config, *gv) - _ = dynamicClient + dynamicClient, err := dynamic.NewForConfig(config) if err != nil { t.Fatalf("unexpected error creating dynamic client: %v", err) } - // Find the Pod resource - resources, err := client.Discovery().ServerResourcesForGroupVersion(gv.String()) - if err != nil { - t.Fatalf("unexpected error listing resources: %v", err) - } - - var resource metav1.APIResource - for _, r := range resources.APIResources { - if r.Kind == "Pod" { - resource = r - break - } - } - - if len(resource.Name) == 0 { - t.Fatalf("could not find the pod resource in group/version %q", gv.String()) - } + resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} // Create a Pod with the normal client pod := &v1.Pod{ @@ -91,11 +74,7 @@ func TestDynamicClient(t *testing.T) { } // check dynamic list - obj, err := dynamicClient.Resource(&resource, ns.Name).List(metav1.ListOptions{}) - unstructuredList, ok := obj.(*unstructured.UnstructuredList) - if !ok { - t.Fatalf("expected *unstructured.UnstructuredList, got %#v", obj) - } + unstructuredList, err := dynamicClient.Resource(resource).Namespace(ns.Name).List(metav1.ListOptions{}) if err != nil { t.Fatalf("unexpected error when listing pods: %v", err) } @@ -114,7 +93,7 @@ func TestDynamicClient(t *testing.T) { } // check dynamic get - unstruct, err := dynamicClient.Resource(&resource, ns.Name).Get(actual.Name, metav1.GetOptions{}) + unstruct, err := dynamicClient.Resource(resource).Namespace(ns.Name).Get(actual.Name, metav1.GetOptions{}) if err != nil { t.Fatalf("unexpected error when getting pod %q: %v", actual.Name, err) } @@ -129,7 +108,7 @@ func TestDynamicClient(t *testing.T) { } // delete the pod dynamically - err = dynamicClient.Resource(&resource, ns.Name).Delete(actual.Name, nil) + err = dynamicClient.Resource(resource).Namespace(ns.Name).Delete(actual.Name, nil) if err != nil { t.Fatalf("unexpected error when deleting pod: %v", err) }