Skip to content

Commit bd6a4b5

Browse files
committed
Add unittest to the object for objectlist finding logic
1 parent 0b6855d commit bd6a4b5

File tree

5 files changed

+174
-70
lines changed

5 files changed

+174
-70
lines changed

pkg/cache/informer_cache.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,26 +70,48 @@ func (ip *informerCache) Get(ctx context.Context, key client.ObjectKey, out runt
7070

7171
// List implements Reader
7272
func (ip *informerCache) List(ctx context.Context, out runtime.Object, opts ...client.ListOption) error {
73-
gvk, err := apiutil.GVKForObject(out, ip.Scheme)
73+
74+
gvk, cacheTypeObj, err := ip.objectTypeForListObject(out)
75+
if err != nil {
76+
return err
77+
}
78+
79+
started, cache, err := ip.InformersMap.Get(*gvk, cacheTypeObj)
7480
if err != nil {
7581
return err
7682
}
7783

84+
if !started {
85+
return &ErrCacheNotStarted{}
86+
}
87+
88+
return cache.Reader.List(ctx, out, opts...)
89+
}
90+
91+
// objectTypeForListObject tries to find the runtime.Object and associated GVK
92+
// for a single object corresponding to the passed-in list type. We need them
93+
// because they are used as cache map key.
94+
func (ip *informerCache) objectTypeForListObject(list runtime.Object) (*schema.GroupVersionKind, runtime.Object, error) {
95+
gvk, err := apiutil.GVKForObject(list, ip.Scheme)
96+
if err != nil {
97+
return nil, nil, err
98+
}
99+
78100
if !strings.HasSuffix(gvk.Kind, "List") {
79-
return fmt.Errorf("non-list type %T (kind %q) passed as output", out, gvk)
101+
return nil, nil, fmt.Errorf("non-list type %T (kind %q) passed as output", list, gvk)
80102
}
81103
// we need the non-list GVK, so chop off the "List" from the end of the kind
82104
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
83-
_, isUnstructured := out.(*unstructured.UnstructuredList)
105+
_, isUnstructured := list.(*unstructured.UnstructuredList)
84106
var cacheTypeObj runtime.Object
85107
if isUnstructured {
86108
u := &unstructured.Unstructured{}
87109
u.SetGroupVersionKind(gvk)
88110
cacheTypeObj = u
89111
} else {
90-
itemsPtr, err := apimeta.GetItemsPtr(out)
112+
itemsPtr, err := apimeta.GetItemsPtr(list)
91113
if err != nil {
92-
return nil
114+
return nil, nil, err
93115
}
94116
// http://knowyourmeme.com/memes/this-is-fine
95117
elemType := reflect.Indirect(reflect.ValueOf(itemsPtr)).Type().Elem()
@@ -101,20 +123,12 @@ func (ip *informerCache) List(ctx context.Context, out runtime.Object, opts ...c
101123
var ok bool
102124
cacheTypeObj, ok = cacheTypeValue.Interface().(runtime.Object)
103125
if !ok {
104-
return fmt.Errorf("cannot get cache for %T, its element %T is not a runtime.Object", out, cacheTypeValue.Interface())
126+
return nil, nil, fmt.Errorf("cannot get cache for %T, its element %T is not a runtime.Object", list, cacheTypeValue.Interface())
105127
}
106128
}
107129

108-
started, cache, err := ip.InformersMap.Get(gvk, cacheTypeObj)
109-
if err != nil {
110-
return err
111-
}
112-
113-
if !started {
114-
return &ErrCacheNotStarted{}
115-
}
130+
return &gvk, cacheTypeObj, nil
116131

117-
return cache.Reader.List(ctx, out, opts...)
118132
}
119133

120134
// GetInformerForKind returns the informer for the GroupVersionKind

pkg/cache/informer_cache_unit_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package cache
2+
3+
import (
4+
. "github.com/onsi/ginkgo"
5+
. "github.com/onsi/gomega"
6+
7+
corev1 "k8s.io/api/core/v1"
8+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9+
"k8s.io/apimachinery/pkg/runtime/schema"
10+
"k8s.io/client-go/kubernetes/scheme"
11+
12+
"sigs.k8s.io/controller-runtime/pkg/cache/internal"
13+
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
14+
crscheme "sigs.k8s.io/controller-runtime/pkg/scheme"
15+
)
16+
17+
const (
18+
itemPointerSliceTypeGroupName = "jakob.fabian"
19+
itemPointerSliceTypeVersion = "v1"
20+
)
21+
22+
var _ = Describe("ip.objectTypeForListObject", func() {
23+
ip := &informerCache{
24+
InformersMap: &internal.InformersMap{Scheme: scheme.Scheme},
25+
}
26+
27+
It("should error on non-list types", func() {
28+
_, _, err := ip.objectTypeForListObject(&corev1.Pod{})
29+
Expect(err).To(HaveOccurred())
30+
Expect(err.Error()).To(Equal(`non-list type *v1.Pod (kind "/v1, Kind=Pod") passed as output`))
31+
})
32+
33+
It("should find the object type for unstructured lists", func() {
34+
unstructuredList := &unstructured.UnstructuredList{}
35+
unstructuredList.SetAPIVersion("v1")
36+
unstructuredList.SetKind("PodList")
37+
38+
gvk, obj, err := ip.objectTypeForListObject(unstructuredList)
39+
Expect(err).ToNot(HaveOccurred())
40+
Expect(gvk.Group).To(Equal(""))
41+
Expect(gvk.Version).To(Equal("v1"))
42+
Expect(gvk.Kind).To(Equal("Pod"))
43+
referenceUnstructured := &unstructured.Unstructured{}
44+
referenceUnstructured.SetGroupVersionKind(*gvk)
45+
Expect(obj).To(Equal(referenceUnstructured))
46+
47+
})
48+
49+
It("should find the object type of a list with a slice of literals items field", func() {
50+
gvk, obj, err := ip.objectTypeForListObject(&corev1.PodList{})
51+
Expect(err).ToNot(HaveOccurred())
52+
Expect(gvk.Group).To(Equal(""))
53+
Expect(gvk.Version).To(Equal("v1"))
54+
Expect(gvk.Kind).To(Equal("Pod"))
55+
var referencePod *corev1.Pod
56+
Expect(obj).To(Equal(referencePod))
57+
58+
})
59+
60+
It("should find the object type of a list with a slice of pointers items field", func() {
61+
By("registering the type", func() {
62+
err := (&crscheme.Builder{
63+
GroupVersion: schema.GroupVersion{Group: itemPointerSliceTypeGroupName, Version: itemPointerSliceTypeVersion},
64+
}).
65+
Register(
66+
&controllertest.UnconventionalListType{},
67+
&controllertest.UnconventionalListTypeList{},
68+
).AddToScheme(scheme.Scheme)
69+
Expect(err).To(BeNil())
70+
})
71+
72+
By("calling objectTypeForListObject", func() {
73+
gvk, obj, err := ip.objectTypeForListObject(&controllertest.UnconventionalListTypeList{})
74+
Expect(err).ToNot(HaveOccurred())
75+
Expect(gvk.Group).To(Equal(itemPointerSliceTypeGroupName))
76+
Expect(gvk.Version).To(Equal(itemPointerSliceTypeVersion))
77+
Expect(gvk.Kind).To(Equal("UnconventionalListType"))
78+
var referenceObject *controllertest.UnconventionalListType
79+
Expect(obj).To(Equal(referenceObject))
80+
})
81+
})
82+
})

pkg/controller/controller_integration_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"k8s.io/apimachinery/pkg/types"
2727
"sigs.k8s.io/controller-runtime/pkg/cache"
2828
"sigs.k8s.io/controller-runtime/pkg/controller"
29+
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
2930
"sigs.k8s.io/controller-runtime/pkg/handler"
3031
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3132
"sigs.k8s.io/controller-runtime/pkg/source"
@@ -169,7 +170,7 @@ var _ = Describe("controller", func() {
169170

170171
By("Listing a type with a slice of pointers as items field")
171172
err = cm.GetClient().
172-
List(context.Background(), &UnconventionalListTypeList{})
173+
List(context.Background(), &controllertest.UnconventionalListTypeList{})
173174
Expect(err).NotTo(HaveOccurred())
174175

175176
close(done)

pkg/controller/controller_suite_test.go

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@ import (
2121

2222
. "github.com/onsi/ginkgo"
2323
. "github.com/onsi/gomega"
24-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25-
"k8s.io/apimachinery/pkg/runtime"
2624
"k8s.io/apimachinery/pkg/runtime/schema"
2725
"k8s.io/client-go/kubernetes"
2826
"k8s.io/client-go/kubernetes/scheme"
2927
"k8s.io/client-go/rest"
3028

29+
"sigs.k8s.io/controller-runtime/pkg/controller/controllertest"
3130
"sigs.k8s.io/controller-runtime/pkg/envtest"
3231
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
3332
logf "sigs.k8s.io/controller-runtime/pkg/log"
@@ -51,8 +50,10 @@ var _ = BeforeSuite(func(done Done) {
5150
err := (&crscheme.Builder{
5251
GroupVersion: schema.GroupVersion{Group: "chaosapps.metamagical.io", Version: "v1"},
5352
}).
54-
Register(&UnconventionalListType{}, &UnconventionalListTypeList{}).
55-
AddToScheme(scheme.Scheme)
53+
Register(
54+
&controllertest.UnconventionalListType{},
55+
&controllertest.UnconventionalListTypeList{},
56+
).AddToScheme(scheme.Scheme)
5657
Expect(err).To(BeNil())
5758

5859
testenv = &envtest.Environment{
@@ -77,53 +78,3 @@ var _ = AfterSuite(func() {
7778
// Put the DefaultBindAddress back
7879
metrics.DefaultBindAddress = ":8080"
7980
})
80-
81-
var _ runtime.Object = &UnconventionalListType{}
82-
var _ runtime.Object = &UnconventionalListTypeList{}
83-
84-
// UnconventionalListType is used to test CRDs with List types that
85-
// have a slice of pointers rather than a slice of literals.
86-
type UnconventionalListType struct {
87-
metav1.TypeMeta `json:",inline"`
88-
metav1.ObjectMeta `json:"metadata,omitempty"`
89-
Spec string `json:"spec,omitempty"`
90-
}
91-
92-
// DeepCopyObject implements runtime.Object
93-
// Handwritten for simplicity.
94-
func (u *UnconventionalListType) DeepCopyObject() runtime.Object {
95-
return u.DeepCopy()
96-
}
97-
98-
func (u *UnconventionalListType) DeepCopy() *UnconventionalListType {
99-
return &UnconventionalListType{
100-
TypeMeta: u.TypeMeta,
101-
ObjectMeta: *u.ObjectMeta.DeepCopy(),
102-
Spec: u.Spec,
103-
}
104-
}
105-
106-
// UnconventionalListTypeList is used to test CRDs with List types that
107-
// have a slice of pointers rather than a slice of literals.
108-
type UnconventionalListTypeList struct {
109-
metav1.TypeMeta `json:",inline"`
110-
metav1.ListMeta `json:"metadata,omitempty"`
111-
Items []*UnconventionalListType `json:"items"`
112-
}
113-
114-
// DeepCopyObject implements runtime.Object
115-
// Handwritten for simplicity.
116-
func (u *UnconventionalListTypeList) DeepCopyObject() runtime.Object {
117-
return u.DeepCopy()
118-
}
119-
120-
func (u *UnconventionalListTypeList) DeepCopy() *UnconventionalListTypeList {
121-
out := &UnconventionalListTypeList{
122-
TypeMeta: u.TypeMeta,
123-
ListMeta: *u.ListMeta.DeepCopy(),
124-
}
125-
for _, item := range u.Items {
126-
out.Items = append(out.Items, item.DeepCopy())
127-
}
128-
return out
129-
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package controllertest
2+
3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
"k8s.io/apimachinery/pkg/runtime"
6+
)
7+
8+
var _ runtime.Object = &UnconventionalListType{}
9+
var _ runtime.Object = &UnconventionalListTypeList{}
10+
11+
// UnconventionalListType is used to test CRDs with List types that
12+
// have a slice of pointers rather than a slice of literals.
13+
type UnconventionalListType struct {
14+
metav1.TypeMeta `json:",inline"`
15+
metav1.ObjectMeta `json:"metadata,omitempty"`
16+
Spec string `json:"spec,omitempty"`
17+
}
18+
19+
// DeepCopyObject implements runtime.Object
20+
// Handwritten for simplicity.
21+
func (u *UnconventionalListType) DeepCopyObject() runtime.Object {
22+
return u.DeepCopy()
23+
}
24+
25+
func (u *UnconventionalListType) DeepCopy() *UnconventionalListType {
26+
return &UnconventionalListType{
27+
TypeMeta: u.TypeMeta,
28+
ObjectMeta: *u.ObjectMeta.DeepCopy(),
29+
Spec: u.Spec,
30+
}
31+
}
32+
33+
// UnconventionalListTypeList is used to test CRDs with List types that
34+
// have a slice of pointers rather than a slice of literals.
35+
type UnconventionalListTypeList struct {
36+
metav1.TypeMeta `json:",inline"`
37+
metav1.ListMeta `json:"metadata,omitempty"`
38+
Items []*UnconventionalListType `json:"items"`
39+
}
40+
41+
// DeepCopyObject implements runtime.Object
42+
// Handwritten for simplicity.
43+
func (u *UnconventionalListTypeList) DeepCopyObject() runtime.Object {
44+
return u.DeepCopy()
45+
}
46+
47+
func (u *UnconventionalListTypeList) DeepCopy() *UnconventionalListTypeList {
48+
out := &UnconventionalListTypeList{
49+
TypeMeta: u.TypeMeta,
50+
ListMeta: *u.ListMeta.DeepCopy(),
51+
}
52+
for _, item := range u.Items {
53+
out.Items = append(out.Items, item.DeepCopy())
54+
}
55+
return out
56+
}

0 commit comments

Comments
 (0)