Skip to content

Commit 3568373

Browse files
awgreeneopenshift-cherrypick-robot
authored andcommitted
Order an operator CR's status.Component.Refs array (#2880)
Problem: The operator CR's status includes a list of componenets owned by the operator. The list of components are ordered by GVK, but the order of objects with the same GVK may change. If an operator owns many components, there is a high likelyhood that OLM will continuously update the status of the operator CR because the order of its components have changed, Solution: Order an operators component references so OLM does not attempt to update the status of the operator CR. Signed-off-by: Alexander Greene <[email protected]> Upstream-repository: operator-lifecycle-manager Upstream-commit: 793a7cc20d18f4907fc17ce2a0cebbca9a0f00be
1 parent 1770db7 commit 3568373

File tree

6 files changed

+103
-22
lines changed

6 files changed

+103
-22
lines changed

staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package decorators
22

33
import (
44
"fmt"
5+
"sort"
56
"strings"
67

78
"github.com/itchyny/gojq"
@@ -331,6 +332,23 @@ func (o *Operator) AddComponents(components ...runtime.Object) error {
331332

332333
o.Status.Components.Refs = append(o.Status.Components.Refs, refs...)
333334

335+
// Sort the component refs to so subsequent reconciles of the object do not change
336+
// the status and result in an update to the object.
337+
sort.SliceStable(o.Status.Components.Refs, func(i, j int) bool {
338+
if o.Status.Components.Refs[i].Kind != o.Status.Components.Refs[j].Kind {
339+
return o.Status.Components.Refs[i].Kind < o.Status.Components.Refs[j].Kind
340+
}
341+
342+
if o.Status.Components.Refs[i].APIVersion != o.Status.Components.Refs[j].APIVersion {
343+
return o.Status.Components.Refs[i].APIVersion < o.Status.Components.Refs[j].APIVersion
344+
}
345+
346+
if o.Status.Components.Refs[i].Namespace != o.Status.Components.Refs[j].Namespace {
347+
return o.Status.Components.Refs[i].Namespace < o.Status.Components.Refs[j].Namespace
348+
}
349+
return o.Status.Components.Refs[i].Name < o.Status.Components.Refs[j].Name
350+
})
351+
334352
return nil
335353
}
336354

staging/operator-lifecycle-manager/pkg/controller/operators/decorators/operator_test.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,22 @@ func TestAddComponents(t *testing.T) {
174174
},
175175
}
176176
operator.Status.Components.Refs = []operatorsv1.RichReference{
177+
{
178+
ObjectReference: &corev1.ObjectReference{
179+
APIVersion: operatorsv1alpha1.SchemeGroupVersion.String(),
180+
Kind: operatorsv1alpha1.ClusterServiceVersionKind,
181+
Namespace: "atlantic",
182+
Name: "puffin",
183+
},
184+
Conditions: []operatorsv1.Condition{
185+
{
186+
Type: operatorsv1.ConditionType(operatorsv1alpha1.CSVPhaseSucceeded),
187+
Status: corev1.ConditionTrue,
188+
Reason: string(operatorsv1alpha1.CSVReasonInstallSuccessful),
189+
Message: "this puffin is happy",
190+
},
191+
},
192+
},
177193
{
178194
ObjectReference: &corev1.ObjectReference{
179195
APIVersion: "v1",
@@ -195,22 +211,6 @@ func TestAddComponents(t *testing.T) {
195211
},
196212
},
197213
},
198-
{
199-
ObjectReference: &corev1.ObjectReference{
200-
APIVersion: operatorsv1alpha1.SchemeGroupVersion.String(),
201-
Kind: operatorsv1alpha1.ClusterServiceVersionKind,
202-
Namespace: "atlantic",
203-
Name: "puffin",
204-
},
205-
Conditions: []operatorsv1.Condition{
206-
{
207-
Type: operatorsv1.ConditionType(operatorsv1alpha1.CSVPhaseSucceeded),
208-
Status: corev1.ConditionTrue,
209-
Reason: string(operatorsv1alpha1.CSVReasonInstallSuccessful),
210-
Message: "this puffin is happy",
211-
},
212-
},
213-
},
214214
}
215215

216216
return operator

staging/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
corev1 "k8s.io/api/core/v1"
1010
rbacv1 "k8s.io/api/rbac/v1"
1111
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
12+
"k8s.io/apimachinery/pkg/api/equality"
1213
apierrors "k8s.io/apimachinery/pkg/api/errors"
1314
"k8s.io/apimachinery/pkg/api/meta"
1415
"k8s.io/apimachinery/pkg/labels"
@@ -151,9 +152,11 @@ func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
151152
return ctrl.Result{Requeue: true}, nil
152153
}
153154
} else {
154-
if err := r.Status().Update(ctx, operator.Operator); err != nil {
155-
log.Error(err, "Could not update Operator status")
156-
return ctrl.Result{Requeue: true}, nil
155+
if !equality.Semantic.DeepEqual(in.Status, operator.Operator.Status) {
156+
if err := r.Status().Update(ctx, operator.Operator); err != nil {
157+
log.Error(err, "Could not update Operator status")
158+
return ctrl.Result{Requeue: true}, nil
159+
}
157160
}
158161
}
159162

staging/operator-lifecycle-manager/pkg/controller/operators/operator_controller_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package operators
22

33
import (
44
"context"
5+
"fmt"
56

67
. "github.com/onsi/ginkgo"
78
. "github.com/onsi/gomega"
@@ -200,6 +201,44 @@ var _ = Describe("Operator Controller", func() {
200201
})
201202
})
202203

204+
Context("when multiple types of a gvk are labeled", func() {
205+
BeforeEach(func() {
206+
newObjs := make([]runtime.Object, 9)
207+
208+
// Create objects in reverse order to ensure they are eventually ordered alphabetically in the status of the operator.
209+
for i := 8; i >= 0; i-- {
210+
newObjs[8-i] = testobj.WithLabels(map[string]string{expectedKey: ""}, testobj.WithNamespacedName(
211+
&types.NamespacedName{Namespace: namespace, Name: fmt.Sprintf("sa-%d", i)}, &corev1.ServiceAccount{},
212+
))
213+
}
214+
215+
for _, obj := range newObjs {
216+
Expect(k8sClient.Create(ctx, obj.(client.Object))).To(Succeed())
217+
}
218+
219+
objs = append(objs, newObjs...)
220+
expectedRefs = append(expectedRefs, toRefs(scheme, newObjs...)...)
221+
})
222+
223+
It("should list each of the component references in alphabetical order by namespace and name", func() {
224+
Eventually(func() ([]operatorsv1.RichReference, error) {
225+
err := k8sClient.Get(ctx, name, operator)
226+
return operator.Status.Components.Refs, err
227+
}, timeout, interval).Should(ConsistOf(expectedRefs))
228+
229+
serviceAccountCount := 0
230+
for _, ref := range operator.Status.Components.Refs {
231+
if ref.Kind != rbacv1.ServiceAccountKind {
232+
continue
233+
}
234+
235+
Expect(ref.Name).Should(Equal(fmt.Sprintf("sa-%d", serviceAccountCount)))
236+
serviceAccountCount++
237+
}
238+
Expect(serviceAccountCount).To(Equal(9))
239+
})
240+
})
241+
203242
Context("when component labels are removed", func() {
204243
BeforeEach(func() {
205244
for _, obj := range testobj.StripLabel(expectedKey, objs...) {

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators/operator.go

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/operator_controller.go

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)