Merge pull request #96020 from deads2k/dynamiclist

update fake dynamic client to return GVK
This commit is contained in:
Kubernetes Prow Robot 2020-11-04 18:11:06 -08:00 committed by GitHub
commit dbd2be08fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 301 additions and 62 deletions

View File

@ -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 { type simpleNegotiatedSerializer struct {
info SerializerInfo info SerializerInfo
} }

View File

@ -95,7 +95,12 @@ func TestFilteredDynamicSharedInformerFactory(t *testing.T) {
if ts.existingObj != nil { if ts.existingObj != nil {
objs = append(objs, ts.existingObj) 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) target := dynamicinformer.NewFilteredDynamicSharedInformerFactory(fakeClient, 0, ts.informNS, nil)
// act // act
@ -214,7 +219,12 @@ func TestDynamicSharedInformerFactory(t *testing.T) {
if ts.existingObj != nil { if ts.existingObj != nil {
objs = append(objs, ts.existingObj) 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) target := dynamicinformer.NewDynamicSharedInformerFactory(fakeClient, 0)
// act // act

View File

@ -18,6 +18,7 @@ package fake
import ( import (
"context" "context"
"fmt"
"strings" "strings"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@ -34,11 +35,45 @@ import (
) )
func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeDynamicClient { 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 return NewSimpleDynamicClientWithCustomListKinds(scheme, nil, objects...)
// 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. // NewSimpleDynamicClientWithCustomListKinds try not to use this. In general you want to have the scheme have the List types registered
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "List"}, &unstructured.UnstructuredList{}) // 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) codecs := serializer.NewCodecFactory(scheme)
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 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.AddReactor("*", "*", testing.ObjectReaction(o))
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
gvr := action.GetResource() gvr := action.GetResource()
@ -68,19 +103,21 @@ func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *
// you want to test easier. // you want to test easier.
type FakeDynamicClient struct { type FakeDynamicClient struct {
testing.Fake testing.Fake
scheme *runtime.Scheme scheme *runtime.Scheme
gvrToListKind map[schema.GroupVersionResource]string
} }
type dynamicResourceClient struct { type dynamicResourceClient struct {
client *FakeDynamicClient client *FakeDynamicClient
namespace string namespace string
resource schema.GroupVersionResource resource schema.GroupVersionResource
listKind string
} }
var _ dynamic.Interface = &FakeDynamicClient{} var _ dynamic.Interface = &FakeDynamicClient{}
func (c *FakeDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface { 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 { 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) { 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 obj runtime.Object
var err error var err error
switch { switch {
case len(c.namespace) == 0: case len(c.namespace) == 0:
obj, err = c.client.Fake. 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: case len(c.namespace) > 0:
obj, err = c.client.Fake. 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 := &unstructured.UnstructuredList{}
list.SetResourceVersion(entireList.GetResourceVersion()) list.SetResourceVersion(entireList.GetResourceVersion())
list.GetObjectKind().SetGroupVersionKind(listGVK)
for i := range entireList.Items { for i := range entireList.Items {
item := &entireList.Items[i] item := &entireList.Items[i]
metadata, err := meta.Accessor(item) metadata, err := meta.Accessor(item)

View File

@ -59,10 +59,74 @@ func newUnstructuredWithSpec(spec map[string]interface{}) *unstructured.Unstruct
return u 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) { func TestList(t *testing.T) {
scheme := runtime.NewScheme() 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("group/version", "TheKind", "ns-foo", "name-foo"),
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"), newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"),
newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"), 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 { type patchTestCase struct {
name string name string
object runtime.Object object runtime.Object

View File

@ -2,11 +2,18 @@ load("@io_bazel_rules_go//go:def.bzl", "go_test")
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = ["timeout_test.go"], srcs = [
"fake_client_test.go",
"timeout_test.go",
],
deps = [ deps = [
"//staging/src/k8s.io/api/apps/v1:go_default_library", "//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/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: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/kubernetes/scheme:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library", "//staging/src/k8s.io/client-go/rest:go_default_library",
"//staging/src/k8s.io/client-go/rest/fake:go_default_library", "//staging/src/k8s.io/client-go/rest/fake:go_default_library",

View File

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

View File

@ -82,6 +82,11 @@ func addCondition(in *unstructured.Unstructured, name, status string) *unstructu
func TestWaitForDeletion(t *testing.T) { func TestWaitForDeletion(t *testing.T) {
scheme := runtime.NewScheme() 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 { tests := []struct {
name string name string
@ -105,7 +110,7 @@ func TestWaitForDeletion(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
return dynamicfakeclient.NewSimpleDynamicClient(scheme) return dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping)
}, },
timeout: 10 * time.Second, timeout: 10 * time.Second,
@ -145,7 +150,7 @@ func TestWaitForDeletion(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { 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) { 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 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: 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) { 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 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: 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) { 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 := newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")
unstructuredObj.SetResourceVersion("123") unstructuredObj.SetResourceVersion("123")
@ -282,7 +287,7 @@ func TestWaitForDeletion(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { 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) { 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 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: 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) { 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 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: 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) { 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 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) { func TestWaitForCondition(t *testing.T) {
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
listMapping := map[schema.GroupVersionResource]string{
{Group: "group", Version: "version", Resource: "theresource"}: "TheKindList",
}
tests := []struct { tests := []struct {
name string name string
@ -471,7 +479,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { 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) { fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
return true, newUnstructuredList(addCondition( return true, newUnstructuredList(addCondition(
newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
@ -517,7 +525,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { fakeClient: func() *dynamicfakeclient.FakeDynamicClient {
return dynamicfakeclient.NewSimpleDynamicClient(scheme) return dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping)
}, },
timeout: 10 * time.Second, timeout: 10 * time.Second,
expectedErr: "resource name must be provided", expectedErr: "resource name must be provided",
@ -540,7 +548,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { 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) { fakeClient.PrependReactor("list", "theresource", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
return true, addCondition( return true, addCondition(
newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"), newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
@ -576,7 +584,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { 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) { 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 := newUnstructured("group/version", "TheKind", "ns-foo", "name-foo")
unstructuredObj.SetResourceVersion("123") unstructuredObj.SetResourceVersion("123")
@ -633,7 +641,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { 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) { 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 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: 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) { fakeClient.PrependWatchReactor("theresource", func(action clienttesting.Action) (handled bool, ret watch.Interface, err error) {
fakeWatch := watch.NewRaceFreeFake() fakeWatch := watch.NewRaceFreeFake()
fakeWatch.Action(watch.Added, addCondition( fakeWatch.Action(watch.Added, addCondition(
@ -710,7 +718,7 @@ func TestWaitForCondition(t *testing.T) {
}, },
}, },
fakeClient: func() *dynamicfakeclient.FakeDynamicClient { 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) { 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 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) { func TestWaitForDeletionIgnoreNotFound(t *testing.T) {
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
listMapping := map[schema.GroupVersionResource]string{
{Group: "group", Version: "version", Resource: "theresource"}: "TheKindList",
}
infos := []*resource.Info{ infos := []*resource.Info{
{ {
Mapping: &meta.RESTMapping{ Mapping: &meta.RESTMapping{
@ -799,7 +810,7 @@ func TestWaitForDeletionIgnoreNotFound(t *testing.T) {
Namespace: "ns-foo", Namespace: "ns-foo",
}, },
} }
fakeClient := dynamicfakeclient.NewSimpleDynamicClient(scheme) fakeClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme, listMapping)
o := &WaitOptions{ o := &WaitOptions{
ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(infos...), ResourceFinder: genericclioptions.NewSimpleFakeResourceFinder(infos...),