Skip to content

Commit ddba31f

Browse files
Merge pull request #2083 from ecordell/copied-csv-no-adopt
Do not adopt copied CSVs
2 parents ad235a6 + 288734f commit ddba31f

File tree

5 files changed

+187
-5
lines changed

5 files changed

+187
-5
lines changed

pkg/controller/operators/adoption_controller.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ func (r *AdoptionReconciler) ReconcileClusterServiceVersion(ctx context.Context,
187187
return reconcile.Result{}, err
188188
}
189189

190+
// disown copied csvs - a previous release inadvertently adopted them, this cleans any up if they are adopted
191+
if in.IsCopied() {
192+
err := r.disownFromAll(ctx, in)
193+
return reconcile.Result{}, err
194+
}
195+
190196
// Adopt all resources owned by the CSV if necessary
191197
return reconcile.Result{}, r.adoptComponents(ctx, in)
192198
}
@@ -315,6 +321,31 @@ func (r *AdoptionReconciler) disown(ctx context.Context, operator *decorators.Op
315321
return r.Patch(ctx, uCObj, client.MergeFrom(cObj))
316322
}
317323

324+
func (r *AdoptionReconciler) disownFromAll(ctx context.Context, component runtime.Object) error {
325+
cObj, ok := component.(client.Object)
326+
if !ok {
327+
return fmt.Errorf("Unable to typecast runtime.Object to client.Object")
328+
}
329+
var operators []decorators.Operator
330+
for _, name := range decorators.OperatorNames(cObj.GetLabels()) {
331+
o := &operatorsv1.Operator{}
332+
o.SetName(name.Name)
333+
operator, err := r.factory.NewOperator(o)
334+
if err != nil {
335+
return err
336+
}
337+
operators = append(operators, *operator)
338+
}
339+
errs := make([]error,0)
340+
for _, operator := range operators {
341+
if err := r.disown(ctx, &operator, component); err != nil {
342+
errs = append(errs, err)
343+
}
344+
}
345+
346+
return utilerrors.NewAggregate(errs)
347+
}
348+
318349
func (r *AdoptionReconciler) adoptees(ctx context.Context, operator decorators.Operator, csv *operatorsv1alpha1.ClusterServiceVersion) ([]runtime.Object, error) {
319350
// Note: We need to figure out how to dynamically add new list types here (or some equivalent) in
320351
// order to support operators composed of custom resources.

pkg/controller/operators/olm/operatorgroup.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
"github.com/operator-framework/api/pkg/operators/v1alpha1"
2020
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
21+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators"
2122
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
2223
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
2324
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
@@ -708,15 +709,23 @@ func (a *Operator) copyToNamespace(csv *v1alpha1.ClusterServiceVersion, namespac
708709
delete(newCSV.Annotations, v1.OperatorGroupTargetsAnnotationKey)
709710

710711
fetchedCSV, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(namespace).Get(newCSV.GetName())
711-
712712
logger = logger.WithField("csv", csv.GetName())
713713
if fetchedCSV != nil {
714714
logger.Debug("checking annotations")
715715

716-
if !reflect.DeepEqual(a.copyOperatorGroupAnnotations(&fetchedCSV.ObjectMeta), a.copyOperatorGroupAnnotations(&newCSV.ObjectMeta)) {
716+
if !reflect.DeepEqual(a.copyOperatorGroupAnnotations(&fetchedCSV.ObjectMeta), a.copyOperatorGroupAnnotations(&newCSV.ObjectMeta)) ||
717+
len(decorators.OperatorNames(fetchedCSV.GetLabels())) > 0 {
717718
// TODO: only copy over the opgroup annotations, not _all_ annotations
718719
fetchedCSV.Annotations = newCSV.Annotations
719720
fetchedCSV.SetLabels(utillabels.AddLabel(fetchedCSV.GetLabels(), v1alpha1.CopiedLabelKey, csv.GetNamespace()))
721+
722+
// remove Operator object component labels before copying so that copied CSVs do not show up in the component list
723+
for k := range fetchedCSV.GetLabels() {
724+
if strings.HasPrefix(k, decorators.ComponentLabelKeyPrefix) {
725+
delete(fetchedCSV.Labels, k)
726+
}
727+
}
728+
720729
// CRs don't support strategic merge patching, but in the future if they do this should be updated to patch
721730
logger.Debug("updating target CSV")
722731
if fetchedCSV, err = a.client.OperatorsV1alpha1().ClusterServiceVersions(namespace).Update(context.TODO(), fetchedCSV, metav1.UpdateOptions{}); err != nil {

pkg/controller/operators/olm/operatorgroup_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,65 @@ func TestCopyToNamespace(t *testing.T) {
121121
},
122122
},
123123
},
124+
{
125+
Name: "component labels are stripped before copy",
126+
Namespace: "bar",
127+
Original: &v1alpha1.ClusterServiceVersion{
128+
ObjectMeta: metav1.ObjectMeta{
129+
Name: "name",
130+
Namespace: "foo",
131+
Labels: map[string]string{
132+
"operators.coreos.com/foo": "",
133+
"operators.coreos.com/bar": "",
134+
"untouched": "fine",
135+
},
136+
},
137+
},
138+
ExistingCopy: &v1alpha1.ClusterServiceVersion{
139+
ObjectMeta: metav1.ObjectMeta{
140+
Name: "name",
141+
Namespace: "bar",
142+
Labels: map[string]string{
143+
"operators.coreos.com/foo": "",
144+
"operators.coreos.com/bar": "",
145+
"untouched": "fine",
146+
},
147+
},
148+
Status: v1alpha1.ClusterServiceVersionStatus{
149+
Message: "The operator is running in foo but is managing this namespace",
150+
Reason: v1alpha1.CSVReasonCopied},
151+
},
152+
ExpectedResult: &v1alpha1.ClusterServiceVersion{
153+
ObjectMeta: metav1.ObjectMeta{
154+
Name: "name",
155+
Namespace: "bar",
156+
Labels: map[string]string{
157+
"untouched": "fine",
158+
"olm.copiedFrom": "foo",
159+
},
160+
},
161+
Status: v1alpha1.ClusterServiceVersionStatus{
162+
Message: "The operator is running in foo but is managing this namespace",
163+
Reason: v1alpha1.CSVReasonCopied,
164+
},
165+
},
166+
ExpectedActions: []ktesting.Action{
167+
ktesting.NewUpdateAction(gvr, "bar", &v1alpha1.ClusterServiceVersion{
168+
ObjectMeta: metav1.ObjectMeta{
169+
Name: "name",
170+
Namespace: "bar",
171+
Labels: map[string]string{
172+
"untouched": "fine",
173+
"olm.copiedFrom": "foo",
174+
},
175+
},
176+
Status: v1alpha1.ClusterServiceVersionStatus{
177+
Message: "The operator is running in foo but is managing this namespace",
178+
Reason: v1alpha1.CSVReasonCopied,
179+
},
180+
}),
181+
},
182+
},
124183
} {
125184
t.Run(tc.Name, func(t *testing.T) {
126185
lister := &operatorlisterfakes.FakeOperatorLister{}

pkg/controller/operators/operatorcondition_controller_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ var _ = Describe("OperatorCondition", func() {
250250
}
251251
Expect(k8sClient.Create(ctx, operatorCondition)).To(Succeed())
252252
})
253-
It("does not injected the OperatorCondition name into the deployment's Environment Variables", func() {
253+
It("does not inject the OperatorCondition name into the deployment's Environment Variables", func() {
254254
deployment := &appsv1.Deployment{}
255255
Consistently(func() error {
256256
err := k8sClient.Get(ctx, types.NamespacedName{Name: operatorCondition.Spec.Deployments[0], Namespace: namespace.GetName()}, deployment)

test/e2e/operator_test.go

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
)
2929

3030
// Describes test specs for the Operator resource.
31-
var _ = Describe("Operator", func() {
31+
var _ = Describe("Operator API", func() {
3232
var (
3333
clientCtx context.Context
3434
scheme *runtime.Scheme
@@ -74,6 +74,8 @@ var _ = Describe("Operator", func() {
7474
o := &operatorsv1.Operator{}
7575
o.SetName(genName("o-"))
7676

77+
Consistently(o).ShouldNot(ContainCopiedCSVReferences())
78+
7779
Eventually(func() error {
7880
return client.Create(clientCtx, o)
7981
}).Should(Succeed())
@@ -327,6 +329,12 @@ var _ = Describe("Operator", func() {
327329
})
328330

329331
It("should automatically adopt components", func() {
332+
Consistently(func() (*operatorsv1.Operator, error) {
333+
o := &operatorsv1.Operator{}
334+
err := client.Get(clientCtx, operatorName, o)
335+
return o, err
336+
}).ShouldNot(ContainCopiedCSVReferences())
337+
330338
Eventually(func() (*operatorsv1.Operator, error) {
331339
o := &operatorsv1.Operator{}
332340
err := client.Get(clientCtx, operatorName, o)
@@ -346,8 +354,36 @@ var _ = Describe("Operator", func() {
346354
getReference(scheme, testobj.WithName("monitoringdashboards.monitoring.kiali.io", &apiextensionsv1.CustomResourceDefinition{})),
347355
}))
348356
})
349-
})
350357

358+
Context("when a namespace is added", func() {
359+
var newNs *corev1.Namespace
360+
361+
BeforeEach(func() {
362+
// Subscribe to a package and await a successful install
363+
newNs = &corev1.Namespace{}
364+
newNs.SetName(genName("ns-"))
365+
Eventually(func() error {
366+
return client.Create(clientCtx, newNs)
367+
}).Should(Succeed())
368+
})
369+
AfterEach(func() {
370+
Eventually(func() error {
371+
err := client.Delete(clientCtx, newNs)
372+
if apierrors.IsNotFound(err) {
373+
return nil
374+
}
375+
return err
376+
}).Should(Succeed())
377+
})
378+
It("should not adopt copied csvs", func() {
379+
Consistently(func() (*operatorsv1.Operator, error) {
380+
o := &operatorsv1.Operator{}
381+
err := client.Get(clientCtx, operatorName, o)
382+
return o, err
383+
}).ShouldNot(ContainCopiedCSVReferences())
384+
})
385+
})
386+
})
351387
})
352388

353389
func getReference(scheme *runtime.Scheme, obj runtime.Object) *corev1.ObjectReference {
@@ -380,6 +416,53 @@ func componentRefEventuallyExists(w watch.Interface, exists bool, ref *corev1.Ob
380416
}))
381417
}
382418

419+
func ContainCopiedCSVReferences() gomegatypes.GomegaMatcher {
420+
return &copiedCSVRefMatcher{}
421+
}
422+
423+
type copiedCSVRefMatcher struct {
424+
}
425+
426+
func (matcher *copiedCSVRefMatcher) Match(actual interface{}) (success bool, err error) {
427+
if actual == nil {
428+
return false, nil
429+
}
430+
operator, ok := actual.(*operatorsv1.Operator)
431+
if !ok {
432+
return false, fmt.Errorf("copiedCSVRefMatcher matcher expects an *Operator")
433+
}
434+
if operator.Status.Components == nil {
435+
return false, nil
436+
}
437+
for _, ref := range operator.Status.Components.Refs {
438+
if ref.Kind != operatorsv1alpha1.ClusterServiceVersionKind {
439+
continue
440+
}
441+
for _, c := range ref.Conditions {
442+
if c.Reason == string(operatorsv1alpha1.CSVReasonCopied) {
443+
return true, nil
444+
}
445+
}
446+
}
447+
return false, nil
448+
}
449+
450+
func (matcher *copiedCSVRefMatcher) FailureMessage(actual interface{}) (message string) {
451+
operator, ok := actual.(*operatorsv1.Operator)
452+
if !ok {
453+
return fmt.Sprintf("copiedCSVRefMatcher matcher expects an *Operator")
454+
}
455+
return fmt.Sprintf("Expected\n\t%#v\nto contain copied CSVs in components\n\t%#v\n", operator, operator.Status.Components)
456+
}
457+
458+
func (matcher *copiedCSVRefMatcher) NegatedFailureMessage(actual interface{}) (message string) {
459+
operator, ok := actual.(*operatorsv1.Operator)
460+
if !ok {
461+
return fmt.Sprintf("copiedCSVRefMatcher matcher expects an *Operator")
462+
}
463+
return fmt.Sprintf("Expected\n\t%#v\nto not contain copied CSVs in components\n\t%#v\n", operator, operator.Status.Components)
464+
}
465+
383466
func operatorPredicate(fn func(*operatorsv1.Operator) bool) predicateFunc {
384467
return func(event watch.Event) bool {
385468
o, ok := event.Object.(*operatorsv1.Operator)

0 commit comments

Comments
 (0)