Skip to content

Commit 7fc9e15

Browse files
authored
Merge branch 'openshift:master' into master
2 parents 320831b + 104da7e commit 7fc9e15

File tree

9 files changed

+232
-22
lines changed

9 files changed

+232
-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"
@@ -152,9 +153,11 @@ func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
152153
return ctrl.Result{Requeue: true}, nil
153154
}
154155
} else {
155-
if err := r.Status().Update(ctx, operator.Operator); err != nil {
156-
log.Error(err, "Could not update Operator status")
157-
return ctrl.Result{Requeue: true}, nil
156+
if !equality.Semantic.DeepEqual(in.Status, operator.Operator.Status) {
157+
if err := r.Status().Update(ctx, operator.Operator); err != nil {
158+
log.Error(err, "Could not update Operator status")
159+
return ctrl.Result{Requeue: true}, nil
160+
}
158161
}
159162
}
160163

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/v2"
78
. "github.com/onsi/gomega"
@@ -204,6 +205,44 @@ var _ = Describe("Operator Controller", func() {
204205
})
205206
})
206207

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

209248
BeforeEach(func() {

staging/operator-lifecycle-manager/pkg/lib/scoped/token_retriever.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ func getAPISecret(logger logrus.FieldLogger, kubeclient operatorclient.ClientInt
8282
func filterSecretsBySAName(saName string, secrets *corev1.SecretList) map[string]*corev1.Secret {
8383
secretMap := make(map[string]*corev1.Secret)
8484
for _, ref := range secrets.Items {
85+
// Avoid referencing the "ref" created by the range-for loop as
86+
// the secret stored in the map will change if there are more
87+
// entries in the list of secrets that the range-for loop is
88+
// iterating over.
89+
ref := ref
90+
8591
annotations := ref.GetAnnotations()
8692
value := annotations[corev1.ServiceAccountNameKey]
8793
if value == saName {
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package scoped
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
corev1 "k8s.io/api/core/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
)
10+
11+
const serviceAccountName = "foo"
12+
13+
func TestFilterSecretsBySAName(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
secrets *corev1.SecretList
17+
wantedSecretNames []string
18+
}{
19+
{
20+
name: "NoSecretFound",
21+
secrets: &corev1.SecretList{
22+
Items: []corev1.Secret{
23+
*newSecret("aSecret"),
24+
*newSecret("someSecret"),
25+
*newSecret("zSecret"),
26+
},
27+
},
28+
wantedSecretNames: []string{},
29+
},
30+
{
31+
name: "FirstSecretFound",
32+
secrets: &corev1.SecretList{
33+
Items: []corev1.Secret{
34+
*newSecret("aSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
35+
*newSecret("someSecret"),
36+
*newSecret("zSecret"),
37+
},
38+
},
39+
wantedSecretNames: []string{"aSecret"},
40+
},
41+
{
42+
name: "SecondSecretFound",
43+
secrets: &corev1.SecretList{
44+
Items: []corev1.Secret{
45+
*newSecret("aSecret"),
46+
*newSecret("someSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
47+
*newSecret("zSecret"),
48+
},
49+
},
50+
wantedSecretNames: []string{"someSecret"},
51+
},
52+
{
53+
name: "ThirdSecretFound",
54+
secrets: &corev1.SecretList{
55+
Items: []corev1.Secret{
56+
*newSecret("aSecret"),
57+
*newSecret("someSecret"),
58+
*newSecret("zSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
59+
},
60+
},
61+
wantedSecretNames: []string{"zSecret"},
62+
},
63+
{
64+
name: "TwoSecretsFound",
65+
secrets: &corev1.SecretList{
66+
Items: []corev1.Secret{
67+
*newSecret("aSecret"),
68+
*newSecret("someSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
69+
*newSecret("zSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
70+
},
71+
},
72+
wantedSecretNames: []string{"someSecret", "zSecret"},
73+
},
74+
{
75+
name: "AllSecretsFound",
76+
secrets: &corev1.SecretList{
77+
Items: []corev1.Secret{
78+
*newSecret("aSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
79+
*newSecret("someSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
80+
*newSecret("zSecret", withAnnotations(map[string]string{corev1.ServiceAccountNameKey: serviceAccountName})),
81+
},
82+
},
83+
wantedSecretNames: []string{"aSecret", "someSecret", "zSecret"},
84+
},
85+
}
86+
87+
for _, tt := range tests {
88+
t.Run(tt.name, func(t *testing.T) {
89+
got := filterSecretsBySAName(serviceAccountName, tt.secrets)
90+
require.Equal(t, len(tt.wantedSecretNames), len(got))
91+
for _, wantedSecretName := range tt.wantedSecretNames {
92+
require.NotNil(t, got[wantedSecretName])
93+
require.Equal(t, wantedSecretName, got[wantedSecretName].GetName())
94+
}
95+
})
96+
}
97+
}
98+
99+
type secretOption func(*corev1.Secret)
100+
101+
func withAnnotations(annotations map[string]string) secretOption {
102+
return func(s *corev1.Secret) {
103+
s.SetAnnotations(annotations)
104+
}
105+
}
106+
107+
func newSecret(name string, opts ...secretOption) *corev1.Secret {
108+
s := &corev1.Secret{
109+
ObjectMeta: metav1.ObjectMeta{
110+
Name: name,
111+
},
112+
}
113+
for _, opt := range opts {
114+
opt(s)
115+
}
116+
return s
117+
}

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.

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/scoped/token_retriever.go

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

0 commit comments

Comments
 (0)