diff --git a/cmd/kubeadm/app/util/apiclient/clientbacked_dryrun.go b/cmd/kubeadm/app/util/apiclient/clientbacked_dryrun.go index 73bbde66ef..34b20dc38d 100644 --- a/cmd/kubeadm/app/util/apiclient/clientbacked_dryrun.go +++ b/cmd/kubeadm/app/util/apiclient/clientbacked_dryrun.go @@ -33,7 +33,7 @@ import ( // ClientBackedDryRunGetter implements the DryRunGetter interface for use in NewDryRunClient() and proxies all GET and LIST requests to the backing API server reachable via rest.Config type ClientBackedDryRunGetter struct { client clientset.Interface - dynamicClient dynamic.DynamicInterface + dynamicClient dynamic.Interface } // InitDryRunGetter should implement the DryRunGetter interface diff --git a/hack/.golint_failures b/hack/.golint_failures index 85b97d8988..451b22ebbf 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/garbagecollector.go b/pkg/controller/garbagecollector/garbagecollector.go index 4d05dee56c..7f93386a86 100644 --- a/pkg/controller/garbagecollector/garbagecollector.go +++ b/pkg/controller/garbagecollector/garbagecollector.go @@ -60,7 +60,7 @@ const ResourceResyncTime time.Duration = 0 // up to date as the notification is sent. type GarbageCollector struct { restMapper resettableRESTMapper - dynamicClient dynamic.DynamicInterface + dynamicClient dynamic.Interface // garbage collector attempts to delete the items in attemptToDelete queue when the time is ripe. attemptToDelete workqueue.RateLimitingInterface // garbage collector attempts to orphan the dependents of the items in the attemptToOrphan queue, then deletes the items. @@ -74,7 +74,7 @@ type GarbageCollector struct { } func NewGarbageCollector( - dynamicClient dynamic.DynamicInterface, + dynamicClient dynamic.Interface, mapper resettableRESTMapper, deletableResources map[schema.GroupVersionResource]struct{}, ignoredResources map[schema.GroupResource]struct{}, diff --git a/pkg/controller/garbagecollector/graph_builder.go b/pkg/controller/garbagecollector/graph_builder.go index 1fbb7a1ce7..4e18b1c267 100644 --- a/pkg/controller/garbagecollector/graph_builder.go +++ b/pkg/controller/garbagecollector/graph_builder.go @@ -91,10 +91,7 @@ type GraphBuilder struct { // it is protected by monitorLock. running bool - dynamicClient dynamic.DynamicInterface - // metaOnlyClientPool uses a special codec, which removes fields except for - // apiVersion, kind, and metadata during decoding. - metaOnlyClientPool dynamic.ClientPool + dynamicClient dynamic.Interface // monitors are the producer of the graphChanges queue, graphBuilder alters // the in-memory graph according to the changes. graphChanges workqueue.RateLimitingInterface @@ -128,7 +125,7 @@ func (m *monitor) Run() { type monitors map[schema.GroupVersionResource]*monitor -func listWatcher(client dynamic.DynamicInterface, resource schema.GroupVersionResource) *cache.ListWatch { +func listWatcher(client dynamic.Interface, resource schema.GroupVersionResource) *cache.ListWatch { return &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { // We want to list this resource in all namespaces if it's namespace scoped, so not passing namespace is ok. diff --git a/pkg/controller/namespace/deletion/namespaced_resources_deleter.go b/pkg/controller/namespace/deletion/namespaced_resources_deleter.go index f92cf81f0d..62e09392f4 100644 --- a/pkg/controller/namespace/deletion/namespaced_resources_deleter.go +++ b/pkg/controller/namespace/deletion/namespaced_resources_deleter.go @@ -43,7 +43,7 @@ type NamespacedResourcesDeleterInterface interface { } func NewNamespacedResourcesDeleter(nsClient v1clientset.NamespaceInterface, - dynamicClient dynamic.DynamicInterface, podsGetter v1clientset.PodsGetter, + dynamicClient dynamic.Interface, podsGetter v1clientset.PodsGetter, discoverResourcesFn func() ([]*metav1.APIResourceList, error), finalizerToken v1.FinalizerName, deleteNamespaceWhenDone bool) NamespacedResourcesDeleterInterface { d := &namespacedResourcesDeleter{ @@ -68,7 +68,7 @@ type namespacedResourcesDeleter struct { // Client to manipulate the namespace. nsClient v1clientset.NamespaceInterface // Dynamic client to list and delete all namespaced resources. - dynamicClient dynamic.DynamicInterface + dynamicClient dynamic.Interface // Interface to get PodInterface. podsGetter v1clientset.PodsGetter // Cache of what operations are not supported on each group version resource. diff --git a/pkg/controller/namespace/namespace_controller.go b/pkg/controller/namespace/namespace_controller.go index 3784b7b571..3164d290b9 100644 --- a/pkg/controller/namespace/namespace_controller.go +++ b/pkg/controller/namespace/namespace_controller.go @@ -63,7 +63,7 @@ type NamespaceController struct { // NewNamespaceController creates a new NamespaceController func NewNamespaceController( kubeClient clientset.Interface, - dynamicClient dynamic.DynamicInterface, + dynamicClient dynamic.Interface, discoverResourcesFn func() ([]*metav1.APIResourceList, error), namespaceInformer coreinformers.NamespaceInformer, resyncPeriod time.Duration, diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index b42efe2851..cadfd43353 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -226,7 +226,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/apply.go b/pkg/kubectl/cmd/apply.go index fcdaaf09ab..a28a8ae44c 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -82,7 +82,7 @@ type ApplyOptions struct { Builder *resource.Builder Mapper meta.RESTMapper Scaler scaleclient.ScalesGetter - DynamicClient dynamic.DynamicInterface + DynamicClient dynamic.Interface ClientSetFunc func() (internalclientset.Interface, error) OpenAPISchema openapi.Resources @@ -580,7 +580,7 @@ func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (n type pruner struct { mapper meta.RESTMapper - dynamicClient dynamic.DynamicInterface + dynamicClient dynamic.Interface clientsetFunc func() (internalclientset.Interface, error) visitedUids sets.String @@ -649,7 +649,7 @@ func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping, scale return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.clientsetFunc, scaleClient) } -func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.DynamicInterface, cascade bool, gracePeriod int, clientsetFunc func() (internalclientset.Interface, error), scaleClient scaleclient.ScalesGetter) error { +func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int, clientsetFunc func() (internalclientset.Interface, error), scaleClient scaleclient.ScalesGetter) error { if !cascade { return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, nil) } @@ -681,7 +681,7 @@ func (p *patcher) delete(namespace, name string) error { type patcher struct { mapping *meta.RESTMapping helper *resource.Helper - dynamicClient dynamic.DynamicInterface + dynamicClient dynamic.Interface clientsetFunc func() (internalclientset.Interface, error) overwrite bool diff --git a/pkg/kubectl/cmd/create/BUILD b/pkg/kubectl/cmd/create/BUILD index 898a9374bc..131dc94981 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/genericclioptions/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.go b/pkg/kubectl/cmd/create/create.go index cee2299f4e..6105c9f4b1 100644 --- a/pkg/kubectl/cmd/create/create.go +++ b/pkg/kubectl/cmd/create/create.go @@ -348,7 +348,7 @@ type CreateSubcommandOptions struct { EnforceNamespace bool Mapper meta.RESTMapper - DynamicClient dynamic.DynamicInterface + DynamicClient dynamic.Interface PrintObj func(obj kruntime.Object) error diff --git a/pkg/kubectl/cmd/create/create_test.go b/pkg/kubectl/cmd/create/create_test.go index 0d5a956afa..607a3bfbd3 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/genericclioptions/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 233d0a0091..38ce65a22c 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/expose.go b/pkg/kubectl/cmd/expose.go index 18ae1589fb..8644489621 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -100,7 +100,7 @@ type ExposeServiceOptions struct { Namespace string Mapper meta.RESTMapper - DynamicClient dynamic.DynamicInterface + DynamicClient dynamic.Interface Builder *resource.Builder Recorder genericclioptions.Recorder diff --git a/pkg/kubectl/cmd/get/BUILD b/pkg/kubectl/cmd/get/BUILD index 2f0fcd3c50..db904110bd 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/genericclioptions/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 d63b8bf03c..e094463cdd 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/genericclioptions/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/run.go b/pkg/kubectl/cmd/run.go index 8e512d6cad..28c1b82b8a 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -107,7 +107,7 @@ type RunOptions struct { PrintObj func(runtime.Object) error Recorder genericclioptions.Recorder - DynamicClient dynamic.DynamicInterface + DynamicClient dynamic.Interface ArgsLenAtDash int Attach bool diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index 9dc6b0b153..b304228d91 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -398,7 +398,7 @@ func (f *TestFactory) ClientSet() (internalclientset.Interface, error) { return clientset, nil } -func (f *TestFactory) DynamicClient() (dynamic.DynamicInterface, error) { +func (f *TestFactory) DynamicClient() (dynamic.Interface, error) { if f.FakeDynamicClient != nil { return f.FakeDynamicClient, nil } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 19880c5fb9..96e7aa3f9f 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -85,7 +85,7 @@ type ClientAccessFactory interface { ClientSet() (internalclientset.Interface, error) // DynamicClient returns a dynamic client ready for use - DynamicClient() (dynamic.DynamicInterface, error) + DynamicClient() (dynamic.Interface, error) // KubernetesClientSet gives you back an external clientset KubernetesClientSet() (*kubernetes.Clientset, error) diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index cc281568d8..86e587fb5a 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -114,7 +114,7 @@ func (f *ring0Factory) ClientSet() (internalclientset.Interface, error) { return internalclientset.NewForConfig(clientConfig) } -func (f *ring0Factory) DynamicClient() (dynamic.DynamicInterface, error) { +func (f *ring0Factory) DynamicClient() (dynamic.Interface, error) { clientConfig, err := f.ClientConfig() if err != nil { return nil, err diff --git a/pkg/kubectl/cmd/util/factory_object_mapping.go b/pkg/kubectl/cmd/util/factory_object_mapping.go index 2e7bc0aa8f..0fbd425b90 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) } @@ -146,13 +146,8 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES return nil, err } - clientConfigCopy := *clientConfig - clientConfigCopy.APIPath = dynamic.LegacyAPIPathResolverFunc(mapping.GroupVersionKind) - gv := mapping.GroupVersionKind.GroupVersion() - clientConfigCopy.GroupVersion = &gv - // used to fetch the resource - dynamicClient, err := dynamic.NewClient(&clientConfigCopy, gv) + dynamicClient, err := dynamic.NewForConfig(clientConfig) if err != nil { return nil, err } diff --git a/pkg/kubectl/genericclioptions/resource/BUILD b/pkg/kubectl/genericclioptions/resource/BUILD index a8eda3b99a..e2fb1a8921 100644 --- a/pkg/kubectl/genericclioptions/resource/BUILD +++ b/pkg/kubectl/genericclioptions/resource/BUILD @@ -11,6 +11,7 @@ go_library( "interfaces.go", "mapper.go", "result.go", + "scheme.go", "selector.go", "visitor.go", ], @@ -36,7 +37,7 @@ 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/kubernetes/scheme: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 aa1d232a9a..46380207f3 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/genericclioptions/resource/scheme.go b/pkg/kubectl/genericclioptions/resource/scheme.go new file mode 100644 index 0000000000..fef6edfc1b --- /dev/null +++ b/pkg/kubectl/genericclioptions/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/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index 636374eb53..89bd48a606 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -203,12 +203,7 @@ type genericDescriber struct { } func (g *genericDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (output string, err error) { - apiResource := &metav1.APIResource{ - Name: g.mapping.Resource.Resource, - Namespaced: g.mapping.Scope.Name() == meta.RESTScopeNameNamespace, - Kind: g.mapping.GroupVersionKind.Kind, - } - obj, err := g.dynamic.Resource(apiResource, namespace).Get(name, metav1.GetOptions{}) + obj, err := g.dynamic.Resource(g.mapping.Resource).Namespace(namespace).Get(name, metav1.GetOptions{}) if err != nil { return "", err } diff --git a/staging/BUILD b/staging/BUILD index 15b7d66c27..4e5d5a3efb 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/apiextensions-apiserver/test/integration/basic_test.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/basic_test.go index 4ed097f032..a4a93d8f08 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/basic_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/basic_test.go @@ -80,7 +80,7 @@ func TestClusterScopedCRUD(t *testing.T) { testFieldSelector(t, ns, noxuDefinition, dynamicClient) } -func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.DynamicInterface) { +func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) { noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) initialList, err := noxuResourceClient.List(metav1.ListOptions{}) if err != nil { @@ -202,7 +202,7 @@ func testSimpleCRUD(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta } } -func testFieldSelector(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.DynamicInterface) { +func testFieldSelector(t *testing.T, ns string, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition, dynamicClient dynamic.Interface) { noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) initialList, err := noxuResourceClient.List(metav1.ListOptions{}) if err != nil { @@ -704,7 +704,7 @@ func TestCrossNamespaceListWatch(t *testing.T) { checkNamespacesWatchHelper(t, ns2, noxuNamespacesWatch2) } -func createInstanceWithNamespaceHelper(t *testing.T, ns string, name string, noxuNamespacedResourceClient dynamic.DynamicResourceInterface, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition) *unstructured.Unstructured { +func createInstanceWithNamespaceHelper(t *testing.T, ns string, name string, noxuNamespacedResourceClient dynamic.ResourceInterface, noxuDefinition *apiextensionsv1beta1.CustomResourceDefinition) *unstructured.Unstructured { createdInstance, err := instantiateCustomResource(t, testserver.NewNoxuInstance(ns, name), noxuNamespacedResourceClient, noxuDefinition) if err != nil { t.Fatalf("unable to create noxu Instance:%v", err) diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/registration_test.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/registration_test.go index 9b5aa7bf39..7d0ab684fe 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/registration_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/registration_test.go @@ -41,7 +41,7 @@ import ( "k8s.io/client-go/dynamic" ) -func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.DynamicResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) { +func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) { createdInstance, err := client.Create(instanceToCreate) if err != nil { t.Logf("%#v", createdInstance) @@ -68,7 +68,7 @@ func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unst return createdInstance, nil } -func NewNamespacedCustomResourceClient(ns string, client dynamic.DynamicInterface, crd *apiextensionsv1beta1.CustomResourceDefinition) dynamic.DynamicResourceInterface { +func NewNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd *apiextensionsv1beta1.CustomResourceDefinition) dynamic.ResourceInterface { gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural} if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { @@ -77,24 +77,6 @@ func NewNamespacedCustomResourceClient(ns string, client dynamic.DynamicInterfac return client.Resource(gvr) } -func NewNamespacedCustomResourceStatusClient(ns string, client dynamic.DynamicInterface, crd *apiextensionsv1beta1.CustomResourceDefinition) dynamic.DynamicResourceInterface { - gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural} - - if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { - return client.NamespacedSubresource(gvr, "status", ns) - } - return client.ClusterSubresource(gvr, "status") -} - -func NewNamespacedCustomResourceScaleClient(ns string, client dynamic.DynamicInterface, crd *apiextensionsv1beta1.CustomResourceDefinition) dynamic.DynamicResourceInterface { - gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural} - - if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { - return client.NamespacedSubresource(gvr, "scale", ns) - } - return client.ClusterSubresource(gvr, "scale") -} - func TestMultipleResourceInstances(t *testing.T) { stopCh, apiExtensionClient, dynamicClient, err := testserver.StartDefaultServerWithClients() if err != nil { diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/subresources_test.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/subresources_test.go index 785ac4da53..1250492959 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/subresources_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/subresources_test.go @@ -105,7 +105,6 @@ func TestStatusSubresource(t *testing.T) { ns := "not-the-default" noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) - noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition) _, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition) if err != nil { t.Fatalf("unable to create noxu instance: %v", err) @@ -135,7 +134,7 @@ func TestStatusSubresource(t *testing.T) { // UpdateStatus should not update spec. // Check that .spec.num = 10 and .status.num = 20 - updatedStatusInstance, err := noxuStatusResourceClient.Update(gottenNoxuInstance) + updatedStatusInstance, err := noxuResourceClient.UpdateStatus(gottenNoxuInstance) if err != nil { t.Fatalf("unable to update status: %v", err) } @@ -238,7 +237,6 @@ func TestScaleSubresource(t *testing.T) { ns := "not-the-default" noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) - noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition) _, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition) if err != nil { t.Fatalf("unable to create noxu instance: %v", err) @@ -258,7 +256,7 @@ func TestScaleSubresource(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - _, err = noxuStatusResourceClient.Update(gottenNoxuInstance) + _, err = noxuResourceClient.UpdateStatus(gottenNoxuInstance) if err != nil { t.Fatalf("unable to update status: %v", err) } @@ -430,7 +428,6 @@ func TestValidateOnlyStatus(t *testing.T) { } ns := "not-the-default" noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) - noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition) // set .spec.num = 10 and .status.num = 10 noxuInstance := NewNoxuSubresourceInstance(ns, "foo") @@ -449,7 +446,7 @@ func TestValidateOnlyStatus(t *testing.T) { if err != nil { t.Fatalf("unexpected error setting .spec.num: %v", err) } - createdNoxuInstance, err = noxuStatusResourceClient.Update(createdNoxuInstance) + createdNoxuInstance, err = noxuResourceClient.UpdateStatus(createdNoxuInstance) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -459,7 +456,7 @@ func TestValidateOnlyStatus(t *testing.T) { if err != nil { t.Fatalf("unexpected error setting .status.num: %v", err) } - createdNoxuInstance, err = noxuStatusResourceClient.Update(createdNoxuInstance) + createdNoxuInstance, err = noxuResourceClient.UpdateStatus(createdNoxuInstance) if err == nil { t.Fatal("expected error, but got none") } @@ -577,7 +574,6 @@ func TestGeneration(t *testing.T) { ns := "not-the-default" noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) - noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition) _, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition) if err != nil { t.Fatalf("unable to create noxu instance: %v", err) @@ -599,7 +595,7 @@ func TestGeneration(t *testing.T) { } // UpdateStatus does not increment generation - updatedStatusInstance, err := noxuStatusResourceClient.Update(gottenNoxuInstance) + updatedStatusInstance, err := noxuResourceClient.UpdateStatus(gottenNoxuInstance) if err != nil { t.Fatalf("unable to update status: %v", err) } @@ -660,8 +656,6 @@ func TestSubresourcePatch(t *testing.T) { ns := "not-the-default" noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) - noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition) - noxuScaleResourceClient := NewNamespacedCustomResourceScaleClient(ns, dynamicClient, noxuDefinition) _, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition) if err != nil { t.Fatalf("unable to create noxu instance: %v", err) @@ -673,7 +667,7 @@ func TestSubresourcePatch(t *testing.T) { } patch := []byte(`{"spec": {"num":999}, "status": {"num":999}}`) - patchedNoxuInstance, err := noxuStatusResourceClient.Patch("foo", types.MergePatchType, patch) + patchedNoxuInstance, err := noxuResourceClient.Patch("foo", types.MergePatchType, patch, "status") if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -708,19 +702,19 @@ func TestSubresourcePatch(t *testing.T) { } // no-op patch - _, err = noxuStatusResourceClient.Patch("foo", types.MergePatchType, patch) + _, err = noxuResourceClient.Patch("foo", types.MergePatchType, patch, "status") if err != nil { t.Fatalf("unexpected error: %v", err) } // empty patch - _, err = noxuStatusResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`)) + _, err = noxuResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`), "status") if err != nil { t.Fatalf("unexpected error: %v", err) } patch = []byte(`{"spec": {"replicas":7}, "status": {"replicas":7}}`) - patchedNoxuInstance, err = noxuScaleResourceClient.Patch("foo", types.MergePatchType, patch) + patchedNoxuInstance, err = noxuResourceClient.Patch("foo", types.MergePatchType, patch, "scale") if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -749,24 +743,24 @@ func TestSubresourcePatch(t *testing.T) { } // no-op patch - _, err = noxuScaleResourceClient.Patch("foo", types.MergePatchType, patch) + _, err = noxuResourceClient.Patch("foo", types.MergePatchType, patch, "scale") if err != nil { t.Fatalf("unexpected error: %v", err) } // empty patch - _, err = noxuScaleResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`)) + _, err = noxuResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`), "scale") if err != nil { t.Fatalf("unexpected error: %v", err) } // make sure strategic merge patch is not supported for both status and scale - _, err = noxuStatusResourceClient.Patch("foo", types.StrategicMergePatchType, patch) + _, err = noxuResourceClient.Patch("foo", types.StrategicMergePatchType, patch, "status") if err == nil { t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources") } - _, err = noxuScaleResourceClient.Patch("foo", types.StrategicMergePatchType, patch) + _, err = noxuResourceClient.Patch("foo", types.StrategicMergePatchType, patch, "scale") if err == nil { t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources") } diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go index 1f4a952b33..2502b9a900 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go @@ -178,7 +178,7 @@ func CreateNewCustomResourceDefinitionWatchUnsafe(crd *apiextensionsv1beta1.Cust return err } -func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, dynamicClientSet dynamic.DynamicInterface) error { +func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface, dynamicClientSet dynamic.Interface) error { err := CreateNewCustomResourceDefinitionWatchUnsafe(crd, apiExtensionsClient) if err != nil { return err @@ -209,14 +209,14 @@ func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceD return nil } -func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClientSet dynamic.DynamicInterface) error { +func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClientSet dynamic.Interface) error { ns := "" if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { ns = "aval" } gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural} - var resourceClient dynamic.DynamicResourceInterface + var resourceClient dynamic.ResourceInterface if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { resourceClient = dynamicClientSet.Resource(gvr).Namespace(ns) } else { diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/start.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/start.go index cac4a9d066..22b9fa2862 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/start.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/start.go @@ -139,7 +139,7 @@ func StartDefaultServer() (chan struct{}, *rest.Config, error) { return StartServer(config) } -func StartDefaultServerWithClients() (chan struct{}, clientset.Interface, dynamic.DynamicInterface, error) { +func StartDefaultServerWithClients() (chan struct{}, clientset.Interface, dynamic.Interface, error) { stopCh, config, err := StartDefaultServer() if err != nil { return nil, nil, nil, err 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 0000000000..530e34285d --- /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 8492d56afc..51e4a5830e 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 43db68c41f..0974fe64de 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.Interface } // 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,30 +85,47 @@ 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.ResourceInterface, subresources []string) ResourceInterface { + return oldResourceShimType{ResourceInterface: in, subresources: subresources} } type oldResourceShimType struct { - DynamicResourceInterface + dynamic.ResourceInterface + subresources []string +} + +func (s oldResourceShimType) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return s.ResourceInterface.Create(obj, s.subresources...) +} + +func (s oldResourceShimType) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + return s.ResourceInterface.Update(obj, s.subresources...) +} + +func (s oldResourceShimType) Delete(name string, opts *metav1.DeleteOptions) error { + return s.ResourceInterface.Delete(name, opts, s.subresources...) +} + +func (s oldResourceShimType) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { + return s.ResourceInterface.Get(name, opts, s.subresources...) } func (s oldResourceShimType) List(opts metav1.ListOptions) (runtime.Object, error) { - return s.DynamicResourceInterface.List(opts) + return s.ResourceInterface.List(opts) } func (s oldResourceShimType) Patch(name string, pt types.PatchType, data []byte) (*unstructured.Unstructured, error) { - return s.DynamicResourceInterface.Patch(name, pt, data) + return s.ResourceInterface.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 f4d258be8a..36dc54ce48 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 0000000000..790474522a --- /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 ddeb07d3c1..90b9ab3011 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/client_test.go b/staging/src/k8s.io/client-go/dynamic/client_test.go index 3cc6e80c89..e8fe938676 100644 --- a/staging/src/k8s.io/client-go/dynamic/client_test.go +++ b/staging/src/k8s.io/client-go/dynamic/client_test.go @@ -58,12 +58,11 @@ func getObject(version, kind, name string) *unstructured.Unstructured { } } -func getClientServer(gv *schema.GroupVersion, h func(http.ResponseWriter, *http.Request)) (Interface, *httptest.Server, error) { +func getClientServer(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) + cl, err := NewForConfig(&restclient.Config{ + Host: srv.URL, + }) if err != nil { srv.Close() return nil, nil, err @@ -116,9 +115,8 @@ func TestList(t *testing.T) { }, } 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) { + resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"} + cl, srv, err := getClientServer(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) } @@ -136,7 +134,7 @@ func TestList(t *testing.T) { } defer srv.Close() - got, err := cl.Resource(resource, tc.namespace).List(metav1.ListOptions{}) + got, err := cl.Resource(resource).Namespace(tc.namespace).List(metav1.ListOptions{}) if err != nil { t.Errorf("unexpected error when listing %q: %v", tc.name, err) continue @@ -150,12 +148,13 @@ func TestList(t *testing.T) { func TestGet(t *testing.T) { tcs := []struct { - resource string - namespace string - name string - path string - resp []byte - want *unstructured.Unstructured + resource string + subresource []string + namespace string + name string + path string + resp []byte + want *unstructured.Unstructured }{ { resource: "rtest", @@ -173,25 +172,26 @@ func TestGet(t *testing.T) { 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", + subresource: []string{"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"), + resource: "rtest", + subresource: []string{"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) { + resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource} + cl, srv, err := getClientServer(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) } @@ -209,7 +209,7 @@ func TestGet(t *testing.T) { } defer srv.Close() - got, err := cl.Resource(resource, tc.namespace).Get(tc.name, metav1.GetOptions{}) + got, err := cl.Resource(resource).Namespace(tc.namespace).Get(tc.name, metav1.GetOptions{}, tc.subresource...) if err != nil { t.Errorf("unexpected error when getting %q: %v", tc.name, err) continue @@ -230,6 +230,7 @@ func TestDelete(t *testing.T) { Status: metav1.StatusSuccess, } tcs := []struct { + subresource []string namespace string name string path string @@ -244,6 +245,17 @@ func TestDelete(t *testing.T) { name: "namespaced_delete", path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete", }, + { + subresource: []string{"srtest"}, + name: "normal_delete", + path: "/apis/gtest/vtest/rtest/normal_delete/srtest", + }, + { + subresource: []string{"srtest"}, + namespace: "nstest", + name: "namespaced_delete", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_delete/srtest", + }, { namespace: "nstest", name: "namespaced_delete_with_options", @@ -252,9 +264,8 @@ func TestDelete(t *testing.T) { }, } 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) { + resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"} + cl, srv, err := getClientServer(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) } @@ -272,7 +283,7 @@ func TestDelete(t *testing.T) { } defer srv.Close() - err = cl.Resource(resource, tc.namespace).Delete(tc.name, tc.deleteOptions) + err = cl.Resource(resource).Namespace(tc.namespace).Delete(tc.name, tc.deleteOptions, tc.subresource...) if err != nil { t.Errorf("unexpected error when deleting %q: %v", tc.name, err) continue @@ -301,9 +312,8 @@ func TestDeleteCollection(t *testing.T) { }, } 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) { + resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"} + cl, srv, err := getClientServer(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) } @@ -321,7 +331,7 @@ func TestDeleteCollection(t *testing.T) { } defer srv.Close() - err = cl.Resource(resource, tc.namespace).DeleteCollection(nil, metav1.ListOptions{}) + err = cl.Resource(resource).Namespace(tc.namespace).DeleteCollection(nil, metav1.ListOptions{}) if err != nil { t.Errorf("unexpected error when deleting collection %q: %v", tc.name, err) continue @@ -331,11 +341,12 @@ func TestDeleteCollection(t *testing.T) { func TestCreate(t *testing.T) { tcs := []struct { - resource string - name string - namespace string - obj *unstructured.Unstructured - path string + resource string + subresource []string + name string + namespace string + obj *unstructured.Unstructured + path string }{ { resource: "rtest", @@ -350,11 +361,25 @@ func TestCreate(t *testing.T) { path: "/apis/gtest/vtest/namespaces/nstest/rtest", obj: getObject("gtest/vTest", "rTest", "namespaced_create"), }, + { + resource: "rtest", + subresource: []string{"srtest"}, + name: "normal_subresource_create", + path: "/apis/gtest/vtest/rtest/normal_subresource_create/srtest", + obj: getObject("vTest", "srTest", "normal_subresource_create"), + }, + { + resource: "rtest/", + subresource: []string{"srtest"}, + name: "namespaced_subresource_create", + namespace: "nstest", + path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_create/srtest", + obj: getObject("vTest", "srTest", "namespaced_subresource_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) { + resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource} + cl, srv, err := getClientServer(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) } @@ -379,7 +404,7 @@ func TestCreate(t *testing.T) { } defer srv.Close() - got, err := cl.Resource(resource, tc.namespace).Create(tc.obj) + got, err := cl.Resource(resource).Namespace(tc.namespace).Create(tc.obj, tc.subresource...) if err != nil { t.Errorf("unexpected error when creating %q: %v", tc.name, err) continue @@ -393,11 +418,12 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { tcs := []struct { - resource string - name string - namespace string - obj *unstructured.Unstructured - path string + resource string + subresource []string + name string + namespace string + obj *unstructured.Unstructured + path string }{ { resource: "rtest", @@ -413,23 +439,24 @@ func TestUpdate(t *testing.T) { 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", + subresource: []string{"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"), + resource: "rtest", + subresource: []string{"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) { + resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource} + cl, srv, err := getClientServer(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) } @@ -454,7 +481,7 @@ func TestUpdate(t *testing.T) { } defer srv.Close() - got, err := cl.Resource(resource, tc.namespace).Update(tc.obj) + got, err := cl.Resource(resource).Namespace(tc.namespace).Update(tc.obj, tc.subresource...) if err != nil { t.Errorf("unexpected error when updating %q: %v", tc.name, err) continue @@ -497,9 +524,8 @@ func TestWatch(t *testing.T) { }, } 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) { + resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"} + cl, srv, err := getClientServer(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) } @@ -511,7 +537,7 @@ func TestWatch(t *testing.T) { t.Errorf("Watch(%q) got query %s. wanted %s", tc.name, r.URL.RawQuery, tc.query) } - enc := restclientwatch.NewEncoder(streaming.NewEncoder(w, dynamicCodec{}), dynamicCodec{}) + enc := restclientwatch.NewEncoder(streaming.NewEncoder(w, unstructured.UnstructuredJSONScheme), unstructured.UnstructuredJSONScheme) for _, e := range tc.events { enc.Encode(&e) } @@ -522,7 +548,7 @@ func TestWatch(t *testing.T) { } defer srv.Close() - watcher, err := cl.Resource(resource, tc.namespace).Watch(metav1.ListOptions{}) + watcher, err := cl.Resource(resource).Namespace(tc.namespace).Watch(metav1.ListOptions{}) if err != nil { t.Errorf("unexpected error when watching %q: %v", tc.name, err) continue @@ -539,12 +565,13 @@ func TestWatch(t *testing.T) { func TestPatch(t *testing.T) { tcs := []struct { - resource string - name string - namespace string - patch []byte - want *unstructured.Unstructured - path string + resource string + subresource []string + name string + namespace string + patch []byte + want *unstructured.Unstructured + path string }{ { resource: "rtest", @@ -562,25 +589,26 @@ func TestPatch(t *testing.T) { 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", + subresource: []string{"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"), + resource: "rtest", + subresource: []string{"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) { + resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource} + cl, srv, err := getClientServer(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) } @@ -610,7 +638,7 @@ func TestPatch(t *testing.T) { } defer srv.Close() - got, err := cl.Resource(resource, tc.namespace).Patch(tc.name, types.StrategicMergePatchType, tc.patch) + got, err := cl.Resource(resource).Namespace(tc.namespace).Patch(tc.name, types.StrategicMergePatchType, tc.patch, tc.subresource...) if err != nil { t.Errorf("unexpected error when patching %q: %v", tc.name, err) continue 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 570f9f174d..0000000000 --- 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 37113d4a4e..0000000000 --- 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 fa3b624da4..e4d949f71f 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 8399076c20..0000000000 --- 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 7ec114892e..0000000000 --- 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/fake/simple.go b/staging/src/k8s.io/client-go/dynamic/fake/simple.go index 9aafb81a45..a71cec50ea 100644 --- a/staging/src/k8s.io/client-go/dynamic/fake/simple.go +++ b/staging/src/k8s.io/client-go/dynamic/fake/simple.go @@ -17,6 +17,8 @@ limitations under the License. package fake import ( + "strings" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -63,37 +65,54 @@ type FakeDynamicClient struct { } type dynamicResourceClient struct { - client *FakeDynamicClient - namespace string - resource schema.GroupVersionResource - subresource string + client *FakeDynamicClient + namespace string + resource schema.GroupVersionResource } -var _ dynamic.DynamicInterface = &FakeDynamicClient{} +var _ dynamic.Interface = &FakeDynamicClient{} -func (c *FakeDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableDynamicResourceInterface { +func (c *FakeDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface { return &dynamicResourceClient{client: c, resource: resource} } -// Deprecated, this isn't how we want to do it -func (c *FakeDynamicClient) ClusterSubresource(resource schema.GroupVersionResource, subresource string) dynamic.DynamicResourceInterface { - return &dynamicResourceClient{client: c, resource: resource, subresource: subresource} -} - -// Deprecated, this isn't how we want to do it -func (c *FakeDynamicClient) NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) dynamic.DynamicResourceInterface { - return &dynamicResourceClient{client: c, resource: resource, namespace: namespace, subresource: subresource} -} - -func (c *dynamicResourceClient) Namespace(ns string) dynamic.DynamicResourceInterface { +func (c *dynamicResourceClient) Namespace(ns string) dynamic.ResourceInterface { ret := *c ret.namespace = ns return &ret } -func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { - uncastRet, err := c.client.Fake. - Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj) +func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) { + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootCreateAction(c.resource, obj), obj) + + case len(c.namespace) == 0 && len(subresources) > 0: + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + name := accessor.GetName() + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), obj), obj) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj) + + case len(c.namespace) > 0 && len(subresources) > 0: + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + name := accessor.GetName() + uncastRet, err = c.client.Fake. + Invokes(testing.NewCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), c.namespace, obj), obj) + + } if err != nil { return nil, err @@ -109,9 +128,27 @@ func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstruc return ret, err } -func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { - uncastRet, err := c.client.Fake. - Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj) +func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) { + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootUpdateAction(c.resource, obj), obj) + + case len(c.namespace) == 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), obj), obj) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj) + + case len(c.namespace) > 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, obj), obj) + + } if err != nil { return nil, err @@ -128,8 +165,18 @@ func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured) (*unstruc } func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { - uncastRet, err := c.client.Fake. - Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj) + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(c.resource, "status", obj), obj) + + case len(c.namespace) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj) + + } if err != nil { return nil, err @@ -145,23 +192,65 @@ func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*u return ret, err } -func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions) error { - _, err := c.client.Fake. - Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic delete fail"}) +func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions, subresources ...string) error { + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + _, err = c.client.Fake. + Invokes(testing.NewRootDeleteAction(c.resource, name), &metav1.Status{Status: "dynamic delete fail"}) + + case len(c.namespace) == 0 && len(subresources) > 0: + _, err = c.client.Fake. + Invokes(testing.NewRootDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic delete fail"}) + + case len(c.namespace) > 0 && len(subresources) == 0: + _, err = c.client.Fake. + Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic delete fail"}) + + case len(c.namespace) > 0 && len(subresources) > 0: + _, err = c.client.Fake. + Invokes(testing.NewDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, name), &metav1.Status{Status: "dynamic delete fail"}) + } return err } func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error { - action := testing.NewDeleteCollectionAction(c.resource, c.namespace, listOptions) + var err error + switch { + case len(c.namespace) == 0: + action := testing.NewRootDeleteCollectionAction(c.resource, listOptions) + _, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"}) + + case len(c.namespace) > 0: + action := testing.NewDeleteCollectionAction(c.resource, c.namespace, listOptions) + _, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"}) + + } - _, err := c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"}) return err } -func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { - uncastRet, err := c.client.Fake. - Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic get fail"}) +func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) { + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootGetAction(c.resource, name), &metav1.Status{Status: "dynamic get fail"}) + + case len(c.namespace) == 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootGetSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"}) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic get fail"}) + + case len(c.namespace) > 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewGetSubresourceAction(c.resource, c.namespace, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"}) + } if err != nil { return nil, err @@ -178,8 +267,18 @@ func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstr } func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) { - obj, err := c.client.Fake. - Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Version: "v1", Kind: "List"}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"}) + var obj runtime.Object + var err error + switch { + case len(c.namespace) == 0: + obj, err = c.client.Fake. + Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Version: "v1", Kind: "List"}, opts), &metav1.Status{Status: "dynamic list fail"}) + + case len(c.namespace) > 0: + obj, err = c.client.Fake. + Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Version: "v1", Kind: "List"}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"}) + + } if obj == nil { return nil, err @@ -213,13 +312,41 @@ func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.Uns } func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) { - return c.client.Fake. - InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts)) + switch { + case len(c.namespace) == 0: + return c.client.Fake. + InvokesWatch(testing.NewRootWatchAction(c.resource, opts)) + + case len(c.namespace) > 0: + return c.client.Fake. + InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts)) + + } + + panic("math broke") } func (c *dynamicResourceClient) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error) { - uncastRet, err := c.client.Fake. - Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, data, subresources...), &metav1.Status{Status: "dynamic patch fail"}) + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootPatchAction(c.resource, name, data), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) == 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, data, subresources...), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewPatchAction(c.resource, c.namespace, name, data), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) > 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, data, subresources...), &metav1.Status{Status: "dynamic patch fail"}) + + } if err != nil { return nil, err 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 0000000000..3f364f872a --- /dev/null +++ b/staging/src/k8s.io/client-go/dynamic/interface.go @@ -0,0 +1,59 @@ +/* +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 ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" +) + +type Interface interface { + Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface +} + +type ResourceInterface interface { + Create(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) + Update(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) + UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) + Delete(name string, options *metav1.DeleteOptions, subresources ...string) error + DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error + Get(name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) + List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error) +} + +type NamespaceableResourceInterface interface { + Namespace(string) ResourceInterface + ResourceInterface +} + +// 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/staging/src/k8s.io/client-go/dynamic/simple.go b/staging/src/k8s.io/client-go/dynamic/simple.go index 396ff8766d..88e9cc2b06 100644 --- a/staging/src/k8s.io/client-go/dynamic/simple.go +++ b/staging/src/k8s.io/client-go/dynamic/simple.go @@ -17,7 +17,6 @@ limitations under the License. package dynamic import ( - "fmt" "io" "k8s.io/apimachinery/pkg/api/meta" @@ -31,39 +30,13 @@ import ( "k8s.io/client-go/rest" ) -type DynamicInterface interface { - Resource(resource schema.GroupVersionResource) NamespaceableDynamicResourceInterface - - // Deprecated, this isn't how we want to do it - ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface - // Deprecated, this isn't how we want to do it - NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) DynamicResourceInterface -} - -type DynamicResourceInterface interface { - Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) - Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) - UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) - Delete(name string, options *metav1.DeleteOptions) error - DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error - Get(name string, options metav1.GetOptions) (*unstructured.Unstructured, error) - List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) - Watch(opts metav1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error) -} - -type NamespaceableDynamicResourceInterface interface { - Namespace(string) DynamicResourceInterface - DynamicResourceInterface -} - type dynamicClient struct { client *rest.RESTClient } -var _ DynamicInterface = &dynamicClient{} +var _ Interface = &dynamicClient{} -func NewForConfig(inConfig *rest.Config) (DynamicInterface, error) { +func NewForConfig(inConfig *rest.Config) (Interface, error) { config := rest.CopyConfig(inConfig) // for serializing the options config.GroupVersion = &schema.GroupVersion{} @@ -84,40 +57,36 @@ func NewForConfig(inConfig *rest.Config) (DynamicInterface, error) { } type dynamicResourceClient struct { - client *dynamicClient - namespace string - resource schema.GroupVersionResource - subresource string + client *dynamicClient + namespace string + resource schema.GroupVersionResource } -func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableDynamicResourceInterface { +func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface { return &dynamicResourceClient{client: c, resource: resource} } -func (c *dynamicClient) ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface { - return &dynamicResourceClient{client: c, resource: resource, subresource: subresource} -} -func (c *dynamicClient) NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) DynamicResourceInterface { - return &dynamicResourceClient{client: c, resource: resource, namespace: namespace, subresource: subresource} -} - -func (c *dynamicResourceClient) Namespace(ns string) DynamicResourceInterface { +func (c *dynamicResourceClient) Namespace(ns string) ResourceInterface { ret := *c ret.namespace = ns return &ret } -func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { - if len(c.subresource) > 0 { - return nil, fmt.Errorf("create not supported for subresources") - } - +func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) { outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj) if err != nil { return nil, err } + name := "" + if len(subresources) > 0 { + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + name = accessor.GetName() + } - result := c.client.client.Post().AbsPath(c.makeURLSegments("")...).Body(outBytes).Do() + result := c.client.client.Post().AbsPath(append(c.makeURLSegments(name), subresources...)...).Body(outBytes).Do() if err := result.Error(); err != nil { return nil, err } @@ -133,7 +102,7 @@ func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstruc return uncastObj.(*unstructured.Unstructured), nil } -func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { +func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) { accessor, err := meta.Accessor(obj) if err != nil { return nil, err @@ -143,7 +112,7 @@ func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured) (*unstruc return nil, err } - result := c.client.client.Put().AbsPath(c.makeURLSegments(accessor.GetName())...).Body(outBytes).Do() + result := c.client.client.Put().AbsPath(append(c.makeURLSegments(accessor.GetName()), subresources...)...).Body(outBytes).Do() if err := result.Error(); err != nil { return nil, err } @@ -171,6 +140,10 @@ func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*u } result := c.client.client.Put().AbsPath(append(c.makeURLSegments(accessor.GetName()), "status")...).Body(outBytes).Do() + if err := result.Error(); err != nil { + return nil, err + } + retBytes, err := result.Raw() if err != nil { return nil, err @@ -182,7 +155,7 @@ func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*u return uncastObj.(*unstructured.Unstructured), nil } -func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions) error { +func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions, subresources ...string) error { if opts == nil { opts = &metav1.DeleteOptions{} } @@ -191,15 +164,11 @@ func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions) return err } - result := c.client.client.Delete().AbsPath(c.makeURLSegments(name)...).Body(deleteOptionsByte).Do() + result := c.client.client.Delete().AbsPath(append(c.makeURLSegments(name), subresources...)...).Body(deleteOptionsByte).Do() return result.Error() } func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error { - if len(c.subresource) > 0 { - return fmt.Errorf("deletecollection not supported for subresources") - } - if opts == nil { opts = &metav1.DeleteOptions{} } @@ -212,8 +181,8 @@ func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, lis return result.Error() } -func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { - result := c.client.client.Get().AbsPath(c.makeURLSegments(name)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do() +func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) { + result := c.client.client.Get().AbsPath(append(c.makeURLSegments(name), subresources...)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do() if err := result.Error(); err != nil { return nil, err } @@ -229,10 +198,6 @@ func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstr } func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) { - if len(c.subresource) > 0 { - return nil, fmt.Errorf("list not supported for subresources") - } - result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do() if err := result.Error(); err != nil { return nil, err @@ -257,10 +222,6 @@ func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.Uns } func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) { - if len(c.subresource) > 0 { - return nil, fmt.Errorf("watch not supported for subresources") - } - internalGV := schema.GroupVersions{ {Group: c.resource.Group, Version: runtime.APIVersionInternal}, // always include the legacy group as a decoding target to handle non-error `Status` return types @@ -320,15 +281,6 @@ func (c *dynamicResourceClient) makeURLSegments(name string) []string { if len(name) > 0 { url = append(url, name) - - // subresources only work on things with names - if len(c.subresource) > 0 { - url = append(url, c.subresource) - } - } else { - if len(c.subresource) > 0 { - panic("somehow snuck a subresource and an empty name. programmer error") - } } return url diff --git a/staging/src/k8s.io/client-go/testing/actions.go b/staging/src/k8s.io/client-go/testing/actions.go index 232cf21e05..b99f231c8d 100644 --- a/staging/src/k8s.io/client-go/testing/actions.go +++ b/staging/src/k8s.io/client-go/testing/actions.go @@ -225,6 +225,16 @@ func NewRootDeleteAction(resource schema.GroupVersionResource, name string) Dele return action } +func NewRootDeleteSubresourceAction(resource schema.GroupVersionResource, subresource string, name string) DeleteActionImpl { + action := DeleteActionImpl{} + action.Verb = "delete" + action.Resource = resource + action.Subresource = subresource + action.Name = name + + return action +} + func NewDeleteAction(resource schema.GroupVersionResource, namespace, name string) DeleteActionImpl { action := DeleteActionImpl{} action.Verb = "delete" @@ -235,6 +245,17 @@ func NewDeleteAction(resource schema.GroupVersionResource, namespace, name strin return action } +func NewDeleteSubresourceAction(resource schema.GroupVersionResource, subresource, namespace, name string) DeleteActionImpl { + action := DeleteActionImpl{} + action.Verb = "delete" + action.Resource = resource + action.Subresource = subresource + action.Namespace = namespace + action.Name = name + + return action +} + func NewRootDeleteCollectionAction(resource schema.GroupVersionResource, opts interface{}) DeleteCollectionActionImpl { action := DeleteCollectionActionImpl{} action.Verb = "delete-collection" diff --git a/test/e2e/apimachinery/crd_watch.go b/test/e2e/apimachinery/crd_watch.go index 6c38a50ae8..a101f54925 100644 --- a/test/e2e/apimachinery/crd_watch.go +++ b/test/e2e/apimachinery/crd_watch.go @@ -115,7 +115,7 @@ var _ = SIGDescribe("CustomResourceDefinition Watch", func() { }) }) -func watchCRWithName(crdResourceClient dynamic.DynamicResourceInterface, name string) (watch.Interface, error) { +func watchCRWithName(crdResourceClient dynamic.ResourceInterface, name string) (watch.Interface, error) { return crdResourceClient.Watch( metav1.ListOptions{ FieldSelector: "metadata.name=" + name, @@ -124,7 +124,7 @@ func watchCRWithName(crdResourceClient dynamic.DynamicResourceInterface, name st ) } -func instantiateCustomResource(instanceToCreate *unstructured.Unstructured, client dynamic.DynamicResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) { +func instantiateCustomResource(instanceToCreate *unstructured.Unstructured, client dynamic.ResourceInterface, definition *apiextensionsv1beta1.CustomResourceDefinition) (*unstructured.Unstructured, error) { createdInstance, err := client.Create(instanceToCreate) if err != nil { return nil, err @@ -150,11 +150,11 @@ func instantiateCustomResource(instanceToCreate *unstructured.Unstructured, clie return createdInstance, nil } -func deleteCustomResource(client dynamic.DynamicResourceInterface, name string) error { +func deleteCustomResource(client dynamic.ResourceInterface, name string) error { return client.Delete(name, &metav1.DeleteOptions{}) } -func newNamespacedCustomResourceClient(ns string, client dynamic.DynamicInterface, crd *apiextensionsv1beta1.CustomResourceDefinition) dynamic.DynamicResourceInterface { +func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd *apiextensionsv1beta1.CustomResourceDefinition) dynamic.ResourceInterface { gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural} if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { diff --git a/test/e2e/apimachinery/webhook.go b/test/e2e/apimachinery/webhook.go index 248f6fb546..5dbda0239c 100644 --- a/test/e2e/apimachinery/webhook.go +++ b/test/e2e/apimachinery/webhook.go @@ -1081,7 +1081,7 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, context *c return func() { client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(configName, nil) } } -func testCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, customResourceClient dynamic.DynamicResourceInterface) { +func testCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, customResourceClient dynamic.ResourceInterface) { By("Creating a custom resource that should be denied by the webhook") crInstance := &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -1104,7 +1104,7 @@ func testCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1 } } -func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, customResourceClient dynamic.DynamicResourceInterface) { +func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, customResourceClient dynamic.ResourceInterface) { By("Creating a custom resource that should be mutated by the webhook") cr := &unstructured.Unstructured{ Object: map[string]interface{}{ diff --git a/test/e2e/framework/crd_util.go b/test/e2e/framework/crd_util.go index 58cbdcc976..4a4acc8cb7 100644 --- a/test/e2e/framework/crd_util.go +++ b/test/e2e/framework/crd_util.go @@ -38,7 +38,7 @@ type TestCrd struct { ApiVersion string ApiExtensionClient *crdclientset.Clientset Crd *apiextensionsv1beta1.CustomResourceDefinition - DynamicClient dynamic.DynamicResourceInterface + DynamicClient dynamic.ResourceInterface CleanUp CleanCrdFn } diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index dcf04ec2a8..d9a23c5332 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -70,7 +70,7 @@ type Framework struct { InternalClientset *internalclientset.Clientset AggregatorClient *aggregatorclient.Clientset - DynamicClient dynamic.DynamicInterface + DynamicClient dynamic.Interface ScalesGetter scaleclient.ScalesGetter diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index 4e4a9e74fd..36d88ef2d2 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -506,7 +506,7 @@ func SkipUnlessServerVersionGTE(v *utilversion.Version, c discovery.ServerVersio } } -func SkipIfMissingResource(dynamicClient dynamic.DynamicInterface, gvr schema.GroupVersionResource, namespace string) { +func SkipIfMissingResource(dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, namespace string) { resourceClient := dynamicClient.Resource(gvr).Namespace(namespace) _, err := resourceClient.List(metav1.ListOptions{}) if err != nil { @@ -1086,7 +1086,7 @@ func CheckTestingNSDeletedExcept(c clientset.Interface, skip string) error { // deleteNS deletes the provided namespace, waits for it to be completely deleted, and then checks // whether there are any pods remaining in a non-terminating state. -func deleteNS(c clientset.Interface, dynamicClient dynamic.DynamicInterface, namespace string, timeout time.Duration) error { +func deleteNS(c clientset.Interface, dynamicClient dynamic.Interface, namespace string, timeout time.Duration) error { startTime := time.Now() if err := c.CoreV1().Namespaces().Delete(namespace, nil); err != nil { return err @@ -1232,7 +1232,7 @@ func isDynamicDiscoveryError(err error) bool { } // hasRemainingContent checks if there is remaining content in the namespace via API discovery -func hasRemainingContent(c clientset.Interface, dynamicClient dynamic.DynamicInterface, namespace string) (bool, error) { +func hasRemainingContent(c clientset.Interface, dynamicClient dynamic.Interface, namespace string) (bool, error) { // some tests generate their own framework.Client rather than the default // TODO: ensure every test call has a configured dynamicClient if dynamicClient == nil { diff --git a/test/integration/client/dynamic_client_test.go b/test/integration/client/dynamic_client_test.go index f7d776512b..787e1fb7ba 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) } diff --git a/test/integration/garbagecollector/garbage_collector_test.go b/test/integration/garbagecollector/garbage_collector_test.go index 83fc260486..b5dace145b 100644 --- a/test/integration/garbagecollector/garbage_collector_test.go +++ b/test/integration/garbagecollector/garbage_collector_test.go @@ -168,9 +168,9 @@ func link(t *testing.T, owner, dependent metav1.Object) { func createRandomCustomResourceDefinition( t *testing.T, apiExtensionClient apiextensionsclientset.Interface, - dynamicClient dynamic.DynamicInterface, + dynamicClient dynamic.Interface, namespace string, -) (*apiextensionsv1beta1.CustomResourceDefinition, dynamic.DynamicResourceInterface) { +) (*apiextensionsv1beta1.CustomResourceDefinition, dynamic.ResourceInterface) { // Create a random custom resource definition and ensure it's available for // use. definition := apiextensionstestserver.NewRandomNameCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) @@ -193,7 +193,7 @@ type testContext struct { gc *garbagecollector.GarbageCollector clientSet clientset.Interface apiExtensionClient apiextensionsclientset.Interface - dynamicClient dynamic.DynamicInterface + dynamicClient dynamic.Interface startGC func(workers int) // syncPeriod is how often the GC started with startGC will be resynced. syncPeriod time.Duration diff --git a/test/integration/master/crd_test.go b/test/integration/master/crd_test.go index b31e404367..37ba367fbb 100644 --- a/test/integration/master/crd_test.go +++ b/test/integration/master/crd_test.go @@ -150,14 +150,12 @@ func TestCRD(t *testing.T) { } t.Logf("Trying to access foos.cr.bar.com with dynamic client") - barComConfig := *result.ClientConfig - barComConfig.GroupVersion = &schema.GroupVersion{Group: "cr.bar.com", Version: "v1"} - barComConfig.APIPath = "/apis" - barComClient, err := dynamic.NewClient(&barComConfig, *barComConfig.GroupVersion) + dynamicClient, err := dynamic.NewForConfig(result.ClientConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } - _, err = barComClient.Resource(&metav1.APIResource{Name: "foos", Namespaced: true}, "default").List(metav1.ListOptions{}) + fooResource := schema.GroupVersionResource{Group: "cr.bar.com", Version: "v1", Resource: "foos"} + _, err = dynamicClient.Resource(fooResource).Namespace("default").List(metav1.ListOptions{}) if err != nil { t.Errorf("Failed to list foos.cr.bar.com instances: %v", err) } @@ -201,7 +199,7 @@ func TestCRD(t *testing.T) { } createErr := make(chan error, 1) go func() { - _, err := barComClient.Resource(&metav1.APIResource{Name: "foos", Namespaced: true}, "default").Create(unstructuredFoo) + _, err := dynamicClient.Resource(fooResource).Namespace("default").Create(unstructuredFoo) t.Logf("Foo instance create returned: %v", err) if err != nil { createErr <- err @@ -216,7 +214,7 @@ func TestCRD(t *testing.T) { } t.Logf("Checking that Foo instance is visible with IncludeUninitialized=true") - _, err := barComClient.Resource(&metav1.APIResource{Name: "foos", Namespaced: true}, "default").Get(foo.ObjectMeta.Name, metav1.GetOptions{ + _, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{ IncludeUninitialized: true, }) switch { @@ -237,7 +235,7 @@ func TestCRD(t *testing.T) { for i := 0; i < 10; i++ { // would love to replace the following with a patch, but removing strings from the intitializer array // is not what JSON (Merge) patch authors had in mind. - fooUnstructured, err := barComClient.Resource(&metav1.APIResource{Name: "foos", Namespaced: true}, "default").Get(foo.ObjectMeta.Name, metav1.GetOptions{ + fooUnstructured, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{ IncludeUninitialized: true, }) if err != nil { @@ -274,7 +272,7 @@ func TestCRD(t *testing.T) { } fooUnstructured.UnmarshalJSON(bs) - _, err = barComClient.Resource(&metav1.APIResource{Name: "foos", Namespaced: true}, "default").Update(fooUnstructured) + _, err = dynamicClient.Resource(fooResource).Namespace("default").Update(fooUnstructured) if err != nil && !errors.IsConflict(err) { t.Fatalf("Failed to update Foo instance: %v", err) } else if err == nil { @@ -287,7 +285,7 @@ func TestCRD(t *testing.T) { } t.Logf("Checking that Foo instance is visible after removing the initializer") - if _, err := barComClient.Resource(&metav1.APIResource{Name: "foos", Namespaced: true}, "default").Get(foo.ObjectMeta.Name, metav1.GetOptions{}); err != nil { + if _, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{}); err != nil { t.Errorf("Unexpected error: %v", err) } }