Merge pull request #63446 from deads2k/client-08-remove-old

Automatic merge from submit-queue (batch tested with PRs 63367, 63718, 63446, 63723, 63720). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

finish new dynamic client and deprecate old dynamic client

Builds on a couple other pulls.  This completes the transition to the new dynamic client.

@kubernetes/sig-api-machinery-pr-reviews 
@caesarxuchao @sttts 

```release-note
The old dynamic client has been replaced by a new one.  The previous dynamic client will exist for one release in `client-go/deprecated-dynamic`.  Switch as soon as possible.
```
This commit is contained in:
Kubernetes Submit Queue
2018-05-11 14:49:16 -07:00
committed by GitHub
54 changed files with 1267 additions and 753 deletions

View File

@@ -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 // 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 { type ClientBackedDryRunGetter struct {
client clientset.Interface client clientset.Interface
dynamicClient dynamic.DynamicInterface dynamicClient dynamic.Interface
} }
// InitDryRunGetter should implement the DryRunGetter interface // InitDryRunGetter should implement the DryRunGetter interface

View File

@@ -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/tokentest
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook
staging/src/k8s.io/apiserver/plugin/pkg/authorizer/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/discovery/cached
staging/src/k8s.io/client-go/dynamic staging/src/k8s.io/client-go/dynamic
staging/src/k8s.io/client-go/dynamic/fake staging/src/k8s.io/client-go/dynamic/fake

View File

@@ -60,7 +60,7 @@ const ResourceResyncTime time.Duration = 0
// up to date as the notification is sent. // up to date as the notification is sent.
type GarbageCollector struct { type GarbageCollector struct {
restMapper resettableRESTMapper restMapper resettableRESTMapper
dynamicClient dynamic.DynamicInterface dynamicClient dynamic.Interface
// garbage collector attempts to delete the items in attemptToDelete queue when the time is ripe. // garbage collector attempts to delete the items in attemptToDelete queue when the time is ripe.
attemptToDelete workqueue.RateLimitingInterface attemptToDelete workqueue.RateLimitingInterface
// garbage collector attempts to orphan the dependents of the items in the attemptToOrphan queue, then deletes the items. // 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( func NewGarbageCollector(
dynamicClient dynamic.DynamicInterface, dynamicClient dynamic.Interface,
mapper resettableRESTMapper, mapper resettableRESTMapper,
deletableResources map[schema.GroupVersionResource]struct{}, deletableResources map[schema.GroupVersionResource]struct{},
ignoredResources map[schema.GroupResource]struct{}, ignoredResources map[schema.GroupResource]struct{},

View File

@@ -91,10 +91,7 @@ type GraphBuilder struct {
// it is protected by monitorLock. // it is protected by monitorLock.
running bool running bool
dynamicClient dynamic.DynamicInterface dynamicClient dynamic.Interface
// metaOnlyClientPool uses a special codec, which removes fields except for
// apiVersion, kind, and metadata during decoding.
metaOnlyClientPool dynamic.ClientPool
// monitors are the producer of the graphChanges queue, graphBuilder alters // monitors are the producer of the graphChanges queue, graphBuilder alters
// the in-memory graph according to the changes. // the in-memory graph according to the changes.
graphChanges workqueue.RateLimitingInterface graphChanges workqueue.RateLimitingInterface
@@ -128,7 +125,7 @@ func (m *monitor) Run() {
type monitors map[schema.GroupVersionResource]*monitor 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{ return &cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { 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. // We want to list this resource in all namespaces if it's namespace scoped, so not passing namespace is ok.

View File

@@ -43,7 +43,7 @@ type NamespacedResourcesDeleterInterface interface {
} }
func NewNamespacedResourcesDeleter(nsClient v1clientset.NamespaceInterface, func NewNamespacedResourcesDeleter(nsClient v1clientset.NamespaceInterface,
dynamicClient dynamic.DynamicInterface, podsGetter v1clientset.PodsGetter, dynamicClient dynamic.Interface, podsGetter v1clientset.PodsGetter,
discoverResourcesFn func() ([]*metav1.APIResourceList, error), discoverResourcesFn func() ([]*metav1.APIResourceList, error),
finalizerToken v1.FinalizerName, deleteNamespaceWhenDone bool) NamespacedResourcesDeleterInterface { finalizerToken v1.FinalizerName, deleteNamespaceWhenDone bool) NamespacedResourcesDeleterInterface {
d := &namespacedResourcesDeleter{ d := &namespacedResourcesDeleter{
@@ -68,7 +68,7 @@ type namespacedResourcesDeleter struct {
// Client to manipulate the namespace. // Client to manipulate the namespace.
nsClient v1clientset.NamespaceInterface nsClient v1clientset.NamespaceInterface
// Dynamic client to list and delete all namespaced resources. // Dynamic client to list and delete all namespaced resources.
dynamicClient dynamic.DynamicInterface dynamicClient dynamic.Interface
// Interface to get PodInterface. // Interface to get PodInterface.
podsGetter v1clientset.PodsGetter podsGetter v1clientset.PodsGetter
// Cache of what operations are not supported on each group version resource. // Cache of what operations are not supported on each group version resource.

View File

@@ -63,7 +63,7 @@ type NamespaceController struct {
// NewNamespaceController creates a new NamespaceController // NewNamespaceController creates a new NamespaceController
func NewNamespaceController( func NewNamespaceController(
kubeClient clientset.Interface, kubeClient clientset.Interface,
dynamicClient dynamic.DynamicInterface, dynamicClient dynamic.Interface,
discoverResourcesFn func() ([]*metav1.APIResourceList, error), discoverResourcesFn func() ([]*metav1.APIResourceList, error),
namespaceInformer coreinformers.NamespaceInformer, namespaceInformer coreinformers.NamespaceInformer,
resyncPeriod time.Duration, resyncPeriod time.Duration,

View File

@@ -226,7 +226,6 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch/testing:go_default_library", "//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/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/version: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:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library",
"//vendor/k8s.io/client-go/scale/fake:go_default_library", "//vendor/k8s.io/client-go/scale/fake:go_default_library",

View File

@@ -82,7 +82,7 @@ type ApplyOptions struct {
Builder *resource.Builder Builder *resource.Builder
Mapper meta.RESTMapper Mapper meta.RESTMapper
Scaler scaleclient.ScalesGetter Scaler scaleclient.ScalesGetter
DynamicClient dynamic.DynamicInterface DynamicClient dynamic.Interface
ClientSetFunc func() (internalclientset.Interface, error) ClientSetFunc func() (internalclientset.Interface, error)
OpenAPISchema openapi.Resources OpenAPISchema openapi.Resources
@@ -580,7 +580,7 @@ func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (n
type pruner struct { type pruner struct {
mapper meta.RESTMapper mapper meta.RESTMapper
dynamicClient dynamic.DynamicInterface dynamicClient dynamic.Interface
clientsetFunc func() (internalclientset.Interface, error) clientsetFunc func() (internalclientset.Interface, error)
visitedUids sets.String 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) 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 { if !cascade {
return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, nil) 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 { type patcher struct {
mapping *meta.RESTMapping mapping *meta.RESTMapping
helper *resource.Helper helper *resource.Helper
dynamicClient dynamic.DynamicInterface dynamicClient dynamic.Interface
clientsetFunc func() (internalclientset.Interface, error) clientsetFunc func() (internalclientset.Interface, error)
overwrite bool overwrite bool

View File

@@ -82,6 +82,7 @@ go_test(
"//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/genericclioptions/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library", "//pkg/kubectl/scheme:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/api/batch/v1: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:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema: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/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/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/rest: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/fake:go_default_library",

View File

@@ -348,7 +348,7 @@ type CreateSubcommandOptions struct {
EnforceNamespace bool EnforceNamespace bool
Mapper meta.RESTMapper Mapper meta.RESTMapper
DynamicClient dynamic.DynamicInterface DynamicClient dynamic.Interface
PrintObj func(obj kruntime.Object) error PrintObj func(obj kruntime.Object) error

View File

@@ -22,7 +22,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
apitesting "k8s.io/kubernetes/pkg/api/testing" apitesting "k8s.io/kubernetes/pkg/api/testing"
@@ -30,6 +29,7 @@ import (
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme" "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) { func initTestErrorHandler(t *testing.T) {
cmdutil.BehaviorOnFatal(func(str string, code int) { cmdutil.BehaviorOnFatal(func(str string, code int) {

View File

@@ -30,7 +30,6 @@ import (
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
@@ -42,7 +41,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/scheme"
) )
var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer var unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer
func fakecmd() *cobra.Command { func fakecmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{

View File

@@ -100,7 +100,7 @@ type ExposeServiceOptions struct {
Namespace string Namespace string
Mapper meta.RESTMapper Mapper meta.RESTMapper
DynamicClient dynamic.DynamicInterface DynamicClient dynamic.Interface
Builder *resource.Builder Builder *resource.Builder
Recorder genericclioptions.Recorder Recorder genericclioptions.Recorder

View File

@@ -75,6 +75,7 @@ go_test(
"//pkg/kubectl/cmd/util/openapi:go_default_library", "//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/cmd/util/openapi/testing:go_default_library", "//pkg/kubectl/cmd/util/openapi/testing:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library", "//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/genericclioptions/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library", "//pkg/kubectl/scheme:go_default_library",
"//pkg/printers:go_default_library", "//pkg/printers:go_default_library",
"//vendor/k8s.io/api/core/v1: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/runtime/serializer/streaming:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff: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/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:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library",
"//vendor/k8s.io/client-go/rest/watch:go_default_library", "//vendor/k8s.io/client-go/rest/watch:go_default_library",

View File

@@ -34,7 +34,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming" "k8s.io/apimachinery/pkg/runtime/serializer/streaming"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
restclientwatch "k8s.io/client-go/rest/watch" restclientwatch "k8s.io/client-go/rest/watch"
@@ -49,6 +48,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
openapitesting "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing" openapitesting "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/scheme"
) )
@@ -61,7 +61,7 @@ func init() {
scheme.Scheme.AddConversionFuncs(v1.Convert_v1_PodSecurityContext_To_core_PodSecurityContext) scheme.Scheme.AddConversionFuncs(v1.Convert_v1_PodSecurityContext_To_core_PodSecurityContext)
} }
var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer var unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer
func defaultHeader() http.Header { func defaultHeader() http.Header {
header := http.Header{} header := http.Header{}

View File

@@ -107,7 +107,7 @@ type RunOptions struct {
PrintObj func(runtime.Object) error PrintObj func(runtime.Object) error
Recorder genericclioptions.Recorder Recorder genericclioptions.Recorder
DynamicClient dynamic.DynamicInterface DynamicClient dynamic.Interface
ArgsLenAtDash int ArgsLenAtDash int
Attach bool Attach bool

View File

@@ -398,7 +398,7 @@ func (f *TestFactory) ClientSet() (internalclientset.Interface, error) {
return clientset, nil return clientset, nil
} }
func (f *TestFactory) DynamicClient() (dynamic.DynamicInterface, error) { func (f *TestFactory) DynamicClient() (dynamic.Interface, error) {
if f.FakeDynamicClient != nil { if f.FakeDynamicClient != nil {
return f.FakeDynamicClient, nil return f.FakeDynamicClient, nil
} }

View File

@@ -85,7 +85,7 @@ type ClientAccessFactory interface {
ClientSet() (internalclientset.Interface, error) ClientSet() (internalclientset.Interface, error)
// DynamicClient returns a dynamic client ready for use // DynamicClient returns a dynamic client ready for use
DynamicClient() (dynamic.DynamicInterface, error) DynamicClient() (dynamic.Interface, error)
// KubernetesClientSet gives you back an external clientset // KubernetesClientSet gives you back an external clientset
KubernetesClientSet() (*kubernetes.Clientset, error) KubernetesClientSet() (*kubernetes.Clientset, error)

View File

@@ -114,7 +114,7 @@ func (f *ring0Factory) ClientSet() (internalclientset.Interface, error) {
return internalclientset.NewForConfig(clientConfig) return internalclientset.NewForConfig(clientConfig)
} }
func (f *ring0Factory) DynamicClient() (dynamic.DynamicInterface, error) { func (f *ring0Factory) DynamicClient() (dynamic.Interface, error) {
clientConfig, err := f.ClientConfig() clientConfig, err := f.ClientConfig()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -117,7 +117,7 @@ func (f *ring1Factory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (
cfg.APIPath = "/api" cfg.APIPath = "/api"
} }
gv := mapping.GroupVersionKind.GroupVersion() gv := mapping.GroupVersionKind.GroupVersion()
cfg.ContentConfig = dynamic.ContentConfig() cfg.ContentConfig = resource.UnstructuredPlusDefaultContentConfig()
cfg.GroupVersion = &gv cfg.GroupVersion = &gv
return restclient.RESTClientFor(cfg) return restclient.RESTClientFor(cfg)
} }
@@ -146,13 +146,8 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES
return nil, err return nil, err
} }
clientConfigCopy := *clientConfig
clientConfigCopy.APIPath = dynamic.LegacyAPIPathResolverFunc(mapping.GroupVersionKind)
gv := mapping.GroupVersionKind.GroupVersion()
clientConfigCopy.GroupVersion = &gv
// used to fetch the resource // used to fetch the resource
dynamicClient, err := dynamic.NewClient(&clientConfigCopy, gv) dynamicClient, err := dynamic.NewForConfig(clientConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -11,6 +11,7 @@ go_library(
"interfaces.go", "interfaces.go",
"mapper.go", "mapper.go",
"result.go", "result.go",
"scheme.go",
"selector.go", "selector.go",
"visitor.go", "visitor.go",
], ],
@@ -36,7 +37,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch: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/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/rest:go_default_library",
"//vendor/k8s.io/client-go/restmapper:go_default_library", "//vendor/k8s.io/client-go/restmapper:go_default_library",
], ],

View File

@@ -19,7 +19,6 @@ package resource
import ( import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
) )
@@ -47,7 +46,7 @@ func (clientConfigFn ClientConfigFunc) unstructuredClientForGroupVersion(gv sche
if err != nil { if err != nil {
return nil, err return nil, err
} }
cfg.ContentConfig = dynamic.ContentConfig() cfg.ContentConfig = UnstructuredPlusDefaultContentConfig()
cfg.GroupVersion = &gv cfg.GroupVersion = &gv
if len(gv.Group) == 0 { if len(gv.Group) == 0 {
cfg.APIPath = "/api" cfg.APIPath = "/api"

View File

@@ -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),
}
}

View File

@@ -203,12 +203,7 @@ type genericDescriber struct {
} }
func (g *genericDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (output string, err error) { func (g *genericDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (output string, err error) {
apiResource := &metav1.APIResource{ obj, err := g.dynamic.Resource(g.mapping.Resource).Namespace(namespace).Get(name, metav1.GetOptions{})
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{})
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -99,6 +99,7 @@ filegroup(
"//staging/src/k8s.io/apiserver/plugin/pkg/audit:all-srcs", "//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/authenticator:all-srcs",
"//staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook: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/discovery:all-srcs",
"//staging/src/k8s.io/client-go/dynamic:all-srcs", "//staging/src/k8s.io/client-go/dynamic:all-srcs",
"//staging/src/k8s.io/client-go/examples/create-update-delete-deployment:all-srcs", "//staging/src/k8s.io/client-go/examples/create-update-delete-deployment:all-srcs",

View File

@@ -80,7 +80,7 @@ func TestClusterScopedCRUD(t *testing.T) {
testFieldSelector(t, ns, noxuDefinition, dynamicClient) 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) noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
initialList, err := noxuResourceClient.List(metav1.ListOptions{}) initialList, err := noxuResourceClient.List(metav1.ListOptions{})
if err != nil { 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) noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
initialList, err := noxuResourceClient.List(metav1.ListOptions{}) initialList, err := noxuResourceClient.List(metav1.ListOptions{})
if err != nil { if err != nil {
@@ -704,7 +704,7 @@ func TestCrossNamespaceListWatch(t *testing.T) {
checkNamespacesWatchHelper(t, ns2, noxuNamespacesWatch2) 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) createdInstance, err := instantiateCustomResource(t, testserver.NewNoxuInstance(ns, name), noxuNamespacedResourceClient, noxuDefinition)
if err != nil { if err != nil {
t.Fatalf("unable to create noxu Instance:%v", err) t.Fatalf("unable to create noxu Instance:%v", err)

View File

@@ -41,7 +41,7 @@ import (
"k8s.io/client-go/dynamic" "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) createdInstance, err := client.Create(instanceToCreate)
if err != nil { if err != nil {
t.Logf("%#v", createdInstance) t.Logf("%#v", createdInstance)
@@ -68,7 +68,7 @@ func instantiateCustomResource(t *testing.T, instanceToCreate *unstructured.Unst
return createdInstance, nil 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} gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural}
if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {
@@ -77,24 +77,6 @@ func NewNamespacedCustomResourceClient(ns string, client dynamic.DynamicInterfac
return client.Resource(gvr) 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) { func TestMultipleResourceInstances(t *testing.T) {
stopCh, apiExtensionClient, dynamicClient, err := testserver.StartDefaultServerWithClients() stopCh, apiExtensionClient, dynamicClient, err := testserver.StartDefaultServerWithClients()
if err != nil { if err != nil {

View File

@@ -105,7 +105,6 @@ func TestStatusSubresource(t *testing.T) {
ns := "not-the-default" ns := "not-the-default"
noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition)
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition) _, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
if err != nil { if err != nil {
t.Fatalf("unable to create noxu instance: %v", err) t.Fatalf("unable to create noxu instance: %v", err)
@@ -135,7 +134,7 @@ func TestStatusSubresource(t *testing.T) {
// UpdateStatus should not update spec. // UpdateStatus should not update spec.
// Check that .spec.num = 10 and .status.num = 20 // Check that .spec.num = 10 and .status.num = 20
updatedStatusInstance, err := noxuStatusResourceClient.Update(gottenNoxuInstance) updatedStatusInstance, err := noxuResourceClient.UpdateStatus(gottenNoxuInstance)
if err != nil { if err != nil {
t.Fatalf("unable to update status: %v", err) t.Fatalf("unable to update status: %v", err)
} }
@@ -238,7 +237,6 @@ func TestScaleSubresource(t *testing.T) {
ns := "not-the-default" ns := "not-the-default"
noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition)
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition) _, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
if err != nil { if err != nil {
t.Fatalf("unable to create noxu instance: %v", err) t.Fatalf("unable to create noxu instance: %v", err)
@@ -258,7 +256,7 @@ func TestScaleSubresource(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
_, err = noxuStatusResourceClient.Update(gottenNoxuInstance) _, err = noxuResourceClient.UpdateStatus(gottenNoxuInstance)
if err != nil { if err != nil {
t.Fatalf("unable to update status: %v", err) t.Fatalf("unable to update status: %v", err)
} }
@@ -430,7 +428,6 @@ func TestValidateOnlyStatus(t *testing.T) {
} }
ns := "not-the-default" ns := "not-the-default"
noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition)
// set .spec.num = 10 and .status.num = 10 // set .spec.num = 10 and .status.num = 10
noxuInstance := NewNoxuSubresourceInstance(ns, "foo") noxuInstance := NewNoxuSubresourceInstance(ns, "foo")
@@ -449,7 +446,7 @@ func TestValidateOnlyStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error setting .spec.num: %v", err) t.Fatalf("unexpected error setting .spec.num: %v", err)
} }
createdNoxuInstance, err = noxuStatusResourceClient.Update(createdNoxuInstance) createdNoxuInstance, err = noxuResourceClient.UpdateStatus(createdNoxuInstance)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@@ -459,7 +456,7 @@ func TestValidateOnlyStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error setting .status.num: %v", err) t.Fatalf("unexpected error setting .status.num: %v", err)
} }
createdNoxuInstance, err = noxuStatusResourceClient.Update(createdNoxuInstance) createdNoxuInstance, err = noxuResourceClient.UpdateStatus(createdNoxuInstance)
if err == nil { if err == nil {
t.Fatal("expected error, but got none") t.Fatal("expected error, but got none")
} }
@@ -577,7 +574,6 @@ func TestGeneration(t *testing.T) {
ns := "not-the-default" ns := "not-the-default"
noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition)
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition) _, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
if err != nil { if err != nil {
t.Fatalf("unable to create noxu instance: %v", err) t.Fatalf("unable to create noxu instance: %v", err)
@@ -599,7 +595,7 @@ func TestGeneration(t *testing.T) {
} }
// UpdateStatus does not increment generation // UpdateStatus does not increment generation
updatedStatusInstance, err := noxuStatusResourceClient.Update(gottenNoxuInstance) updatedStatusInstance, err := noxuResourceClient.UpdateStatus(gottenNoxuInstance)
if err != nil { if err != nil {
t.Fatalf("unable to update status: %v", err) t.Fatalf("unable to update status: %v", err)
} }
@@ -660,8 +656,6 @@ func TestSubresourcePatch(t *testing.T) {
ns := "not-the-default" ns := "not-the-default"
noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition) noxuResourceClient := NewNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
noxuStatusResourceClient := NewNamespacedCustomResourceStatusClient(ns, dynamicClient, noxuDefinition)
noxuScaleResourceClient := NewNamespacedCustomResourceScaleClient(ns, dynamicClient, noxuDefinition)
_, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition) _, err = instantiateCustomResource(t, NewNoxuSubresourceInstance(ns, "foo"), noxuResourceClient, noxuDefinition)
if err != nil { if err != nil {
t.Fatalf("unable to create noxu instance: %v", err) 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}}`) 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 { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -708,19 +702,19 @@ func TestSubresourcePatch(t *testing.T) {
} }
// no-op patch // no-op patch
_, err = noxuStatusResourceClient.Patch("foo", types.MergePatchType, patch) _, err = noxuResourceClient.Patch("foo", types.MergePatchType, patch, "status")
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
// empty patch // empty patch
_, err = noxuStatusResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`)) _, err = noxuResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`), "status")
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
patch = []byte(`{"spec": {"replicas":7}, "status": {"replicas":7}}`) 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 { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -749,24 +743,24 @@ func TestSubresourcePatch(t *testing.T) {
} }
// no-op patch // no-op patch
_, err = noxuScaleResourceClient.Patch("foo", types.MergePatchType, patch) _, err = noxuResourceClient.Patch("foo", types.MergePatchType, patch, "scale")
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
// empty patch // empty patch
_, err = noxuScaleResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`)) _, err = noxuResourceClient.Patch("foo", types.MergePatchType, []byte(`{}`), "scale")
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
// make sure strategic merge patch is not supported for both status and scale // 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 { if err == nil {
t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources") 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 { if err == nil {
t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources") t.Fatalf("unexpected non-error: strategic merge patch is not supported for custom resources")
} }

View File

@@ -178,7 +178,7 @@ func CreateNewCustomResourceDefinitionWatchUnsafe(crd *apiextensionsv1beta1.Cust
return err 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) err := CreateNewCustomResourceDefinitionWatchUnsafe(crd, apiExtensionsClient)
if err != nil { if err != nil {
return err return err
@@ -209,14 +209,14 @@ func CreateNewCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceD
return nil return nil
} }
func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClientSet dynamic.DynamicInterface) error { func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition, dynamicClientSet dynamic.Interface) error {
ns := "" ns := ""
if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {
ns = "aval" ns = "aval"
} }
gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural} 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 { if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {
resourceClient = dynamicClientSet.Resource(gvr).Namespace(ns) resourceClient = dynamicClientSet.Resource(gvr).Namespace(ns)
} else { } else {

View File

@@ -139,7 +139,7 @@ func StartDefaultServer() (chan struct{}, *rest.Config, error) {
return StartServer(config) 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() stopCh, config, err := StartDefaultServer()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err

View File

@@ -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"],
)

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package dynamic package deprecated_dynamic
import ( import (
"encoding/json" "encoding/json"

View File

@@ -17,7 +17,7 @@ limitations under the License.
// Package dynamic provides a client interface to arbitrary Kubernetes // Package dynamic provides a client interface to arbitrary Kubernetes
// APIs that exposes common high level operations and exposes common // APIs that exposes common high level operations and exposes common
// metadata. // metadata.
package dynamic package deprecated_dynamic
import ( import (
"strings" "strings"
@@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
) )
@@ -65,13 +66,13 @@ type ResourceInterface interface {
// and manipulate metadata of a Kubernetes API group, and implements Interface. // and manipulate metadata of a Kubernetes API group, and implements Interface.
type Client struct { type Client struct {
version schema.GroupVersion version schema.GroupVersion
delegate DynamicInterface delegate dynamic.Interface
} }
// NewClient returns a new client based on the passed in config. The // NewClient returns a new client based on the passed in config. The
// codec is ignored, as the dynamic client uses it's own codec. // codec is ignored, as the dynamic client uses it's own codec.
func NewClient(conf *restclient.Config, version schema.GroupVersion) (*Client, error) { func NewClient(conf *restclient.Config, version schema.GroupVersion) (*Client, error) {
delegate, err := NewForConfig(conf) delegate, err := dynamic.NewForConfig(conf)
if err != nil { if err != nil {
return nil, err 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. // is ignored. The ResourceInterface inherits the parameter codec of c.
func (c *Client) Resource(resource *metav1.APIResource, namespace string) ResourceInterface { func (c *Client) Resource(resource *metav1.APIResource, namespace string) ResourceInterface {
resourceTokens := strings.SplitN(resource.Name, "/", 2) resourceTokens := strings.SplitN(resource.Name, "/", 2)
subresource := "" subresources := []string{}
if len(resourceTokens) > 1 { if len(resourceTokens) > 1 {
subresource = resourceTokens[1] subresources = strings.Split(resourceTokens[1], "/")
} }
if len(namespace) == 0 { 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 // the old interfaces used the wrong type for lists. this fixes that
func oldResourceShim(in DynamicResourceInterface) ResourceInterface { func oldResourceShim(in dynamic.ResourceInterface, subresources []string) ResourceInterface {
return oldResourceShimType{DynamicResourceInterface: in} return oldResourceShimType{ResourceInterface: in, subresources: subresources}
} }
type oldResourceShimType struct { 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) { 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) { 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...)
} }

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package dynamic package deprecated_dynamic
import ( import (
"sync" "sync"

View File

@@ -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)
}
}
}

View File

@@ -8,10 +8,7 @@ load(
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = [ srcs = ["client_test.go"],
"client_test.go",
"dynamic_util_test.go",
],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
@@ -29,10 +26,7 @@ go_test(
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"bad_debt.go", "interface.go",
"client.go",
"client_pool.go",
"dynamic_util.go",
"scheme.go", "scheme.go",
"simple.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/runtime/serializer/versioning:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch: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", "//vendor/k8s.io/client-go/rest:go_default_library",
], ],
) )

View File

@@ -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)) srv := httptest.NewServer(http.HandlerFunc(h))
cl, err := NewClient(&restclient.Config{ cl, err := NewForConfig(&restclient.Config{
Host: srv.URL, Host: srv.URL,
ContentConfig: restclient.ContentConfig{GroupVersion: gv}, })
}, *gv)
if err != nil { if err != nil {
srv.Close() srv.Close()
return nil, nil, err return nil, nil, err
@@ -116,9 +115,8 @@ func TestList(t *testing.T) {
}, },
} }
for _, tc := range tcs { for _, tc := range tcs {
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" { if r.Method != "GET" {
t.Errorf("List(%q) got HTTP method %s. wanted GET", tc.name, r.Method) 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() 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 { if err != nil {
t.Errorf("unexpected error when listing %q: %v", tc.name, err) t.Errorf("unexpected error when listing %q: %v", tc.name, err)
continue continue
@@ -150,12 +148,13 @@ func TestList(t *testing.T) {
func TestGet(t *testing.T) { func TestGet(t *testing.T) {
tcs := []struct { tcs := []struct {
resource string resource string
namespace string subresource []string
name string namespace string
path string name string
resp []byte path string
want *unstructured.Unstructured resp []byte
want *unstructured.Unstructured
}{ }{
{ {
resource: "rtest", resource: "rtest",
@@ -173,25 +172,26 @@ func TestGet(t *testing.T) {
want: getObject("vTest", "rTest", "namespaced_get"), want: getObject("vTest", "rTest", "namespaced_get"),
}, },
{ {
resource: "rtest/srtest", resource: "rtest",
name: "normal_subresource_get", subresource: []string{"srtest"},
path: "/apis/gtest/vtest/rtest/normal_subresource_get/srtest", name: "normal_subresource_get",
resp: getJSON("vTest", "srTest", "normal_subresource_get"), path: "/apis/gtest/vtest/rtest/normal_subresource_get/srtest",
want: getObject("vTest", "srTest", "normal_subresource_get"), resp: getJSON("vTest", "srTest", "normal_subresource_get"),
want: getObject("vTest", "srTest", "normal_subresource_get"),
}, },
{ {
resource: "rtest/srtest", resource: "rtest",
namespace: "nstest", subresource: []string{"srtest"},
name: "namespaced_subresource_get", namespace: "nstest",
path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_get/srtest", name: "namespaced_subresource_get",
resp: getJSON("vTest", "srTest", "namespaced_subresource_get"), path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_get/srtest",
want: getObject("vTest", "srTest", "namespaced_subresource_get"), resp: getJSON("vTest", "srTest", "namespaced_subresource_get"),
want: getObject("vTest", "srTest", "namespaced_subresource_get"),
}, },
} }
for _, tc := range tcs { for _, tc := range tcs {
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource}
resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" { if r.Method != "GET" {
t.Errorf("Get(%q) got HTTP method %s. wanted GET", tc.name, r.Method) 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() 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 { if err != nil {
t.Errorf("unexpected error when getting %q: %v", tc.name, err) t.Errorf("unexpected error when getting %q: %v", tc.name, err)
continue continue
@@ -230,6 +230,7 @@ func TestDelete(t *testing.T) {
Status: metav1.StatusSuccess, Status: metav1.StatusSuccess,
} }
tcs := []struct { tcs := []struct {
subresource []string
namespace string namespace string
name string name string
path string path string
@@ -244,6 +245,17 @@ func TestDelete(t *testing.T) {
name: "namespaced_delete", name: "namespaced_delete",
path: "/apis/gtest/vtest/namespaces/nstest/rtest/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", namespace: "nstest",
name: "namespaced_delete_with_options", name: "namespaced_delete_with_options",
@@ -252,9 +264,8 @@ func TestDelete(t *testing.T) {
}, },
} }
for _, tc := range tcs { for _, tc := range tcs {
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
if r.Method != "DELETE" { if r.Method != "DELETE" {
t.Errorf("Delete(%q) got HTTP method %s. wanted DELETE", tc.name, r.Method) 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() 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 { if err != nil {
t.Errorf("unexpected error when deleting %q: %v", tc.name, err) t.Errorf("unexpected error when deleting %q: %v", tc.name, err)
continue continue
@@ -301,9 +312,8 @@ func TestDeleteCollection(t *testing.T) {
}, },
} }
for _, tc := range tcs { for _, tc := range tcs {
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
if r.Method != "DELETE" { if r.Method != "DELETE" {
t.Errorf("DeleteCollection(%q) got HTTP method %s. wanted DELETE", tc.name, r.Method) 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() 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 { if err != nil {
t.Errorf("unexpected error when deleting collection %q: %v", tc.name, err) t.Errorf("unexpected error when deleting collection %q: %v", tc.name, err)
continue continue
@@ -331,11 +341,12 @@ func TestDeleteCollection(t *testing.T) {
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
tcs := []struct { tcs := []struct {
resource string resource string
name string subresource []string
namespace string name string
obj *unstructured.Unstructured namespace string
path string obj *unstructured.Unstructured
path string
}{ }{
{ {
resource: "rtest", resource: "rtest",
@@ -350,11 +361,25 @@ func TestCreate(t *testing.T) {
path: "/apis/gtest/vtest/namespaces/nstest/rtest", path: "/apis/gtest/vtest/namespaces/nstest/rtest",
obj: getObject("gtest/vTest", "rTest", "namespaced_create"), 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 { for _, tc := range tcs {
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource}
resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" { if r.Method != "POST" {
t.Errorf("Create(%q) got HTTP method %s. wanted POST", tc.name, r.Method) 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() 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 { if err != nil {
t.Errorf("unexpected error when creating %q: %v", tc.name, err) t.Errorf("unexpected error when creating %q: %v", tc.name, err)
continue continue
@@ -393,11 +418,12 @@ func TestCreate(t *testing.T) {
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
tcs := []struct { tcs := []struct {
resource string resource string
name string subresource []string
namespace string name string
obj *unstructured.Unstructured namespace string
path string obj *unstructured.Unstructured
path string
}{ }{
{ {
resource: "rtest", resource: "rtest",
@@ -413,23 +439,24 @@ func TestUpdate(t *testing.T) {
obj: getObject("gtest/vTest", "rTest", "namespaced_update"), obj: getObject("gtest/vTest", "rTest", "namespaced_update"),
}, },
{ {
resource: "rtest/srtest", resource: "rtest",
name: "normal_subresource_update", subresource: []string{"srtest"},
path: "/apis/gtest/vtest/rtest/normal_update/srtest", name: "normal_subresource_update",
obj: getObject("gtest/vTest", "srTest", "normal_update"), path: "/apis/gtest/vtest/rtest/normal_update/srtest",
obj: getObject("gtest/vTest", "srTest", "normal_update"),
}, },
{ {
resource: "rtest/srtest", resource: "rtest",
name: "namespaced_subresource_update", subresource: []string{"srtest"},
namespace: "nstest", name: "namespaced_subresource_update",
path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update/srtest", namespace: "nstest",
obj: getObject("gtest/vTest", "srTest", "namespaced_update"), path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_update/srtest",
obj: getObject("gtest/vTest", "srTest", "namespaced_update"),
}, },
} }
for _, tc := range tcs { for _, tc := range tcs {
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource}
resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
if r.Method != "PUT" { if r.Method != "PUT" {
t.Errorf("Update(%q) got HTTP method %s. wanted PUT", tc.name, r.Method) 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() 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 { if err != nil {
t.Errorf("unexpected error when updating %q: %v", tc.name, err) t.Errorf("unexpected error when updating %q: %v", tc.name, err)
continue continue
@@ -497,9 +524,8 @@ func TestWatch(t *testing.T) {
}, },
} }
for _, tc := range tcs { for _, tc := range tcs {
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
resource := &metav1.APIResource{Name: "rtest", Namespaced: len(tc.namespace) != 0} cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" { if r.Method != "GET" {
t.Errorf("Watch(%q) got HTTP method %s. wanted GET", tc.name, r.Method) 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) 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 { for _, e := range tc.events {
enc.Encode(&e) enc.Encode(&e)
} }
@@ -522,7 +548,7 @@ func TestWatch(t *testing.T) {
} }
defer srv.Close() 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 { if err != nil {
t.Errorf("unexpected error when watching %q: %v", tc.name, err) t.Errorf("unexpected error when watching %q: %v", tc.name, err)
continue continue
@@ -539,12 +565,13 @@ func TestWatch(t *testing.T) {
func TestPatch(t *testing.T) { func TestPatch(t *testing.T) {
tcs := []struct { tcs := []struct {
resource string resource string
name string subresource []string
namespace string name string
patch []byte namespace string
want *unstructured.Unstructured patch []byte
path string want *unstructured.Unstructured
path string
}{ }{
{ {
resource: "rtest", resource: "rtest",
@@ -562,25 +589,26 @@ func TestPatch(t *testing.T) {
want: getObject("gtest/vTest", "rTest", "namespaced_patch"), want: getObject("gtest/vTest", "rTest", "namespaced_patch"),
}, },
{ {
resource: "rtest/srtest", resource: "rtest",
name: "normal_subresource_patch", subresource: []string{"srtest"},
path: "/apis/gtest/vtest/rtest/normal_subresource_patch/srtest", name: "normal_subresource_patch",
patch: getJSON("gtest/vTest", "srTest", "normal_subresource_patch"), path: "/apis/gtest/vtest/rtest/normal_subresource_patch/srtest",
want: getObject("gtest/vTest", "srTest", "normal_subresource_patch"), patch: getJSON("gtest/vTest", "srTest", "normal_subresource_patch"),
want: getObject("gtest/vTest", "srTest", "normal_subresource_patch"),
}, },
{ {
resource: "rtest/srtest", resource: "rtest",
name: "namespaced_subresource_patch", subresource: []string{"srtest"},
namespace: "nstest", name: "namespaced_subresource_patch",
path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_patch/srtest", namespace: "nstest",
patch: getJSON("gtest/vTest", "srTest", "namespaced_subresource_patch"), path: "/apis/gtest/vtest/namespaces/nstest/rtest/namespaced_subresource_patch/srtest",
want: getObject("gtest/vTest", "srTest", "namespaced_subresource_patch"), patch: getJSON("gtest/vTest", "srTest", "namespaced_subresource_patch"),
want: getObject("gtest/vTest", "srTest", "namespaced_subresource_patch"),
}, },
} }
for _, tc := range tcs { for _, tc := range tcs {
gv := &schema.GroupVersion{Group: "gtest", Version: "vtest"} resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: tc.resource}
resource := &metav1.APIResource{Name: tc.resource, Namespaced: len(tc.namespace) != 0} cl, srv, err := getClientServer(func(w http.ResponseWriter, r *http.Request) {
cl, srv, err := getClientServer(gv, func(w http.ResponseWriter, r *http.Request) {
if r.Method != "PATCH" { if r.Method != "PATCH" {
t.Errorf("Patch(%q) got HTTP method %s. wanted PATCH", tc.name, r.Method) 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() 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 { if err != nil {
t.Errorf("unexpected error when patching %q: %v", tc.name, err) t.Errorf("unexpected error when patching %q: %v", tc.name, err)
continue continue

View File

@@ -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]
}

View File

@@ -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)
}
}
}

View File

@@ -7,11 +7,7 @@ load(
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = ["simple.go"],
"client.go",
"client_pool.go",
"simple.go",
],
importpath = "k8s.io/client-go/dynamic/fake", importpath = "k8s.io/client-go/dynamic/fake",
deps = [ deps = [
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//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/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/dynamic: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/testing:go_default_library",
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
], ],
) )

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -17,6 +17,8 @@ limitations under the License.
package fake package fake
import ( import (
"strings"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -63,37 +65,54 @@ type FakeDynamicClient struct {
} }
type dynamicResourceClient struct { type dynamicResourceClient struct {
client *FakeDynamicClient client *FakeDynamicClient
namespace string namespace string
resource schema.GroupVersionResource resource schema.GroupVersionResource
subresource string
} }
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} return &dynamicResourceClient{client: c, resource: resource}
} }
// Deprecated, this isn't how we want to do it func (c *dynamicResourceClient) Namespace(ns string) dynamic.ResourceInterface {
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 {
ret := *c ret := *c
ret.namespace = ns ret.namespace = ns
return &ret return &ret
} }
func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) {
uncastRet, err := c.client.Fake. var uncastRet runtime.Object
Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj) 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 { if err != nil {
return nil, err return nil, err
@@ -109,9 +128,27 @@ func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstruc
return ret, err return ret, err
} }
func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) {
uncastRet, err := c.client.Fake. var uncastRet runtime.Object
Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj) 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 { if err != nil {
return nil, err 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) { func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
uncastRet, err := c.client.Fake. var uncastRet runtime.Object
Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj) 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 { if err != nil {
return nil, err return nil, err
@@ -145,23 +192,65 @@ func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*u
return ret, err return ret, err
} }
func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions) error { func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions, subresources ...string) error {
_, err := c.client.Fake. var err error
Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic delete fail"}) 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 return err
} }
func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error { 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 return err
} }
func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
uncastRet, err := c.client.Fake. var uncastRet runtime.Object
Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic get fail"}) 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 { if err != nil {
return nil, err 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) { func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
obj, err := c.client.Fake. var obj runtime.Object
Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Version: "v1", Kind: "List"}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"}) 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 { if obj == nil {
return nil, err 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) { func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
return c.client.Fake. switch {
InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts)) 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) { func (c *dynamicResourceClient) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error) {
uncastRet, err := c.client.Fake. var uncastRet runtime.Object
Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, data, subresources...), &metav1.Status{Status: "dynamic patch fail"}) 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 { if err != nil {
return nil, err return nil, err

View File

@@ -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"
}

View File

@@ -17,7 +17,6 @@ limitations under the License.
package dynamic package dynamic
import ( import (
"fmt"
"io" "io"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@@ -31,39 +30,13 @@ import (
"k8s.io/client-go/rest" "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 { type dynamicClient struct {
client *rest.RESTClient 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) config := rest.CopyConfig(inConfig)
// for serializing the options // for serializing the options
config.GroupVersion = &schema.GroupVersion{} config.GroupVersion = &schema.GroupVersion{}
@@ -84,40 +57,36 @@ func NewForConfig(inConfig *rest.Config) (DynamicInterface, error) {
} }
type dynamicResourceClient struct { type dynamicResourceClient struct {
client *dynamicClient client *dynamicClient
namespace string namespace string
resource schema.GroupVersionResource resource schema.GroupVersionResource
subresource string
} }
func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableDynamicResourceInterface { func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface {
return &dynamicResourceClient{client: c, resource: resource} return &dynamicResourceClient{client: c, resource: resource}
} }
func (c *dynamicClient) ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface { func (c *dynamicResourceClient) Namespace(ns string) ResourceInterface {
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 {
ret := *c ret := *c
ret.namespace = ns ret.namespace = ns
return &ret return &ret
} }
func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error) {
if len(c.subresource) > 0 {
return nil, fmt.Errorf("create not supported for subresources")
}
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj) outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil { if err != nil {
return nil, err 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 { if err := result.Error(); err != nil {
return nil, err return nil, err
} }
@@ -133,7 +102,7 @@ func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstruc
return uncastObj.(*unstructured.Unstructured), nil 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) accessor, err := meta.Accessor(obj)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -143,7 +112,7 @@ func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured) (*unstruc
return nil, err 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 { if err := result.Error(); err != nil {
return nil, err 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() 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() retBytes, err := result.Raw()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -182,7 +155,7 @@ func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*u
return uncastObj.(*unstructured.Unstructured), nil 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 { if opts == nil {
opts = &metav1.DeleteOptions{} opts = &metav1.DeleteOptions{}
} }
@@ -191,15 +164,11 @@ func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions)
return err 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() return result.Error()
} }
func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) 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 { if opts == nil {
opts = &metav1.DeleteOptions{} opts = &metav1.DeleteOptions{}
} }
@@ -212,8 +181,8 @@ func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, lis
return result.Error() return result.Error()
} }
func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) { func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
result := c.client.client.Get().AbsPath(c.makeURLSegments(name)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do() result := c.client.client.Get().AbsPath(append(c.makeURLSegments(name), subresources...)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do()
if err := result.Error(); err != nil { if err := result.Error(); err != nil {
return nil, err 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) { 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() result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do()
if err := result.Error(); err != nil { if err := result.Error(); err != nil {
return nil, err 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) { 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{ internalGV := schema.GroupVersions{
{Group: c.resource.Group, Version: runtime.APIVersionInternal}, {Group: c.resource.Group, Version: runtime.APIVersionInternal},
// always include the legacy group as a decoding target to handle non-error `Status` return types // 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 { if len(name) > 0 {
url = append(url, name) 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 return url

View File

@@ -225,6 +225,16 @@ func NewRootDeleteAction(resource schema.GroupVersionResource, name string) Dele
return action 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 { func NewDeleteAction(resource schema.GroupVersionResource, namespace, name string) DeleteActionImpl {
action := DeleteActionImpl{} action := DeleteActionImpl{}
action.Verb = "delete" action.Verb = "delete"
@@ -235,6 +245,17 @@ func NewDeleteAction(resource schema.GroupVersionResource, namespace, name strin
return action 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 { func NewRootDeleteCollectionAction(resource schema.GroupVersionResource, opts interface{}) DeleteCollectionActionImpl {
action := DeleteCollectionActionImpl{} action := DeleteCollectionActionImpl{}
action.Verb = "delete-collection" action.Verb = "delete-collection"

View File

@@ -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( return crdResourceClient.Watch(
metav1.ListOptions{ metav1.ListOptions{
FieldSelector: "metadata.name=" + name, 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) createdInstance, err := client.Create(instanceToCreate)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -150,11 +150,11 @@ func instantiateCustomResource(instanceToCreate *unstructured.Unstructured, clie
return createdInstance, nil 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{}) 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} gvr := schema.GroupVersionResource{Group: crd.Spec.Group, Version: crd.Spec.Version, Resource: crd.Spec.Names.Plural}
if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped { if crd.Spec.Scope != apiextensionsv1beta1.ClusterScoped {

View File

@@ -1081,7 +1081,7 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, context *c
return func() { client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(configName, nil) } 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") By("Creating a custom resource that should be denied by the webhook")
crInstance := &unstructured.Unstructured{ crInstance := &unstructured.Unstructured{
Object: map[string]interface{}{ 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") By("Creating a custom resource that should be mutated by the webhook")
cr := &unstructured.Unstructured{ cr := &unstructured.Unstructured{
Object: map[string]interface{}{ Object: map[string]interface{}{

View File

@@ -38,7 +38,7 @@ type TestCrd struct {
ApiVersion string ApiVersion string
ApiExtensionClient *crdclientset.Clientset ApiExtensionClient *crdclientset.Clientset
Crd *apiextensionsv1beta1.CustomResourceDefinition Crd *apiextensionsv1beta1.CustomResourceDefinition
DynamicClient dynamic.DynamicResourceInterface DynamicClient dynamic.ResourceInterface
CleanUp CleanCrdFn CleanUp CleanCrdFn
} }

View File

@@ -70,7 +70,7 @@ type Framework struct {
InternalClientset *internalclientset.Clientset InternalClientset *internalclientset.Clientset
AggregatorClient *aggregatorclient.Clientset AggregatorClient *aggregatorclient.Clientset
DynamicClient dynamic.DynamicInterface DynamicClient dynamic.Interface
ScalesGetter scaleclient.ScalesGetter ScalesGetter scaleclient.ScalesGetter

View File

@@ -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) resourceClient := dynamicClient.Resource(gvr).Namespace(namespace)
_, err := resourceClient.List(metav1.ListOptions{}) _, err := resourceClient.List(metav1.ListOptions{})
if err != nil { 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 // 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. // 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() startTime := time.Now()
if err := c.CoreV1().Namespaces().Delete(namespace, nil); err != nil { if err := c.CoreV1().Namespaces().Delete(namespace, nil); err != nil {
return err return err
@@ -1232,7 +1232,7 @@ func isDynamicDiscoveryError(err error) bool {
} }
// hasRemainingContent checks if there is remaining content in the namespace via API discovery // 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 // some tests generate their own framework.Client rather than the default
// TODO: ensure every test call has a configured dynamicClient // TODO: ensure every test call has a configured dynamicClient
if dynamicClient == nil { if dynamicClient == nil {

View File

@@ -46,29 +46,12 @@ func TestDynamicClient(t *testing.T) {
} }
client := clientset.NewForConfigOrDie(config) client := clientset.NewForConfigOrDie(config)
dynamicClient, err := dynamic.NewClient(config, *gv) dynamicClient, err := dynamic.NewForConfig(config)
_ = dynamicClient
if err != nil { if err != nil {
t.Fatalf("unexpected error creating dynamic client: %v", err) t.Fatalf("unexpected error creating dynamic client: %v", err)
} }
// Find the Pod resource resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
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())
}
// Create a Pod with the normal client // Create a Pod with the normal client
pod := &v1.Pod{ pod := &v1.Pod{
@@ -91,11 +74,7 @@ func TestDynamicClient(t *testing.T) {
} }
// check dynamic list // check dynamic list
obj, err := dynamicClient.Resource(&resource, ns.Name).List(metav1.ListOptions{}) unstructuredList, err := dynamicClient.Resource(resource).Namespace(ns.Name).List(metav1.ListOptions{})
unstructuredList, ok := obj.(*unstructured.UnstructuredList)
if !ok {
t.Fatalf("expected *unstructured.UnstructuredList, got %#v", obj)
}
if err != nil { if err != nil {
t.Fatalf("unexpected error when listing pods: %v", err) t.Fatalf("unexpected error when listing pods: %v", err)
} }
@@ -114,7 +93,7 @@ func TestDynamicClient(t *testing.T) {
} }
// check dynamic get // 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 { if err != nil {
t.Fatalf("unexpected error when getting pod %q: %v", actual.Name, err) 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 // 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 { if err != nil {
t.Fatalf("unexpected error when deleting pod: %v", err) t.Fatalf("unexpected error when deleting pod: %v", err)
} }

View File

@@ -168,9 +168,9 @@ func link(t *testing.T, owner, dependent metav1.Object) {
func createRandomCustomResourceDefinition( func createRandomCustomResourceDefinition(
t *testing.T, apiExtensionClient apiextensionsclientset.Interface, t *testing.T, apiExtensionClient apiextensionsclientset.Interface,
dynamicClient dynamic.DynamicInterface, dynamicClient dynamic.Interface,
namespace string, namespace string,
) (*apiextensionsv1beta1.CustomResourceDefinition, dynamic.DynamicResourceInterface) { ) (*apiextensionsv1beta1.CustomResourceDefinition, dynamic.ResourceInterface) {
// Create a random custom resource definition and ensure it's available for // Create a random custom resource definition and ensure it's available for
// use. // use.
definition := apiextensionstestserver.NewRandomNameCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped) definition := apiextensionstestserver.NewRandomNameCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
@@ -193,7 +193,7 @@ type testContext struct {
gc *garbagecollector.GarbageCollector gc *garbagecollector.GarbageCollector
clientSet clientset.Interface clientSet clientset.Interface
apiExtensionClient apiextensionsclientset.Interface apiExtensionClient apiextensionsclientset.Interface
dynamicClient dynamic.DynamicInterface dynamicClient dynamic.Interface
startGC func(workers int) startGC func(workers int)
// syncPeriod is how often the GC started with startGC will be resynced. // syncPeriod is how often the GC started with startGC will be resynced.
syncPeriod time.Duration syncPeriod time.Duration

View File

@@ -150,14 +150,12 @@ func TestCRD(t *testing.T) {
} }
t.Logf("Trying to access foos.cr.bar.com with dynamic client") t.Logf("Trying to access foos.cr.bar.com with dynamic client")
barComConfig := *result.ClientConfig dynamicClient, err := dynamic.NewForConfig(result.ClientConfig)
barComConfig.GroupVersion = &schema.GroupVersion{Group: "cr.bar.com", Version: "v1"}
barComConfig.APIPath = "/apis"
barComClient, err := dynamic.NewClient(&barComConfig, *barComConfig.GroupVersion)
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) 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 { if err != nil {
t.Errorf("Failed to list foos.cr.bar.com instances: %v", err) 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) createErr := make(chan error, 1)
go func() { 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) t.Logf("Foo instance create returned: %v", err)
if err != nil { if err != nil {
createErr <- err createErr <- err
@@ -216,7 +214,7 @@ func TestCRD(t *testing.T) {
} }
t.Logf("Checking that Foo instance is visible with IncludeUninitialized=true") 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, IncludeUninitialized: true,
}) })
switch { switch {
@@ -237,7 +235,7 @@ func TestCRD(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
// would love to replace the following with a patch, but removing strings from the intitializer array // 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. // 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, IncludeUninitialized: true,
}) })
if err != nil { if err != nil {
@@ -274,7 +272,7 @@ func TestCRD(t *testing.T) {
} }
fooUnstructured.UnmarshalJSON(bs) 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) { if err != nil && !errors.IsConflict(err) {
t.Fatalf("Failed to update Foo instance: %v", err) t.Fatalf("Failed to update Foo instance: %v", err)
} else if err == nil { } 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") 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) t.Errorf("Unexpected error: %v", err)
} }
} }