@@ -18,6 +18,7 @@ package fake
18
18
19
19
import (
20
20
"context"
21
+ "fmt"
21
22
"strings"
22
23
23
24
"k8s.io/apimachinery/pkg/api/meta"
@@ -34,11 +35,45 @@ import (
34
35
)
35
36
36
37
func NewSimpleDynamicClient (scheme * runtime.Scheme , objects ... runtime.Object ) * FakeDynamicClient {
37
- // In order to use List with this client, you have to have the v1.List registered in your scheme. Neat thing though
38
- // it does NOT have to be the *same* list. UnstructuredList returned from this fake client will NOT have apiVersion and kind set,
39
- // but each Unstructured object in Items will preserve their respective apiVersion and kind. As a result, schema conversion for
40
- // *List kinds will not work and conversion of each Unstructured object in Items will be required instead.
41
- scheme .AddKnownTypeWithName (schema.GroupVersionKind {Group : "fake-dynamic-client-group" , Version : "v1" , Kind : "List" }, & unstructured.UnstructuredList {})
38
+ return NewSimpleDynamicClientWithCustomListKinds (scheme , nil , objects ... )
39
+ }
40
+
41
+ // NewSimpleDynamicClientWithCustomListKinds try not to use this. In general you want to have the scheme have the List types registered
42
+ // and allow the default guessing for resources match. Sometimes that doesn't work, so you can specify a custom mapping here.
43
+ func NewSimpleDynamicClientWithCustomListKinds (scheme * runtime.Scheme , gvrToListKind map [schema.GroupVersionResource ]string , objects ... runtime.Object ) * FakeDynamicClient {
44
+ // In order to use List with this client, you have to have your lists registered so that the object tracker will find them
45
+ // in the scheme to support the t.scheme.New(listGVK) call when it's building the return value.
46
+ // Since the base fake client needs the listGVK passed through the action (in cases where there are no instances, it
47
+ // cannot look up the actual hits), we need to know a mapping of GVR to listGVK here. For GETs and other types of calls,
48
+ // there is no return value that contains a GVK, so it doesn't have to know the mapping in advance.
49
+
50
+ // first we attempt to invert known List types from the scheme to auto guess the resource with unsafe guesses
51
+ // this covers common usage of registering types in scheme and passing them
52
+ completeGVRToListKind := map [schema.GroupVersionResource ]string {}
53
+ for listGVK := range scheme .AllKnownTypes () {
54
+ if ! strings .HasSuffix (listGVK .Kind , "List" ) {
55
+ continue
56
+ }
57
+ nonListGVK := listGVK .GroupVersion ().WithKind (listGVK .Kind [:len (listGVK .Kind )- 4 ])
58
+ plural , _ := meta .UnsafeGuessKindToResource (nonListGVK )
59
+ completeGVRToListKind [plural ] = listGVK .Kind
60
+ }
61
+
62
+ for gvr , listKind := range gvrToListKind {
63
+ if ! strings .HasSuffix (listKind , "List" ) {
64
+ panic ("coding error, listGVK must end in List or this fake client doesn't work right" )
65
+ }
66
+ listGVK := gvr .GroupVersion ().WithKind (listKind )
67
+
68
+ // if we already have this type registered, just skip it
69
+ if _ , err := scheme .New (listGVK ); err == nil {
70
+ completeGVRToListKind [gvr ] = listKind
71
+ continue
72
+ }
73
+
74
+ scheme .AddKnownTypeWithName (listGVK , & unstructured.UnstructuredList {})
75
+ completeGVRToListKind [gvr ] = listKind
76
+ }
42
77
43
78
codecs := serializer .NewCodecFactory (scheme )
44
79
o := testing .NewObjectTracker (scheme , codecs .UniversalDecoder ())
@@ -48,7 +83,7 @@ func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *
48
83
}
49
84
}
50
85
51
- cs := & FakeDynamicClient {scheme : scheme }
86
+ cs := & FakeDynamicClient {scheme : scheme , gvrToListKind : completeGVRToListKind }
52
87
cs .AddReactor ("*" , "*" , testing .ObjectReaction (o ))
53
88
cs .AddWatchReactor ("*" , func (action testing.Action ) (handled bool , ret watch.Interface , err error ) {
54
89
gvr := action .GetResource ()
@@ -68,19 +103,21 @@ func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *
68
103
// you want to test easier.
69
104
type FakeDynamicClient struct {
70
105
testing.Fake
71
- scheme * runtime.Scheme
106
+ scheme * runtime.Scheme
107
+ gvrToListKind map [schema.GroupVersionResource ]string
72
108
}
73
109
74
110
type dynamicResourceClient struct {
75
111
client * FakeDynamicClient
76
112
namespace string
77
113
resource schema.GroupVersionResource
114
+ listKind string
78
115
}
79
116
80
117
var _ dynamic.Interface = & FakeDynamicClient {}
81
118
82
119
func (c * FakeDynamicClient ) Resource (resource schema.GroupVersionResource ) dynamic.NamespaceableResourceInterface {
83
- return & dynamicResourceClient {client : c , resource : resource }
120
+ return & dynamicResourceClient {client : c , resource : resource , listKind : c . gvrToListKind [ resource ] }
84
121
}
85
122
86
123
func (c * dynamicResourceClient ) Namespace (ns string ) dynamic.ResourceInterface {
@@ -276,16 +313,22 @@ func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav
276
313
}
277
314
278
315
func (c * dynamicResourceClient ) List (ctx context.Context , opts metav1.ListOptions ) (* unstructured.UnstructuredList , error ) {
316
+ if len (c .listKind ) == 0 {
317
+ 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 ))
318
+ }
319
+ listGVK := c .resource .GroupVersion ().WithKind (c .listKind )
320
+ listForFakeClientGVK := c .resource .GroupVersion ().WithKind (c .listKind [:len (c .listKind )- 4 ]) /*base library appends List*/
321
+
279
322
var obj runtime.Object
280
323
var err error
281
324
switch {
282
325
case len (c .namespace ) == 0 :
283
326
obj , err = c .client .Fake .
284
- 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" })
327
+ Invokes (testing .NewRootListAction (c .resource , listForFakeClientGVK , opts ), & metav1.Status {Status : "dynamic list fail" })
285
328
286
329
case len (c .namespace ) > 0 :
287
330
obj , err = c .client .Fake .
288
- 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" })
331
+ Invokes (testing .NewListAction (c .resource , listForFakeClientGVK , c .namespace , opts ), & metav1.Status {Status : "dynamic list fail" })
289
332
290
333
}
291
334
@@ -309,6 +352,7 @@ func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOption
309
352
310
353
list := & unstructured.UnstructuredList {}
311
354
list .SetResourceVersion (entireList .GetResourceVersion ())
355
+ list .GetObjectKind ().SetGroupVersionKind (listGVK )
312
356
for i := range entireList .Items {
313
357
item := & entireList .Items [i ]
314
358
metadata , err := meta .Accessor (item )
0 commit comments