diff --git a/staging/src/k8s.io/apimachinery/pkg/runtime/negotiate.go b/staging/src/k8s.io/apimachinery/pkg/runtime/negotiate.go index 159b301206a..3ab119b0a8b 100644 --- a/staging/src/k8s.io/apimachinery/pkg/runtime/negotiate.go +++ b/staging/src/k8s.io/apimachinery/pkg/runtime/negotiate.go @@ -92,39 +92,6 @@ func NewClientNegotiator(serializer NegotiatedSerializer, gv schema.GroupVersion } } -// NewInternalClientNegotiator applies the default client rules for connecting to a Kubernetes apiserver -// where objects are converted to gv prior to sending and decoded to their internal representation prior -// to retrieval. -// -// DEPRECATED: Internal clients are deprecated and will be removed in a future Kubernetes release. -func NewInternalClientNegotiator(serializer NegotiatedSerializer, gv schema.GroupVersion) ClientNegotiator { - decode := schema.GroupVersions{ - { - Group: gv.Group, - Version: APIVersionInternal, - }, - // always include the legacy group as a decoding target to handle non-error `Status` return types - { - Group: "", - Version: APIVersionInternal, - }, - } - return &clientNegotiator{ - encode: gv, - decode: decode, - serializer: serializer, - } -} - -// NewSimpleClientNegotiator will negotiate for a single serializer. This should only be used -// for testing or when the caller is taking responsibility for setting the GVK on encoded objects. -func NewSimpleClientNegotiator(info SerializerInfo, gv schema.GroupVersion) ClientNegotiator { - return &clientNegotiator{ - serializer: &simpleNegotiatedSerializer{info: info}, - encode: gv, - } -} - type simpleNegotiatedSerializer struct { info SerializerInfo } diff --git a/staging/src/k8s.io/client-go/dynamic/dynamicinformer/informer_test.go b/staging/src/k8s.io/client-go/dynamic/dynamicinformer/informer_test.go index b3b10ef7e3f..98acdb40fc0 100644 --- a/staging/src/k8s.io/client-go/dynamic/dynamicinformer/informer_test.go +++ b/staging/src/k8s.io/client-go/dynamic/dynamicinformer/informer_test.go @@ -95,7 +95,12 @@ func TestFilteredDynamicSharedInformerFactory(t *testing.T) { if ts.existingObj != nil { objs = append(objs, ts.existingObj) } - fakeClient := fake.NewSimpleDynamicClient(scheme, objs...) + // don't adjust the scheme to include deploymentlist. This is testing whether an informer can be created against using + // a client that doesn't have a type registered in the scheme. + gvrToListKind := map[schema.GroupVersionResource]string{ + {Group: "apps", Version: "v1", Resource: "deployments"}: "DeploymentList", + } + fakeClient := fake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind, objs...) target := dynamicinformer.NewFilteredDynamicSharedInformerFactory(fakeClient, 0, ts.informNS, nil) // act @@ -214,7 +219,12 @@ func TestDynamicSharedInformerFactory(t *testing.T) { if ts.existingObj != nil { objs = append(objs, ts.existingObj) } - fakeClient := fake.NewSimpleDynamicClient(scheme, objs...) + // don't adjust the scheme to include deploymentlist. This is testing whether an informer can be created against using + // a client that doesn't have a type registered in the scheme. + gvrToListKind := map[schema.GroupVersionResource]string{ + {Group: "extensions", Version: "v1beta1", Resource: "deployments"}: "DeploymentList", + } + fakeClient := fake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind, objs...) target := dynamicinformer.NewDynamicSharedInformerFactory(fakeClient, 0) // act diff --git a/staging/src/k8s.io/client-go/dynamic/fake/simple.go b/staging/src/k8s.io/client-go/dynamic/fake/simple.go index 001780970bb..622995776a5 100644 --- a/staging/src/k8s.io/client-go/dynamic/fake/simple.go +++ b/staging/src/k8s.io/client-go/dynamic/fake/simple.go @@ -18,6 +18,7 @@ package fake import ( "context" + "fmt" "strings" "k8s.io/apimachinery/pkg/api/meta" @@ -34,11 +35,45 @@ import ( ) func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeDynamicClient { - // In order to use List with this client, you have to have the v1.List registered in your scheme. Neat thing though - // it does NOT have to be the *same* list. UnstructuredList returned from this fake client will NOT have apiVersion and kind set, - // but each Unstructured object in Items will preserve their respective apiVersion and kind. As a result, schema conversion for - // *List kinds will not work and conversion of each Unstructured object in Items will be required instead. - scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "List"}, &unstructured.UnstructuredList{}) + return NewSimpleDynamicClientWithCustomListKinds(scheme, nil, objects...) +} + +// NewSimpleDynamicClientWithCustomListKinds try not to use this. In general you want to have the scheme have the List types registered +// and allow the default guessing for resources match. Sometimes that doesn't work, so you can specify a custom mapping here. +func NewSimpleDynamicClientWithCustomListKinds(scheme *runtime.Scheme, gvrToListKind map[schema.GroupVersionResource]string, objects ...runtime.Object) *FakeDynamicClient { + // In order to use List with this client, you have to have your lists registered so that the object tracker will find them + // in the scheme to support the t.scheme.New(listGVK) call when it's building the return value. + // Since the base fake client needs the listGVK passed through the action (in cases where there are no instances, it + // cannot look up the actual hits), we need to know a mapping of GVR to listGVK here. For GETs and other types of calls, + // there is no return value that contains a GVK, so it doesn't have to know the mapping in advance. + + // first we attempt to invert known List types from the scheme to auto guess the resource with unsafe guesses + // this covers common usage of registering types in scheme and passing them + completeGVRToListKind := map[schema.GroupVersionResource]string{} + for listGVK := range scheme.AllKnownTypes() { + if !strings.HasSuffix(listGVK.Kind, "List") { + continue + } + nonListGVK := listGVK.GroupVersion().WithKind(listGVK.Kind[:len(listGVK.Kind)-4]) + plural, _ := meta.UnsafeGuessKindToResource(nonListGVK) + completeGVRToListKind[plural] = listGVK.Kind + } + + for gvr, listKind := range gvrToListKind { + if !strings.HasSuffix(listKind, "List") { + panic("coding error, listGVK must end in List or this fake client doesn't work right") + } + listGVK := gvr.GroupVersion().WithKind(listKind) + + // if we already have this type registered, just skip it + if _, err := scheme.New(listGVK); err == nil { + completeGVRToListKind[gvr] = listKind + continue + } + + scheme.AddKnownTypeWithName(listGVK, &unstructured.UnstructuredList{}) + completeGVRToListKind[gvr] = listKind + } codecs := serializer.NewCodecFactory(scheme) o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) @@ -48,7 +83,7 @@ func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) * } } - cs := &FakeDynamicClient{scheme: scheme} + cs := &FakeDynamicClient{scheme: scheme, gvrToListKind: completeGVRToListKind} cs.AddReactor("*", "*", testing.ObjectReaction(o)) cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { gvr := action.GetResource() @@ -68,19 +103,21 @@ func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) * // you want to test easier. type FakeDynamicClient struct { testing.Fake - scheme *runtime.Scheme + scheme *runtime.Scheme + gvrToListKind map[schema.GroupVersionResource]string } type dynamicResourceClient struct { client *FakeDynamicClient namespace string resource schema.GroupVersionResource + listKind string } var _ dynamic.Interface = &FakeDynamicClient{} func (c *FakeDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface { - return &dynamicResourceClient{client: c, resource: resource} + return &dynamicResourceClient{client: c, resource: resource, listKind: c.gvrToListKind[resource]} } func (c *dynamicResourceClient) Namespace(ns string) dynamic.ResourceInterface { @@ -276,16 +313,22 @@ func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav } func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) { + if len(c.listKind) == 0 { + panic(fmt.Sprintf("coding error: you must register resource to list kind for every resource you're going to LIST when creating the client. See NewSimpleDynamicClientWithCustomListKinds or register the list into the scheme: %v out of %v", c.resource, c.client.gvrToListKind)) + } + listGVK := c.resource.GroupVersion().WithKind(c.listKind) + listForFakeClientGVK := c.resource.GroupVersion().WithKind(c.listKind[:len(c.listKind)-4]) /*base library appends List*/ + var obj runtime.Object var err error switch { case len(c.namespace) == 0: obj, err = c.client.Fake. - Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, opts), &metav1.Status{Status: "dynamic list fail"}) + Invokes(testing.NewRootListAction(c.resource, listForFakeClientGVK, opts), &metav1.Status{Status: "dynamic list fail"}) case len(c.namespace) > 0: obj, err = c.client.Fake. - Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"}) + Invokes(testing.NewListAction(c.resource, listForFakeClientGVK, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"}) } @@ -309,6 +352,7 @@ func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOption list := &unstructured.UnstructuredList{} list.SetResourceVersion(entireList.GetResourceVersion()) + list.GetObjectKind().SetGroupVersionKind(listGVK) for i := range entireList.Items { item := &entireList.Items[i] metadata, err := meta.Accessor(item) diff --git a/staging/src/k8s.io/client-go/dynamic/fake/simple_test.go b/staging/src/k8s.io/client-go/dynamic/fake/simple_test.go index 33037c3c597..c7f8c1af3c9 100644 --- a/staging/src/k8s.io/client-go/dynamic/fake/simple_test.go +++ b/staging/src/k8s.io/client-go/dynamic/fake/simple_test.go @@ -59,10 +59,74 @@ func newUnstructuredWithSpec(spec map[string]interface{}) *unstructured.Unstruct return u } +func TestGet(t *testing.T) { + scheme := runtime.NewScheme() + + client := NewSimpleDynamicClient(scheme, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")) + get, err := client.Resource(schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"}).Namespace("ns-foo").Get(context.TODO(), "name-foo", metav1.GetOptions{}) + if err != nil { + t.Fatal(err) + } + + expected := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "group/version", + "kind": "TheKind", + "metadata": map[string]interface{}{ + "name": "name-foo", + "namespace": "ns-foo", + }, + }, + } + if !equality.Semantic.DeepEqual(get, expected) { + t.Fatal(diff.ObjectGoPrintDiff(expected, get)) + } +} + +func TestListDecoding(t *testing.T) { + // this the duplication of logic from the real List API. This will prove that our dynamic client actually returns the gvk + uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, []byte(`{"apiVersion": "group/version", "kind": "TheKindList", "items":[]}`)) + if err != nil { + t.Fatal(err) + } + list := uncastObj.(*unstructured.UnstructuredList) + expectedList := &unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "apiVersion": "group/version", + "kind": "TheKindList", + }, + Items: []unstructured.Unstructured{}, + } + if !equality.Semantic.DeepEqual(list, expectedList) { + t.Fatal(diff.ObjectGoPrintDiff(expectedList, list)) + } +} + +func TestGetDecoding(t *testing.T) { + // this the duplication of logic from the real Get API. This will prove that our dynamic client actually returns the gvk + uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, []byte(`{"apiVersion": "group/version", "kind": "TheKind"}`)) + if err != nil { + t.Fatal(err) + } + get := uncastObj.(*unstructured.Unstructured) + expectedObj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "group/version", + "kind": "TheKind", + }, + } + if !equality.Semantic.DeepEqual(get, expectedObj) { + t.Fatal(diff.ObjectGoPrintDiff(expectedObj, get)) + } +} + func TestList(t *testing.T) { scheme := runtime.NewScheme() - client := NewSimpleDynamicClient(scheme, + client := NewSimpleDynamicClientWithCustomListKinds(scheme, + map[schema.GroupVersionResource]string{ + {Group: "group", Version: "version", Resource: "thekinds"}: "TheKindList", + }, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"), newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"), @@ -84,6 +148,49 @@ func TestList(t *testing.T) { } } +func Test_ListKind(t *testing.T) { + scheme := runtime.NewScheme() + + client := NewSimpleDynamicClientWithCustomListKinds(scheme, + map[schema.GroupVersionResource]string{ + {Group: "group", Version: "version", Resource: "thekinds"}: "TheKindList", + }, + &unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "apiVersion": "group/version", + "kind": "TheKindList", + }, + Items: []unstructured.Unstructured{ + *newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), + *newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"), + *newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"), + }, + }, + ) + listFirst, err := client.Resource(schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"}).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + t.Fatal(err) + } + + expectedList := &unstructured.UnstructuredList{ + Object: map[string]interface{}{ + "apiVersion": "group/version", + "kind": "TheKindList", + "metadata": map[string]interface{}{ + "resourceVersion": "", + }, + }, + Items: []unstructured.Unstructured{ + *newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"), + *newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"), + *newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), + }, + } + if !equality.Semantic.DeepEqual(listFirst, expectedList) { + t.Fatal(diff.ObjectGoPrintDiff(expectedList, listFirst)) + } +} + type patchTestCase struct { name string object runtime.Object diff --git a/staging/src/k8s.io/client-go/kubernetes_test/BUILD b/staging/src/k8s.io/client-go/kubernetes_test/BUILD index fcabd03f90c..e0c21abe262 100644 --- a/staging/src/k8s.io/client-go/kubernetes_test/BUILD +++ b/staging/src/k8s.io/client-go/kubernetes_test/BUILD @@ -2,11 +2,18 @@ load("@io_bazel_rules_go//go:def.bzl", "go_test") go_test( name = "go_default_test", - srcs = ["timeout_test.go"], + srcs = [ + "fake_client_test.go", + "timeout_test.go", + ], deps = [ "//staging/src/k8s.io/api/apps/v1:go_default_library", + "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", + "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", "//staging/src/k8s.io/client-go/rest:go_default_library", "//staging/src/k8s.io/client-go/rest/fake:go_default_library", diff --git a/staging/src/k8s.io/client-go/kubernetes_test/fake_client_test.go b/staging/src/k8s.io/client-go/kubernetes_test/fake_client_test.go new file mode 100644 index 00000000000..cf0c50272f3 --- /dev/null +++ b/staging/src/k8s.io/client-go/kubernetes_test/fake_client_test.go @@ -0,0 +1,93 @@ +/* +Copyright 2020 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 kubernetes + +import ( + "context" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/kubernetes/scheme" +) + +// This test proves that the kube fake client does not return GVKs. This is consistent with actual client (see tests below) +// and should not be changed unless the decoding behavior and somehow literal creation (`&corev1.ConfigMap{}`) behavior change. +func Test_ConfigMapFakeClient(t *testing.T) { + fakeKubeClient := fake.NewSimpleClientset(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Namespace: "foo-ns", Name: "foo-name"}}) + cm, err := fakeKubeClient.CoreV1().ConfigMaps("foo-ns").Get(context.TODO(), "foo-name", metav1.GetOptions{}) + if err != nil { + t.Fatal(err) + } + if cm.GetObjectKind().GroupVersionKind() != (schema.GroupVersionKind{}) { + t.Fatal(cm.GetObjectKind().GroupVersionKind()) + } + cmList, err := fakeKubeClient.CoreV1().ConfigMaps("foo-ns").List(context.TODO(), metav1.ListOptions{}) + if err != nil { + t.Fatal(err) + } + if cmList.GetObjectKind().GroupVersionKind() != (schema.GroupVersionKind{}) { + t.Fatal(cmList.GetObjectKind().GroupVersionKind()) + } +} + +// This test checks decoding behavior for the actual client to ensure the fake client (tested above) is consistent. +func TestGetDecoding(t *testing.T) { + // this the duplication of logic from the real Get API for configmaps. This will prove that the generated client will not return a GVK + mediaTypes := scheme.Codecs.WithoutConversion().SupportedMediaTypes() + info, ok := runtime.SerializerInfoForMediaType(mediaTypes, "application/json") + if !ok { + t.Fatal("missing serializer") + } + decoder := scheme.Codecs.WithoutConversion().DecoderToVersion(info.Serializer, corev1.SchemeGroupVersion) + + body := []byte(`{"apiVersion": "v1", "kind": "ConfigMap", "metadata":{"Namespace":"foo","Name":"bar"}}`) + obj := &corev1.ConfigMap{} + out, _, err := decoder.Decode(body, nil, obj) + if err != nil || out != obj { + t.Fatal(err) + } + + if obj.GetObjectKind().GroupVersionKind() != (schema.GroupVersionKind{}) { + t.Fatal(obj.GetObjectKind().GroupVersionKind()) + } +} + +// This test checks decoding behavior for the actual client to ensure the fake client (tested above) is consistent. +func TestListDecoding(t *testing.T) { + // this the duplication of logic from the real Get API for configmaps. This will prove that the generated client will not return a GVK + mediaTypes := scheme.Codecs.WithoutConversion().SupportedMediaTypes() + info, ok := runtime.SerializerInfoForMediaType(mediaTypes, "application/json") + if !ok { + t.Fatal("missing serializer") + } + decoder := scheme.Codecs.WithoutConversion().DecoderToVersion(info.Serializer, corev1.SchemeGroupVersion) + + body := []byte(`{"apiVersion": "v1", "kind": "ConfigMapList", "items":[]}`) + obj := &corev1.ConfigMapList{} + out, _, err := decoder.Decode(body, nil, obj) + if err != nil || out != obj { + t.Fatal(err) + } + + if obj.GetObjectKind().GroupVersionKind() != (schema.GroupVersionKind{}) { + t.Fatal(obj.GetObjectKind().GroupVersionKind()) + } +} diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/wait/wait_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/wait/wait_test.go index c9312fae31b..f347ea25737 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/wait/wait_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/wait/wait_test.go @@ -82,6 +82,11 @@ func addCondition(in *unstructured.Unstructured, name, status string) *unstructu func TestWaitForDeletion(t *testing.T) { scheme := runtime.NewScheme() + listMapping := map[schema.GroupVersionResource]string{ + {Group: "group", Version: "version", Resource: "theresource"}: "TheKindList", + {Group: "group", Version: "version", Resource: "theresource-1"}: "TheKindList", + {Group: "group", Version: "version", Resource: "theresource-2"}: "TheKindList", + } tests := []struct { name string @@ -105,7 +110,7 @@ func TestWaitForDeletion(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - return dynamicfakeclient.NewSimpleDynamicClient(scheme) + return dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) }, timeout: 10 * time.Second, @@ -145,7 +150,7 @@ func TestWaitForDeletion(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil }) @@ -192,7 +197,7 @@ func TestWaitForDeletion(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil }) @@ -225,7 +230,7 @@ func TestWaitForDeletion(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { unstructuredObj := newUnstructured("group/version", "TheKind", "ns-foo", "name-foo") unstructuredObj.SetResourceVersion("123") @@ -282,7 +287,7 @@ func TestWaitForDeletion(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil }) @@ -326,7 +331,7 @@ func TestWaitForDeletion(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("get", "theresource-1", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, newUnstructured("group/version", "TheKind", "ns-foo", "name-foo-1"), nil }) @@ -371,7 +376,7 @@ func TestWaitForDeletion(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil }) @@ -449,6 +454,9 @@ func TestWaitForDeletion(t *testing.T) { func TestWaitForCondition(t *testing.T) { scheme := runtime.NewScheme() + listMapping := map[schema.GroupVersionResource]string{ + {Group: "group", Version: "version", Resource: "theresource"}: "TheKindList", + } tests := []struct { name string @@ -471,7 +479,7 @@ func TestWaitForCondition(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, newUnstructuredList(addCondition( newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), @@ -517,7 +525,7 @@ func TestWaitForCondition(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - return dynamicfakeclient.NewSimpleDynamicClient(scheme) + return dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) }, timeout: 10 * time.Second, expectedErr: "resource name must be provided", @@ -540,7 +548,7 @@ func TestWaitForCondition(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, addCondition( newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), @@ -576,7 +584,7 @@ func TestWaitForCondition(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { unstructuredObj := newUnstructured("group/version", "TheKind", "ns-foo", "name-foo") unstructuredObj.SetResourceVersion("123") @@ -633,7 +641,7 @@ func TestWaitForCondition(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil }) @@ -673,7 +681,7 @@ func TestWaitForCondition(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) { fakeWatch := watch.NewRaceFreeFake() fakeWatch.Action(watch.Added, addCondition( @@ -710,7 +718,7 @@ func TestWaitForCondition(t *testing.T) { }, }, fakeClient: func() *dynamicfakeclient.FakeDynamicClient { - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) { return true, newUnstructuredList(newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")), nil }) @@ -790,6 +798,9 @@ func TestWaitForCondition(t *testing.T) { func TestWaitForDeletionIgnoreNotFound(t *testing.T) { scheme := runtime.NewScheme() + listMapping := map[schema.GroupVersionResource]string{ + {Group: "group", Version: "version", Resource: "theresource"}: "TheKindList", + } infos := []*resource.Info{ { Mapping: &meta.RESTMapping{ @@ -799,7 +810,7 @@ func TestWaitForDeletionIgnoreNotFound(t *testing.T) { Namespace: "ns-foo", }, } - fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) + fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping) o := &WaitOptions{ ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(infos...),