Skip to content

Commit ad27f33

Browse files
njhaletimflannagan
authored andcommitted
test(og): de-flake sync unit tests
1 parent c97edf6 commit ad27f33

File tree

2 files changed

+107
-71
lines changed

2 files changed

+107
-71
lines changed

pkg/controller/operators/olm/operator_test.go

Lines changed: 102 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import (
3434
"k8s.io/apimachinery/pkg/runtime"
3535
"k8s.io/apimachinery/pkg/types"
3636
utilclock "k8s.io/apimachinery/pkg/util/clock"
37-
"k8s.io/apimachinery/pkg/util/diff"
3837
utilerrors "k8s.io/apimachinery/pkg/util/errors"
3938
"k8s.io/apimachinery/pkg/util/intstr"
4039
"k8s.io/apimachinery/pkg/util/wait"
@@ -3667,9 +3666,13 @@ func TestUpdates(t *testing.T) {
36673666
}
36683667

36693668
func TestSyncOperatorGroups(t *testing.T) {
3670-
logrus.SetLevel(logrus.DebugLevel)
3669+
logrus.SetLevel(logrus.WarnLevel)
36713670
clockFake := utilclock.NewFakeClock(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.FixedZone("MST", -7*3600)))
36723671
now := metav1.NewTime(clockFake.Now().UTC())
3672+
const (
3673+
timeout = 5 * time.Second
3674+
tick = 50 * time.Millisecond
3675+
)
36733676

36743677
operatorNamespace := "operator-ns"
36753678
targetNamespace := "target-ns"
@@ -4305,6 +4308,10 @@ func TestSyncOperatorGroups(t *testing.T) {
43054308
expectedEqual: true,
43064309
initial: initial{
43074310
operatorGroup: &v1.OperatorGroup{
4311+
TypeMeta: metav1.TypeMeta{
4312+
Kind: v1.OperatorGroupKind,
4313+
APIVersion: v1.GroupVersion.String(),
4314+
},
43084315
ObjectMeta: metav1.ObjectMeta{
43094316
Name: "operator-group-1",
43104317
Namespace: operatorNamespace,
@@ -4334,6 +4341,10 @@ func TestSyncOperatorGroups(t *testing.T) {
43344341
final: final{objects: map[string][]runtime.Object{
43354342
operatorNamespace: {
43364343
&v1.OperatorGroup{
4344+
TypeMeta: metav1.TypeMeta{
4345+
Kind: v1.OperatorGroupKind,
4346+
APIVersion: v1.GroupVersion.String(),
4347+
},
43374348
ObjectMeta: metav1.ObjectMeta{
43384349
Name: "operator-group-1",
43394350
Namespace: operatorNamespace,
@@ -4415,124 +4426,154 @@ func TestSyncOperatorGroups(t *testing.T) {
44154426
"AllNamespaces InstallModeType not supported, cannot configure to watch all namespaces",
44164427
now),
44174428
},
4418-
"": {},
4419-
targetNamespace: {},
44204429
}},
44214430
},
44224431
}
44234432

4433+
copyObjs := func(objs []runtime.Object) []runtime.Object {
4434+
if len(objs) < 1 {
4435+
return nil
4436+
}
4437+
4438+
copied := make([]runtime.Object, len(objs))
4439+
for i, obj := range objs {
4440+
copied[i] = obj.DeepCopyObject()
4441+
}
4442+
4443+
return copied
4444+
}
4445+
44244446
for _, tt := range tests {
44254447
t.Run(tt.name, func(t *testing.T) {
4426-
namespaces := []string{}
44274448
// Pick out Namespaces
4449+
var namespaces []string
44284450
for _, obj := range tt.initial.k8sObjs {
44294451
if ns, ok := obj.(*corev1.Namespace); ok {
44304452
namespaces = append(namespaces, ns.GetName())
44314453
}
44324454
}
44334455

4434-
// Append operatorGroup to initialObjs
4435-
tt.initial.clientObjs = append(tt.initial.clientObjs, tt.initial.operatorGroup)
4456+
// DeepCopy test fixtures to prevent test case pollution
4457+
var (
4458+
operatorGroup = tt.initial.operatorGroup.DeepCopy()
4459+
clientObjs = copyObjs(append(tt.initial.clientObjs, operatorGroup))
4460+
k8sObjs = copyObjs(tt.initial.k8sObjs)
4461+
extObjs = copyObjs(tt.initial.crds)
4462+
regObjs = copyObjs(tt.initial.apis)
4463+
)
44364464

44374465
// Create test operator
4438-
ctx, cancel := context.WithCancel(context.TODO())
4466+
ctx, cancel := context.WithCancel(context.Background())
44394467
defer cancel()
4468+
44404469
op, err := NewFakeOperator(
44414470
ctx,
44424471
withClock(clockFake),
44434472
withNamespaces(namespaces...),
44444473
withOperatorNamespace(operatorNamespace),
4445-
withClientObjs(tt.initial.clientObjs...),
4446-
withK8sObjs(tt.initial.k8sObjs...),
4447-
withExtObjs(tt.initial.crds...),
4448-
withRegObjs(tt.initial.apis...),
4474+
withClientObjs(clientObjs...),
4475+
withK8sObjs(k8sObjs...),
4476+
withExtObjs(extObjs...),
4477+
withRegObjs(regObjs...),
44494478
)
44504479
require.NoError(t, err)
44514480

4452-
simulateSuccessfulRollout := func(csv *v1alpha1.ClusterServiceVersion, client operatorclient.ClientInterface) {
4453-
// get the deployment, which should exist
4454-
dep, err := client.GetDeployment(tt.initial.operatorGroup.GetNamespace(), deploymentName)
4481+
simulateSuccessfulRollout := func(csv *v1alpha1.ClusterServiceVersion) {
4482+
// Get the deployment, which should exist
4483+
namespace := operatorGroup.GetNamespace()
4484+
dep, err := op.opClient.GetDeployment(namespace, deploymentName)
44554485
require.NoError(t, err)
44564486

4457-
// force it healthy
4487+
// Force it healthy
44584488
dep.Status.Replicas = 1
44594489
dep.Status.UpdatedReplicas = 1
44604490
dep.Status.AvailableReplicas = 1
44614491
dep.Status.Conditions = []appsv1.DeploymentCondition{{
44624492
Type: appsv1.DeploymentAvailable,
44634493
Status: corev1.ConditionTrue,
44644494
}}
4465-
_, err = client.KubernetesInterface().AppsV1().Deployments(tt.initial.operatorGroup.GetNamespace()).UpdateStatus(context.TODO(), dep, metav1.UpdateOptions{})
4495+
_, err = op.opClient.KubernetesInterface().AppsV1().Deployments(namespace).UpdateStatus(ctx, dep, metav1.UpdateOptions{})
4496+
require.NoError(t, err)
4497+
4498+
// Wait for the lister cache to catch up
4499+
err = wait.PollImmediateWithContext(ctx, tick, timeout, func(ctx context.Context) (bool, error) {
4500+
deployment, err := op.lister.AppsV1().DeploymentLister().Deployments(namespace).Get(dep.GetName())
4501+
if err != nil || deployment == nil {
4502+
return false, err
4503+
}
4504+
4505+
for _, condition := range deployment.Status.Conditions {
4506+
if condition.Type == appsv1.DeploymentAvailable {
4507+
return condition.Status == corev1.ConditionTrue, nil
4508+
}
4509+
}
4510+
4511+
return false, nil
4512+
})
44664513
require.NoError(t, err)
44674514
}
44684515

4469-
err = op.syncOperatorGroups(tt.initial.operatorGroup)
4516+
err = op.syncOperatorGroups(operatorGroup)
44704517
require.NoError(t, err)
44714518

4472-
// wait on operator group updated status to be in the cache as it is required for later CSV operations
4473-
err = wait.PollImmediate(1*time.Millisecond, 5*time.Second, func() (bool, error) {
4474-
operatorGroup, err := op.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(tt.initial.operatorGroup.GetNamespace()).Get(tt.initial.operatorGroup.GetName())
4519+
// Wait on operator group updated status to be in the cache as it is required for later CSV operations
4520+
err = wait.PollImmediateWithContext(ctx, tick, timeout, func(ctx context.Context) (bool, error) {
4521+
og, err := op.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(operatorGroup.GetNamespace()).Get(operatorGroup.GetName())
44754522
if err != nil {
44764523
return false, err
44774524
}
44784525
sort.Strings(tt.expectedStatus.Namespaces)
4479-
sort.Strings(operatorGroup.Status.Namespaces)
4480-
if !reflect.DeepEqual(tt.expectedStatus, operatorGroup.Status) {
4526+
sort.Strings(og.Status.Namespaces)
4527+
if !reflect.DeepEqual(tt.expectedStatus, og.Status) {
44814528
return false, err
44824529
}
4530+
4531+
operatorGroup = og
4532+
44834533
return true, nil
44844534
})
44854535
require.NoError(t, err)
44864536

4487-
// this must be done twice to have annotateCSVs run in syncOperatorGroups
4488-
// and to catch provided API changes
4489-
err = op.syncOperatorGroups(tt.initial.operatorGroup)
4537+
// This must be done (at least) twice to have annotateCSVs run in syncOperatorGroups and to catch provided API changes
4538+
// syncOperatorGroups is eventually consistent and may return errors until the cache has caught up with the cluster (fake client here)
4539+
wait.PollImmediateWithContext(ctx, tick, timeout, func(ctx context.Context) (bool, error) { // Throw away timeout errors since any timeout will coincide with err != nil anyway
4540+
err = op.syncOperatorGroups(operatorGroup)
4541+
return err == nil, nil
4542+
})
44904543
require.NoError(t, err)
44914544

4492-
// Sync csvs enough to get them back to succeeded state
4493-
for i := 0; i < 16; i++ {
4494-
opGroupCSVs, err := op.client.OperatorsV1alpha1().ClusterServiceVersions(operatorNamespace).List(context.TODO(), metav1.ListOptions{})
4495-
require.NoError(t, err)
4545+
// Sync csvs enough to get them back to a succeeded state
4546+
err = wait.PollImmediateWithContext(ctx, tick, timeout, func(ctx context.Context) (bool, error) {
4547+
csvs, err := op.client.OperatorsV1alpha1().ClusterServiceVersions(operatorNamespace).List(ctx, metav1.ListOptions{})
4548+
if err != nil {
4549+
return false, err
4550+
}
44964551

4497-
for i, obj := range opGroupCSVs.Items {
4498-
if obj.Status.Phase == v1alpha1.CSVPhaseInstalling {
4499-
simulateSuccessfulRollout(&obj, op.opClient)
4552+
for _, csv := range csvs.Items {
4553+
if csv.Status.Phase == v1alpha1.CSVPhaseInstalling {
4554+
simulateSuccessfulRollout(&csv)
45004555
}
4501-
err = op.syncClusterServiceVersion(&obj)
4502-
require.NoError(t, err, "%#v", obj)
45034556

4504-
err = op.syncCopyCSV(&obj)
4505-
if !tt.ignoreCopyError {
4506-
require.NoError(t, err, "%#v", obj)
4557+
if err := op.syncClusterServiceVersion(&csv); err != nil {
4558+
return false, err
45074559
}
45084560

4509-
if i == 0 {
4510-
err = wait.PollImmediate(1*time.Millisecond, 10*time.Second, func() (bool, error) {
4511-
for namespace, objects := range tt.final.objects {
4512-
if err := RequireObjectsInCache(t, op.lister, namespace, objects, false); err != nil {
4513-
return false, nil
4514-
}
4515-
}
4516-
return true, nil
4517-
})
4518-
require.NoError(t, err)
4561+
if err := op.syncCopyCSV(&csv); err != nil && !tt.ignoreCopyError {
4562+
return false, err
45194563
}
4564+
}
45204565

4521-
if i == 16 {
4522-
err = wait.PollImmediate(1*time.Millisecond, 10*time.Second, func() (bool, error) {
4523-
for namespace, objects := range tt.final.objects {
4524-
if err := RequireObjectsInCache(t, op.lister, namespace, objects, true); err != nil {
4525-
return false, nil
4526-
}
4527-
}
4528-
return true, nil
4529-
})
4530-
require.NoError(t, err)
4566+
for namespace, objects := range tt.final.objects {
4567+
if err := RequireObjectsInCache(t, op.lister, namespace, objects, true); err != nil {
4568+
return false, nil
45314569
}
45324570
}
4533-
}
45344571

4535-
operatorGroup, err := op.client.OperatorsV1().OperatorGroups(tt.initial.operatorGroup.GetNamespace()).Get(context.TODO(), tt.initial.operatorGroup.GetName(), metav1.GetOptions{})
4572+
return true, nil
4573+
})
4574+
require.NoError(t, err)
4575+
4576+
operatorGroup, err = op.client.OperatorsV1().OperatorGroups(operatorGroup.GetNamespace()).Get(ctx, operatorGroup.GetName(), metav1.GetOptions{})
45364577
require.NoError(t, err)
45374578
sort.Strings(tt.expectedStatus.Namespaces)
45384579
sort.Strings(operatorGroup.Status.Namespaces)
@@ -4799,7 +4840,7 @@ func RequireObjectsInNamespace(t *testing.T, opClient operatorclient.ClientInter
47994840
require.Failf(t, "couldn't find expected object", "%#v", object)
48004841
}
48014842
require.NoError(t, err, "couldn't fetch %s %v", namespace, object)
4802-
require.True(t, reflect.DeepEqual(object, fetched), diff.ObjectDiff(object, fetched))
4843+
require.True(t, reflect.DeepEqual(object, fetched), cmp.Diff(object, fetched))
48034844
}
48044845
}
48054846

pkg/controller/operators/olm/operatorgroup.go

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -532,13 +532,11 @@ func (a *Operator) ensureSingletonRBAC(operatorNamespace string, csv *v1alpha1.C
532532
}
533533
// TODO: this should do something smarter if the cluster role already exists
534534
if cr, err := a.opClient.CreateClusterRole(clusterRole); err != nil {
535-
if !k8serrors.IsAlreadyExists(err) {
536-
return err
537-
}
538-
// if the CR already exists, but the label is correct, the cache is just behind
539-
if cr != nil && ownerutil.IsOwnedByLabel(cr, csv) {
535+
// If the CR already exists, but the label is correct, the cache is just behind
536+
if k8serrors.IsAlreadyExists(err) && cr != nil && ownerutil.IsOwnedByLabel(cr, csv) {
540537
continue
541538
}
539+
return err
542540
}
543541
a.logger.Debug("created cluster role")
544542
}
@@ -573,11 +571,8 @@ func (a *Operator) ensureSingletonRBAC(operatorNamespace string, csv *v1alpha1.C
573571
}
574572
// TODO: this should do something smarter if the cluster role binding already exists
575573
if crb, err := a.opClient.CreateClusterRoleBinding(clusterRoleBinding); err != nil {
576-
if !k8serrors.IsAlreadyExists(err) {
577-
return err
578-
}
579-
// if the CRB already exists, but the label is correct, the cache is just behind
580-
if crb != nil && ownerutil.IsOwnedByLabel(crb, csv) {
574+
// If the CRB already exists, but the label is correct, the cache is just behind
575+
if k8serrors.IsAlreadyExists(err) && crb != nil && ownerutil.IsOwnedByLabel(crb, csv) {
581576
continue
582577
}
583578
return err

0 commit comments

Comments
 (0)