Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 057af8c

Browse files
committedJan 9, 2024
Add finalizer for CSV cleanup
Remove old CSV cleanup code Cleanup CRB/CR/MWC/VWC via finalizer. Signed-off-by: Todd Short <[email protected]>
1 parent 4d6d11b commit 057af8c

File tree

2 files changed

+108
-195
lines changed

2 files changed

+108
-195
lines changed
 

Diff for: ‎pkg/controller/operators/olm/operator.go

+2-195
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/labeller"
1212
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins"
1313
"github.com/sirupsen/logrus"
14-
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
1514
appsv1 "k8s.io/api/apps/v1"
1615
corev1 "k8s.io/api/core/v1"
1716
rbacv1 "k8s.io/api/rbac/v1"
@@ -50,7 +49,6 @@ import (
5049
csvutility "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv"
5150
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/event"
5251
index "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/index"
53-
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubestate"
5452
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/labeler"
5553
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
5654
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister"
@@ -86,7 +84,6 @@ type Operator struct {
8684
olmConfigQueue workqueue.RateLimitingInterface
8785
csvCopyQueueSet *queueinformer.ResourceQueueSet
8886
copiedCSVGCQueueSet *queueinformer.ResourceQueueSet
89-
objGCQueueSet *queueinformer.ResourceQueueSet
9087
nsQueueSet workqueue.RateLimitingInterface
9188
apiServiceQueue workqueue.RateLimitingInterface
9289
csvIndexers map[string]cache.Indexer
@@ -202,7 +199,6 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
202199
olmConfigQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "olmConfig"),
203200
csvCopyQueueSet: queueinformer.NewEmptyResourceQueueSet(),
204201
copiedCSVGCQueueSet: queueinformer.NewEmptyResourceQueueSet(),
205-
objGCQueueSet: queueinformer.NewEmptyResourceQueueSet(),
206202
apiServiceQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "apiservice"),
207203
resolver: config.strategyResolver,
208204
apiReconciler: config.apiReconciler,
@@ -480,21 +476,6 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
480476
if err := op.RegisterQueueInformer(serviceAccountQueueInformer); err != nil {
481477
return nil, err
482478
}
483-
484-
objGCQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), fmt.Sprintf("%s/obj-gc", namespace))
485-
op.objGCQueueSet.Set(namespace, objGCQueue)
486-
objGCQueueInformer, err := queueinformer.NewQueue(
487-
ctx,
488-
queueinformer.WithLogger(op.logger),
489-
queueinformer.WithQueue(objGCQueue),
490-
queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncGCObject).ToSyncer()),
491-
)
492-
if err != nil {
493-
return nil, err
494-
}
495-
if err := op.RegisterQueueInformer(objGCQueueInformer); err != nil {
496-
return nil, err
497-
}
498479
}
499480

500481
complete := map[schema.GroupVersionResource][]bool{}
@@ -568,22 +549,6 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
568549
return nil, err
569550
}
570551

571-
// add queue for all namespaces as well
572-
objGCQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), fmt.Sprintf("%s/obj-gc", ""))
573-
op.objGCQueueSet.Set("", objGCQueue)
574-
objGCQueueInformer, err := queueinformer.NewQueue(
575-
ctx,
576-
queueinformer.WithLogger(op.logger),
577-
queueinformer.WithQueue(objGCQueue),
578-
queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncGCObject).ToSyncer()),
579-
)
580-
if err != nil {
581-
return nil, err
582-
}
583-
if err := op.RegisterQueueInformer(objGCQueueInformer); err != nil {
584-
return nil, err
585-
}
586-
587552
// Register QueueInformer for olmConfig
588553
olmConfigInformer := externalversions.NewSharedInformerFactoryWithOptions(
589554
op.client,
@@ -948,98 +913,6 @@ func (a *Operator) EnsureCSVMetric() error {
948913
return nil
949914
}
950915

951-
func (a *Operator) syncGCObject(obj interface{}) (syncError error) {
952-
metaObj, ok := obj.(metav1.Object)
953-
if !ok {
954-
a.logger.Warn("object sync: casting to metav1.Object failed")
955-
return
956-
}
957-
logger := a.logger.WithFields(logrus.Fields{
958-
"name": metaObj.GetName(),
959-
"namespace": metaObj.GetNamespace(),
960-
"self": metaObj.GetSelfLink(),
961-
})
962-
963-
switch metaObj.(type) {
964-
case *rbacv1.ClusterRole:
965-
if name, ns, ok := ownerutil.GetOwnerByKindLabel(metaObj, v1alpha1.ClusterServiceVersionKind); ok {
966-
_, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ns).Get(name)
967-
if err == nil {
968-
logger.Debugf("CSV still present, must wait until it is deleted (owners=%v/%v)", ns, name)
969-
syncError = fmt.Errorf("cleanup must wait")
970-
return
971-
} else if !apierrors.IsNotFound(err) {
972-
syncError = err
973-
return
974-
}
975-
}
976-
977-
if err := a.opClient.DeleteClusterRole(metaObj.GetName(), &metav1.DeleteOptions{}); err != nil {
978-
logger.WithError(err).Warn("cannot delete cluster role")
979-
break
980-
}
981-
logger.Debugf("Deleted cluster role %v due to no owning CSV", metaObj.GetName())
982-
case *rbacv1.ClusterRoleBinding:
983-
if name, ns, ok := ownerutil.GetOwnerByKindLabel(metaObj, v1alpha1.ClusterServiceVersionKind); ok {
984-
_, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ns).Get(name)
985-
if err == nil {
986-
logger.Debugf("CSV still present, must wait until it is deleted (owners=%v)", name)
987-
syncError = fmt.Errorf("cleanup must wait")
988-
return
989-
} else if !apierrors.IsNotFound(err) {
990-
syncError = err
991-
return
992-
}
993-
}
994-
995-
if err := a.opClient.DeleteClusterRoleBinding(metaObj.GetName(), &metav1.DeleteOptions{}); err != nil {
996-
logger.WithError(err).Warn("cannot delete cluster role binding")
997-
break
998-
}
999-
logger.Debugf("Deleted cluster role binding %v due to no owning CSV", metaObj.GetName())
1000-
case *admissionregistrationv1.MutatingWebhookConfiguration:
1001-
if name, ns, ok := ownerutil.GetOwnerByKindLabel(metaObj, v1alpha1.ClusterServiceVersionKind); ok {
1002-
_, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ns).Get(name)
1003-
if err == nil {
1004-
logger.Debugf("CSV still present, must wait until it is deleted (owners=%v)", name)
1005-
syncError = fmt.Errorf("cleanup must wait")
1006-
return
1007-
} else if !apierrors.IsNotFound(err) {
1008-
logger.Infof("error CSV retrieval error")
1009-
syncError = err
1010-
return
1011-
}
1012-
}
1013-
1014-
if err := a.opClient.KubernetesInterface().AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(context.TODO(), metaObj.GetName(), metav1.DeleteOptions{}); err != nil {
1015-
logger.WithError(err).Warn("cannot delete MutatingWebhookConfiguration")
1016-
break
1017-
}
1018-
logger.Debugf("Deleted MutatingWebhookConfiguration %v due to no owning CSV", metaObj.GetName())
1019-
case *admissionregistrationv1.ValidatingWebhookConfiguration:
1020-
if name, ns, ok := ownerutil.GetOwnerByKindLabel(metaObj, v1alpha1.ClusterServiceVersionKind); ok {
1021-
_, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ns).Get(name)
1022-
if err == nil {
1023-
logger.Debugf("CSV still present, must wait until it is deleted (owners=%v)", name)
1024-
syncError = fmt.Errorf("cleanup must wait")
1025-
return
1026-
} else if !apierrors.IsNotFound(err) {
1027-
logger.Infof("Error CSV retrieval error")
1028-
syncError = err
1029-
return
1030-
}
1031-
}
1032-
1033-
if err := a.opClient.KubernetesInterface().AdmissionregistrationV1().ValidatingWebhookConfigurations().Delete(context.TODO(), metaObj.GetName(), metav1.DeleteOptions{}); err != nil {
1034-
logger.WithError(err).Warn("cannot delete ValidatingWebhookConfiguration")
1035-
break
1036-
}
1037-
logger.Debugf("Deleted ValidatingWebhookConfiguration %v due to no owning CSV", metaObj.GetName())
1038-
}
1039-
1040-
return
1041-
}
1042-
1043916
func (a *Operator) syncObject(obj interface{}) (syncError error) {
1044917
// Assert as metav1.Object
1045918
metaObj, ok := obj.(metav1.Object)
@@ -1054,30 +927,8 @@ func (a *Operator) syncObject(obj interface{}) (syncError error) {
1054927
"self": metaObj.GetSelfLink(),
1055928
})
1056929

1057-
// Requeues objects that can't have ownerrefs (cluster -> namespace, cross-namespace)
1058-
if ownerutil.IsOwnedByKindLabel(metaObj, v1alpha1.ClusterServiceVersionKind) {
1059-
name, ns, ok := ownerutil.GetOwnerByKindLabel(metaObj, v1alpha1.ClusterServiceVersionKind)
1060-
if !ok {
1061-
logger.Error("unexpected owner label retrieval failure")
1062-
}
1063-
_, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(ns).Get(name)
1064-
if !apierrors.IsNotFound(err) {
1065-
logger.Debug("requeueing owner csvs from owner label")
1066-
a.requeueOwnerCSVs(metaObj)
1067-
} else {
1068-
switch metaObj.(type) {
1069-
case *rbacv1.ClusterRole, *rbacv1.ClusterRoleBinding, *admissionregistrationv1.MutatingWebhookConfiguration, *admissionregistrationv1.ValidatingWebhookConfiguration:
1070-
resourceEvent := kubestate.NewResourceEvent(
1071-
kubestate.ResourceUpdated,
1072-
metaObj,
1073-
)
1074-
if syncError = a.objGCQueueSet.RequeueEvent("", resourceEvent); syncError != nil {
1075-
logger.WithError(syncError).Warnf("failed to requeue gc event: %v", resourceEvent)
1076-
}
1077-
return
1078-
}
1079-
}
1080-
}
930+
// Objects that can't have ownerrefs (cluster -> namespace, cross-namespace)
931+
// are handled by finalizer
1081932

1082933
// Requeue all owner CSVs
1083934
if ownerutil.IsOwnedByKind(metaObj, v1alpha1.ClusterServiceVersionKind) {
@@ -1332,50 +1183,6 @@ func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) {
13321183
}
13331184
}
13341185

1335-
ownerSelector := ownerutil.CSVOwnerSelector(clusterServiceVersion)
1336-
crbs, err := a.lister.RbacV1().ClusterRoleBindingLister().List(ownerSelector)
1337-
if err != nil {
1338-
logger.WithError(err).Warn("cannot list cluster role bindings")
1339-
}
1340-
for _, crb := range crbs {
1341-
if err := a.objGCQueueSet.RequeueEvent("", kubestate.NewResourceEvent(kubestate.ResourceUpdated, crb)); err != nil {
1342-
logger.WithError(err).Warnf("failed to requeue gc event: %v", crb)
1343-
}
1344-
}
1345-
1346-
crs, err := a.lister.RbacV1().ClusterRoleLister().List(ownerSelector)
1347-
if err != nil {
1348-
logger.WithError(err).Warn("cannot list cluster roles")
1349-
}
1350-
for _, cr := range crs {
1351-
if err := a.objGCQueueSet.RequeueEvent("", kubestate.NewResourceEvent(kubestate.ResourceUpdated, cr)); err != nil {
1352-
logger.WithError(err).Warnf("failed to requeue gc event: %v", cr)
1353-
}
1354-
}
1355-
1356-
webhookSelector := labels.SelectorFromSet(ownerutil.OwnerLabel(clusterServiceVersion, v1alpha1.ClusterServiceVersionKind)).String()
1357-
mWebhooks, err := a.opClient.KubernetesInterface().AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.TODO(), metav1.ListOptions{LabelSelector: webhookSelector})
1358-
if err != nil {
1359-
logger.WithError(err).Warn("cannot list MutatingWebhookConfigurations")
1360-
}
1361-
for _, webhook := range mWebhooks.Items {
1362-
w := webhook
1363-
if err := a.objGCQueueSet.RequeueEvent("", kubestate.NewResourceEvent(kubestate.ResourceUpdated, &w)); err != nil {
1364-
logger.WithError(err).Warnf("failed to requeue gc event: %v", webhook)
1365-
}
1366-
}
1367-
1368-
vWebhooks, err := a.opClient.KubernetesInterface().AdmissionregistrationV1().ValidatingWebhookConfigurations().List(context.TODO(), metav1.ListOptions{LabelSelector: webhookSelector})
1369-
if err != nil {
1370-
logger.WithError(err).Warn("cannot list ValidatingWebhookConfigurations")
1371-
}
1372-
for _, webhook := range vWebhooks.Items {
1373-
w := webhook
1374-
if err := a.objGCQueueSet.RequeueEvent("", kubestate.NewResourceEvent(kubestate.ResourceUpdated, &w)); err != nil {
1375-
logger.WithError(err).Warnf("failed to requeue gc event: %v", webhook)
1376-
}
1377-
}
1378-
13791186
// Conversion webhooks are defined within a CRD.
13801187
// In an effort to prevent customer dataloss, OLM does not delete CRDs associated with a CSV when it is deleted.
13811188
// Deleting a CSV that introduced a conversion webhook removes the deployment that serviced the conversion webhook calls.

Diff for: ‎pkg/controller/operators/operatorconditiongenerator_controller.go

+106
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@ package operators
22

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

78
"github.com/go-logr/logr"
9+
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
10+
rbacv1 "k8s.io/api/rbac/v1"
811
apierrors "k8s.io/apimachinery/pkg/api/errors"
912
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1013
"k8s.io/apimachinery/pkg/runtime"
14+
utilerrors "k8s.io/apimachinery/pkg/util/errors"
1115
ctrl "sigs.k8s.io/controller-runtime"
1216
"sigs.k8s.io/controller-runtime/pkg/builder"
1317
"sigs.k8s.io/controller-runtime/pkg/client"
18+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1419
"sigs.k8s.io/controller-runtime/pkg/event"
1520
"sigs.k8s.io/controller-runtime/pkg/handler"
1621
"sigs.k8s.io/controller-runtime/pkg/predicate"
@@ -95,6 +100,10 @@ func (r *OperatorConditionGeneratorReconciler) Reconcile(ctx context.Context, re
95100
return ctrl.Result{}, client.IgnoreNotFound(err)
96101
}
97102

103+
if err, ok := r.processFinalizer(ctx, in); !ok {
104+
return ctrl.Result{}, err
105+
}
106+
98107
operatorCondition := &operatorsv2.OperatorCondition{
99108
ObjectMeta: metav1.ObjectMeta{
100109
// For now, only generate an OperatorCondition with the same name as the csv.
@@ -170,3 +179,100 @@ func (r *OperatorConditionGeneratorReconciler) ensureOperatorCondition(operatorC
170179
existingOperatorCondition.Spec.ServiceAccounts = operatorCondition.Spec.ServiceAccounts
171180
return r.Client.Update(context.TODO(), existingOperatorCondition)
172181
}
182+
183+
// Return values, err, ok; ok == true: continue Reconcile, ok == false: exit Reconcile
184+
func (r *OperatorConditionGeneratorReconciler) processFinalizer(ctx context.Context, csv *operatorsv1alpha1.ClusterServiceVersion) (error, bool) {
185+
myFinalizerName := "operators.coreos.com/csv-cleanup"
186+
log := r.log.WithValues("name", csv.GetName()).WithValues("namespace", csv.GetNamespace())
187+
188+
if csv.ObjectMeta.DeletionTimestamp.IsZero() {
189+
// CSV is not being deleted, add finalizer if not present
190+
if !controllerutil.ContainsFinalizer(csv, myFinalizerName) {
191+
patch := client.MergeFrom(csv.DeepCopy())
192+
controllerutil.AddFinalizer(csv, myFinalizerName)
193+
if err := r.Client.Patch(ctx, csv, patch); err != nil {
194+
log.Error(err, "Adding finalizer")
195+
return err, false
196+
}
197+
}
198+
return nil, true
199+
}
200+
201+
if !controllerutil.ContainsFinalizer(csv, myFinalizerName) {
202+
// Finalizer has been removed; stop reconciliation as the CSV is being deleted
203+
return nil, false
204+
}
205+
206+
// CSV is being deleted and the finalizer still present; do any clean up
207+
ownerSelector := ownerutil.CSVOwnerSelector(csv)
208+
listOptions := client.ListOptions{
209+
LabelSelector: ownerSelector,
210+
}
211+
deleteOptions := client.DeleteAllOfOptions{
212+
ListOptions: listOptions,
213+
}
214+
// Look for resources owned by this CSV, and delete them.
215+
log.WithValues("selector", ownerSelector).Info("Cleaning up resources after CSV deletion")
216+
var errs []error
217+
218+
err := r.Client.DeleteAllOf(ctx, &rbacv1.ClusterRoleBinding{}, &deleteOptions)
219+
if client.IgnoreNotFound(err) != nil {
220+
log.Error(err, "Deleting ClusterRoleBindings on CSV delete")
221+
errs = append(errs, err)
222+
}
223+
224+
err = r.Client.DeleteAllOf(ctx, &rbacv1.ClusterRole{}, &deleteOptions)
225+
if client.IgnoreNotFound(err) != nil {
226+
log.Error(err, "Deleting ClusterRoles on CSV delete")
227+
errs = append(errs, err)
228+
}
229+
230+
err = r.Client.DeleteAllOf(ctx, &admissionregistrationv1.MutatingWebhookConfiguration{}, &deleteOptions)
231+
if client.IgnoreNotFound(err) != nil {
232+
log.Error(err, "Deleting MutatingWebhookConfigurations on CSV delete")
233+
errs = append(errs, err)
234+
}
235+
236+
err = r.Client.DeleteAllOf(ctx, &admissionregistrationv1.ValidatingWebhookConfiguration{}, &deleteOptions)
237+
if client.IgnoreNotFound(err) != nil {
238+
log.Error(err, "Deleting ValidatingWebhookConfigurations on CSV delete")
239+
errs = append(errs, err)
240+
}
241+
242+
// Make sure things are deleted
243+
crbList := &rbacv1.ClusterRoleBindingList{}
244+
if _ = r.Client.List(ctx, crbList, &listOptions); len(crbList.Items) != 0 {
245+
errs = append(errs, fmt.Errorf("waiting for ClusterRoleBindings to delete"))
246+
}
247+
248+
crList := &rbacv1.ClusterRoleList{}
249+
if _ = r.Client.List(ctx, crList, &listOptions); len(crList.Items) != 0 {
250+
errs = append(errs, fmt.Errorf("waiting for ClusterRoles to delete"))
251+
}
252+
253+
mwcList := &admissionregistrationv1.MutatingWebhookConfigurationList{}
254+
if _ = r.Client.List(ctx, mwcList, &listOptions); len(mwcList.Items) != 0 {
255+
errs = append(errs, fmt.Errorf("waiting for MutatingWebhookConfigurations to delete"))
256+
}
257+
258+
vwcList := &admissionregistrationv1.ValidatingWebhookConfigurationList{}
259+
if _ = r.Client.List(ctx, vwcList, &listOptions); len(vwcList.Items) != 0 {
260+
errs = append(errs, fmt.Errorf("waiting for ValidatingWebhookConfigurations to delete"))
261+
}
262+
263+
// Return any errors
264+
if err := utilerrors.NewAggregate(errs); err != nil {
265+
return err, false
266+
}
267+
268+
// If no errors, remove our finalizer from the CSV and update
269+
patch := client.MergeFrom(csv.DeepCopy())
270+
controllerutil.RemoveFinalizer(csv, myFinalizerName)
271+
if err := r.Client.Patch(ctx, csv, patch); err != nil {
272+
log.Error(err, "Removing finalizer")
273+
return err, false
274+
}
275+
276+
// Stop reconciliation as the csv is being deleted
277+
return nil, false
278+
}

0 commit comments

Comments
 (0)
Please sign in to comment.