Skip to content

Commit 727dff5

Browse files
committed
test(reconciler): refactor reconciler tests with k8s decorator
- Use fake k8s decorator for reconciler tests - Consolidate fake reconciler factory constructors - Add gRPC registry pod check unit tests - Refactor gRPC registry pod reconcile unit tests
1 parent 0f96d13 commit 727dff5

File tree

5 files changed

+325
-156
lines changed

5 files changed

+325
-156
lines changed

pkg/controller/registry/reconciler/configmap.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,36 @@ type configMapCatalogSourceDecorator struct {
2626
*v1alpha1.CatalogSource
2727
}
2828

29+
const (
30+
// ConfigMapServerPostfix is a postfix appended to the names of resources generated for a ConfigMap server.
31+
ConfigMapServerPostfix string = "-configmap-server"
32+
)
33+
2934
func (s *configMapCatalogSourceDecorator) serviceAccountName() string {
30-
return s.GetName() + "-configmap-server"
35+
return s.GetName() + ConfigMapServerPostfix
3136
}
3237

3338
func (s *configMapCatalogSourceDecorator) roleName() string {
34-
return s.GetName() + "-configmap-reader"
39+
return s.GetName() + ConfigMapServerPostfix
3540
}
3641

3742
func (s *configMapCatalogSourceDecorator) Selector() map[string]string {
3843
return map[string]string{
39-
"olm.catalogSource": s.GetName(),
44+
CatalogSourceLabelKey: s.GetName(),
4045
}
4146
}
4247

48+
const (
49+
// ConfigMapRVLabelKey is the key for a label used to track the resource version of a related ConfigMap.
50+
ConfigMapRVLabelKey string = "olm.configMapResourceVersion"
51+
)
52+
4353
func (s *configMapCatalogSourceDecorator) Labels() map[string]string {
4454
labels := map[string]string{
45-
"olm.catalogSource": s.GetName(),
55+
CatalogSourceLabelKey: s.GetName(),
4656
}
4757
if s.Spec.SourceType == v1alpha1.SourceTypeInternal || s.Spec.SourceType == v1alpha1.SourceTypeConfigmap {
48-
labels["olm.configMapResourceVersion"] = s.Status.ConfigMapResource.ResourceVersion
58+
labels[ConfigMapRVLabelKey] = s.Status.ConfigMapResource.ResourceVersion
4959
}
5060
return labels
5161
}
@@ -123,7 +133,7 @@ func (s *configMapCatalogSourceDecorator) Pod(image string) *v1.Pod {
123133
Operator: v1.TolerationOpExists,
124134
},
125135
},
126-
ServiceAccountName: s.GetName() + "-configmap-server",
136+
ServiceAccountName: s.GetName() + ConfigMapServerPostfix,
127137
},
128138
}
129139
ownerutil.AddOwner(pod, s.CatalogSource, false, false)
@@ -160,10 +170,15 @@ func (s *configMapCatalogSourceDecorator) Role() *rbacv1.Role {
160170
return role
161171
}
162172

173+
const (
174+
// ConfigMapReaderPostfix is the postfix applied to the name of reader RoleBinding generated for a ConfigMap server.
175+
ConfigMapReaderPostfix string = ConfigMapServerPostfix + "-reader"
176+
)
177+
163178
func (s *configMapCatalogSourceDecorator) RoleBinding() *rbacv1.RoleBinding {
164179
rb := &rbacv1.RoleBinding{
165180
ObjectMeta: metav1.ObjectMeta{
166-
Name: s.GetName() + "-server-configmap-reader",
181+
Name: s.GetName() + ConfigMapReaderPostfix,
167182
Namespace: s.GetNamespace(),
168183
},
169184
Subjects: []rbacv1.Subject{
@@ -442,7 +457,7 @@ func (c *ConfigMapRegistryReconciler) CheckRegistryServer(catalogSource *v1alpha
442457
c.currentRole(source) == nil ||
443458
c.currentRoleBinding(source) == nil ||
444459
c.currentService(source) == nil ||
445-
len(c.currentPods(source, c.Image)) != 1 {
460+
len(c.currentPods(source, c.Image)) < 1 {
446461
healthy = false
447462
return
448463
}

pkg/controller/registry/reconciler/configmap_test.go

Lines changed: 109 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,73 @@ package reconciler
22

33
import (
44
"fmt"
5+
"reflect"
56
"testing"
67
"time"
78

89
"github.com/ghodss/yaml"
9-
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
10-
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
11-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
12-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister"
1310
"github.com/stretchr/testify/require"
1411
corev1 "k8s.io/api/core/v1"
12+
rbacv1 "k8s.io/api/rbac/v1"
1513
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
14+
"k8s.io/apimachinery/pkg/api/meta"
1615
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+
"k8s.io/apimachinery/pkg/labels"
1717
"k8s.io/apimachinery/pkg/runtime"
1818
"k8s.io/apimachinery/pkg/types"
1919
"k8s.io/client-go/informers"
20-
k8sfake "k8s.io/client-go/kubernetes/fake"
2120
"k8s.io/client-go/tools/cache"
21+
k8slabels "k8s.io/kubernetes/pkg/util/labels"
22+
23+
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
24+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
25+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clientfake"
26+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
27+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister"
2228
)
2329

2430
const (
2531
registryImageName = "test:image"
2632
testNamespace = "testns"
2733
)
2834

29-
func cmReconciler(t *testing.T, k8sObjs []runtime.Object, stopc <-chan struct{}) (*ConfigMapRegistryReconciler, operatorclient.ClientInterface) {
30-
opClientFake := operatorclient.NewClient(k8sfake.NewSimpleClientset(k8sObjs...), nil, nil)
35+
type fakeReconcilerConfig struct {
36+
k8sObjs []runtime.Object
37+
k8sClientOptions []clientfake.Option
38+
configMapServerImage string
39+
}
40+
41+
type fakeReconcilerOption func(*fakeReconcilerConfig)
42+
43+
func withK8sObjs(k8sObjs ...runtime.Object) fakeReconcilerOption {
44+
return func(config *fakeReconcilerConfig) {
45+
config.k8sObjs = k8sObjs
46+
}
47+
}
48+
49+
func withK8sClientOptions(options ...clientfake.Option) fakeReconcilerOption {
50+
return func(config *fakeReconcilerConfig) {
51+
config.k8sClientOptions = options
52+
}
53+
}
54+
55+
func withConfigMapServerImage(configMapServerImage string) fakeReconcilerOption {
56+
return func(config *fakeReconcilerConfig) {
57+
config.configMapServerImage = configMapServerImage
58+
}
59+
}
60+
61+
func fakeReconcilerFactory(t *testing.T, stopc <-chan struct{}, options ...fakeReconcilerOption) (RegistryReconcilerFactory, operatorclient.ClientInterface) {
62+
config := &fakeReconcilerConfig{
63+
configMapServerImage: registryImageName,
64+
}
65+
66+
// Apply all config options
67+
for _, option := range options {
68+
option(config)
69+
}
70+
71+
opClientFake := operatorclient.NewClient(clientfake.NewReactionForwardingClientsetDecorator(config.k8sObjs, config.k8sClientOptions...), nil, nil)
3172

3273
// Creates registry pods in response to configmaps
3374
informerFactory := informers.NewSharedInformerFactory(opClientFake.KubernetesInterface(), 5*time.Second)
@@ -55,10 +96,10 @@ func cmReconciler(t *testing.T, k8sObjs []runtime.Object, stopc <-chan struct{})
5596
lister.CoreV1().RegisterPodLister(testNamespace, podInformer.Lister())
5697
lister.CoreV1().RegisterConfigMapLister(testNamespace, configMapInformer.Lister())
5798

58-
rec := &ConfigMapRegistryReconciler{
59-
Image: registryImageName,
60-
OpClient: opClientFake,
61-
Lister: lister,
99+
rec := &registryReconcilerFactory{
100+
OpClient: opClientFake,
101+
Lister: lister,
102+
ConfigMapServerImage: config.configMapServerImage,
62103
}
63104

64105
var hasSyncedCheckFns []cache.InformerSynced
@@ -137,14 +178,27 @@ func validConfigMapCatalogSource(configMap *corev1.ConfigMap) *v1alpha1.CatalogS
137178
}
138179

139180
func objectsForCatalogSource(catsrc *v1alpha1.CatalogSource) []runtime.Object {
140-
decorated := configMapCatalogSourceDecorator{catsrc}
141-
objs := []runtime.Object{
142-
decorated.Pod(registryImageName),
143-
decorated.Service(),
144-
decorated.ServiceAccount(),
145-
decorated.Role(),
146-
decorated.RoleBinding(),
181+
var objs []runtime.Object
182+
switch catsrc.Spec.SourceType {
183+
case v1alpha1.SourceTypeInternal, v1alpha1.SourceTypeConfigmap:
184+
decorated := configMapCatalogSourceDecorator{catsrc}
185+
objs = clientfake.AddSimpleGeneratedNames(
186+
clientfake.AddSimpleGeneratedName(decorated.Pod(registryImageName)),
187+
decorated.Service(),
188+
decorated.ServiceAccount(),
189+
decorated.Role(),
190+
decorated.RoleBinding(),
191+
)
192+
case v1alpha1.SourceTypeGrpc:
193+
if catsrc.Spec.Image != "" {
194+
decorated := grpcCatalogSourceDecorator{catsrc}
195+
objs = clientfake.AddSimpleGeneratedNames(
196+
decorated.Pod(),
197+
decorated.Service(),
198+
)
199+
}
147200
}
201+
148202
blockOwnerDeletion := false
149203
isController := false
150204
for _, o := range objs {
@@ -161,14 +215,30 @@ func objectsForCatalogSource(catsrc *v1alpha1.CatalogSource) []runtime.Object {
161215
return objs
162216
}
163217

164-
func modifyObjName(objs []runtime.Object, kind, newName string) []runtime.Object {
218+
func modifyObjName(objs []runtime.Object, kind runtime.Object, newName string) []runtime.Object {
165219
out := []runtime.Object{}
166-
for _, o := range objs {
167-
if o.GetObjectKind().GroupVersionKind().Kind == kind {
168-
mo := o.(metav1.Object)
169-
mo.SetName(newName)
170-
out = append(out, mo.(runtime.Object))
171-
continue
220+
t := reflect.TypeOf(kind)
221+
for _, obj := range objs {
222+
o := obj.DeepCopyObject()
223+
if reflect.TypeOf(o) == t {
224+
if accessor, err := meta.Accessor(o); err == nil {
225+
accessor.SetName(newName)
226+
}
227+
}
228+
out = append(out, o)
229+
}
230+
return out
231+
}
232+
233+
func setLabel(objs []runtime.Object, kind runtime.Object, label, value string) []runtime.Object {
234+
out := []runtime.Object{}
235+
t := reflect.TypeOf(kind)
236+
for _, obj := range objs {
237+
o := obj.DeepCopyObject()
238+
if reflect.TypeOf(o) == t {
239+
if accessor, err := meta.Accessor(o); err == nil {
240+
k8slabels.AddLabel(accessor.GetLabels(), label, value)
241+
}
172242
}
173243
out = append(out, o)
174244
}
@@ -235,7 +305,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
235305
testName: "ExistingRegistry/BadServiceAccount",
236306
in: in{
237307
cluster: cluster{
238-
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), "ServiceAccount", "badName"), validConfigMap),
308+
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), &corev1.ServiceAccount{}, "badName"), validConfigMap),
239309
},
240310
catsrc: validCatalogSource,
241311
},
@@ -253,7 +323,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
253323
testName: "ExistingRegistry/BadService",
254324
in: in{
255325
cluster: cluster{
256-
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), "Service", "badName"), validConfigMap),
326+
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), &corev1.Service{}, "badName"), validConfigMap),
257327
},
258328
catsrc: validCatalogSource,
259329
},
@@ -271,7 +341,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
271341
testName: "ExistingRegistry/BadPod",
272342
in: in{
273343
cluster: cluster{
274-
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), "Pod", "badName"), validConfigMap),
344+
k8sObjs: append(setLabel(objectsForCatalogSource(validCatalogSource), &corev1.Pod{}, CatalogSourceLabelKey, "badValue"), validConfigMap),
275345
},
276346
catsrc: validCatalogSource,
277347
},
@@ -289,7 +359,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
289359
testName: "ExistingRegistry/BadRole",
290360
in: in{
291361
cluster: cluster{
292-
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), "Role", "badName"), validConfigMap),
362+
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), &rbacv1.Role{}, "badName"), validConfigMap),
293363
},
294364
catsrc: validCatalogSource,
295365
},
@@ -307,7 +377,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
307377
testName: "ExistingRegistry/BadRoleBinding",
308378
in: in{
309379
cluster: cluster{
310-
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), "RoleBinding", "badName"), validConfigMap),
380+
k8sObjs: append(modifyObjName(objectsForCatalogSource(validCatalogSource), &rbacv1.RoleBinding{}, "badName"), validConfigMap),
311381
},
312382
catsrc: validCatalogSource,
313383
},
@@ -345,7 +415,8 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
345415
stopc := make(chan struct{})
346416
defer close(stopc)
347417

348-
rec, client := cmReconciler(t, tt.in.cluster.k8sObjs, stopc)
418+
factory, client := fakeReconcilerFactory(t, stopc, withK8sObjs(tt.in.cluster.k8sObjs...), withK8sClientOptions(clientfake.WithNameGeneration(t)))
419+
rec := factory.ReconcilerForSource(tt.in.catsrc)
349420

350421
err := rec.EnsureRegistryServer(tt.in.catsrc)
351422

@@ -360,9 +431,14 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
360431
decorated := configMapCatalogSourceDecorator{tt.in.catsrc}
361432

362433
pod := decorated.Pod(registryImageName)
363-
outPod, err := client.KubernetesInterface().CoreV1().Pods(pod.GetNamespace()).Get(pod.GetName(), metav1.GetOptions{})
434+
listOptions := metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{CatalogSourceLabelKey: tt.in.catsrc.GetName()}).String()}
435+
outPods, err := client.KubernetesInterface().CoreV1().Pods(pod.GetNamespace()).List(listOptions)
364436
require.NoError(t, err)
365-
require.Equal(t, pod, outPod)
437+
require.Len(t, outPods.Items, 1)
438+
outPod := outPods.Items[0]
439+
require.Equal(t, pod.GetGenerateName(), outPod.GetGenerateName())
440+
require.Equal(t, pod.GetLabels(), outPod.GetLabels())
441+
require.Equal(t, pod.Spec, outPod.Spec)
366442

367443
service := decorated.Service()
368444
outService, err := client.KubernetesInterface().CoreV1().Services(service.GetNamespace()).Get(service.GetName(), metav1.GetOptions{})

pkg/controller/registry/reconciler/grpc.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ package reconciler
33
import (
44
"fmt"
55

6-
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
7-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
8-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister"
9-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
106
"github.com/pkg/errors"
117
"github.com/sirupsen/logrus"
128
v1 "k8s.io/api/core/v1"
139
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1410
"k8s.io/apimachinery/pkg/labels"
1511
"k8s.io/apimachinery/pkg/util/intstr"
12+
13+
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
14+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
15+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister"
16+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
1617
)
1718

1819
// grpcCatalogSourceDecorator wraps CatalogSource to add additional methods
@@ -22,13 +23,13 @@ type grpcCatalogSourceDecorator struct {
2223

2324
func (s *grpcCatalogSourceDecorator) Selector() labels.Selector {
2425
return labels.SelectorFromValidatedSet(map[string]string{
25-
"olm.catalogSource": s.GetName(),
26+
CatalogSourceLabelKey: s.GetName(),
2627
})
2728
}
2829

2930
func (s *grpcCatalogSourceDecorator) Labels() map[string]string {
3031
return map[string]string{
31-
"olm.catalogSource": s.GetName(),
32+
CatalogSourceLabelKey: s.GetName(),
3233
}
3334
}
3435

0 commit comments

Comments
 (0)