Skip to content

Commit fae8f3b

Browse files
Merge pull request #1282 from dprince/csv_cleanup
Cleanup legacy OLM Subscription & CSVs
2 parents 5acf791 + c361674 commit fae8f3b

File tree

4 files changed

+240
-24
lines changed

4 files changed

+240
-24
lines changed

config/operator/rbac/role.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ rules:
103103
- get
104104
- patch
105105
- update
106+
- apiGroups:
107+
- operators.coreos.com
108+
resources:
109+
- clusterserviceversions
110+
- installplans
111+
- operators
112+
- subscriptions
113+
verbs:
114+
- delete
115+
- get
116+
- list
106117
- apiGroups:
107118
- rbac.authorization.k8s.io
108119
resources:

controllers/operator/openstack_controller.go

Lines changed: 208 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"path/filepath"
2424
"sort"
2525
"strings"
26+
"time"
2627

2728
"k8s.io/apimachinery/pkg/runtime"
2829
"k8s.io/client-go/kubernetes"
@@ -31,6 +32,8 @@ import (
3132
"sigs.k8s.io/controller-runtime/pkg/handler"
3233
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3334

35+
"k8s.io/apimachinery/pkg/runtime/schema"
36+
3437
"github.com/go-logr/logr"
3538
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
3639
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
@@ -41,6 +44,7 @@ import (
4144
appsv1 "k8s.io/api/apps/v1"
4245
apierrors "k8s.io/apimachinery/pkg/api/errors"
4346
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
47+
uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
4448
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
4549
"sigs.k8s.io/controller-runtime/pkg/log"
4650
)
@@ -58,7 +62,7 @@ type OpenStackReconciler struct {
5862

5963
// GetLog returns a logger object with a prefix of "controller.name" and aditional controller context fields
6064
func (r *OpenStackReconciler) GetLogger(ctx context.Context) logr.Logger {
61-
return log.FromContext(ctx).WithName("Controllers").WithName("OpenStackControlPlane")
65+
return log.FromContext(ctx).WithName("Controllers").WithName("OpenStackOperator")
6266
}
6367

6468
var (
@@ -110,6 +114,7 @@ func SetupEnv() {
110114
// +kubebuilder:rbac:groups=cert-manager.io,resources=issuers,verbs=get;list;watch;create;update;patch;delete;
111115
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates,verbs=get;list;watch;create;update;patch;delete;
112116
// +kubebuilder:rbac:groups="monitoring.coreos.com",resources=servicemonitors,verbs=list;get;watch;update;create
117+
// +kubebuilder:rbac:groups=operators.coreos.com,resources=clusterserviceversions;subscriptions;installplans;operators,verbs=get;list;delete;
113118

114119
// Reconcile is part of the main kubernetes reconciliation loop which aims to
115120
// move the current state of the cluster closer to the desired state.
@@ -228,12 +233,10 @@ func (r *OpenStackReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
228233
return r.reconcileDelete(ctx, instance, openstackHelper)
229234
}
230235

231-
// TODO: cleanup obsolete resources here (remove old CSVs, etc)
232-
/*
233-
if err := r.cleanupObsoleteResources(ctx); err != nil {
234-
return ctrl.Result{}, err
235-
}
236-
*/
236+
// cleanup obsolete resources here (remove old CSVs, etc)
237+
if err := r.cleanupObsoleteResources(ctx, instance); err != nil {
238+
return ctrl.Result{}, err
239+
}
237240

238241
if err := r.applyManifests(ctx, instance); err != nil {
239242
instance.Status.Conditions.Set(condition.FalseCondition(
@@ -245,6 +248,12 @@ func (r *OpenStackReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
245248
return ctrl.Result{}, err
246249
}
247250

251+
// now that CRDs have been updated (with old olm.managed references removed)
252+
// we can finally cleanup the old operators
253+
if err := r.postCleanupObsoleteResources(ctx, instance); err != nil {
254+
return ctrl.Result{RequeueAfter: time.Duration(5) * time.Second}, err
255+
}
256+
248257
// Check if all deployments are running
249258
deploymentsRunning, err := r.countDeployments(ctx, instance)
250259
instance.Status.DeployedOperatorCount = &deploymentsRunning
@@ -422,6 +431,198 @@ func (r *OpenStackReconciler) renderAndApply(
422431
return nil
423432
}
424433

434+
func isServiceOperatorResource(name string) bool {
435+
//NOTE: test-operator was deployed as a independant package so it may or may not be installed
436+
//NOTE: depending on how watcher-operator is released for FR2 and then in FR3 it may need to be
437+
// added into this list in the future
438+
serviceOperatorNames := []string{"barbican", "cinder", "designate", "glance", "heat", "horizon", "infra",
439+
"ironic", "keystone", "manila", "mariadb", "neutron", "nova", "octavia", "openstack-baremetal", "ovn",
440+
"placement", "rabbitmq-cluster", "swift", "telemetry", "test"}
441+
442+
for _, item := range serviceOperatorNames {
443+
if strings.Index(name, item) == 0 {
444+
return true
445+
}
446+
}
447+
return false
448+
}
449+
450+
// cleanupObsoleteResources - deletes CSVs and subscriptions
451+
func (r *OpenStackReconciler) cleanupObsoleteResources(ctx context.Context, instance *operatorv1beta1.OpenStack) error {
452+
Log := r.GetLogger(ctx)
453+
454+
csvGVR := schema.GroupVersionResource{
455+
Group: "operators.coreos.com",
456+
Version: "v1alpha1",
457+
Resource: "clusterserviceversions",
458+
}
459+
460+
subscriptionGVR := schema.GroupVersionResource{
461+
Group: "operators.coreos.com",
462+
Version: "v1alpha1",
463+
Resource: "subscriptions",
464+
}
465+
466+
installPlanGVR := schema.GroupVersionResource{
467+
Group: "operators.coreos.com",
468+
Version: "v1alpha1",
469+
Resource: "installplans",
470+
}
471+
472+
csvList := &uns.UnstructuredList{}
473+
csvList.SetGroupVersionKind(csvGVR.GroupVersion().WithKind("ClusterServiceVersion"))
474+
err := r.Client.List(ctx, csvList, &client.ListOptions{Namespace: instance.Namespace})
475+
if err != nil {
476+
return err
477+
}
478+
for _, csv := range csvList.Items {
479+
Log.Info("Found CSV", "name", csv.GetName())
480+
if isServiceOperatorResource(csv.GetName()) {
481+
err = r.Client.Delete(ctx, &csv)
482+
if err != nil {
483+
if apierrors.IsNotFound(err) {
484+
Log.Info("CSV not found on delete. Continuing...", "name", csv.GetName())
485+
continue
486+
}
487+
return err
488+
}
489+
Log.Info("CSV deleted successfully", "name", csv.GetName())
490+
}
491+
}
492+
493+
subscriptionList := &uns.UnstructuredList{}
494+
subscriptionList.SetGroupVersionKind(subscriptionGVR.GroupVersion().WithKind("Subscription"))
495+
err = r.Client.List(ctx, subscriptionList, &client.ListOptions{Namespace: instance.Namespace})
496+
if err != nil {
497+
return err
498+
}
499+
for _, subscription := range subscriptionList.Items {
500+
Log.Info("Found Subscription", "name", subscription.GetName())
501+
if isServiceOperatorResource(subscription.GetName()) {
502+
err = r.Client.Delete(ctx, &subscription)
503+
if err != nil {
504+
if apierrors.IsNotFound(err) {
505+
Log.Info("Subscription not found on delete. Continuing...", "name", subscription.GetName())
506+
continue
507+
}
508+
return err
509+
}
510+
Log.Info("Subscription deleted successfully", "name", subscription.GetName())
511+
}
512+
}
513+
514+
// lookup the installplan which has the clusterServiceVersionNames we removed above
515+
// there will be just a single installPlan that has all of them referenced
516+
installPlanList := &uns.UnstructuredList{}
517+
installPlanList.SetGroupVersionKind(installPlanGVR.GroupVersion().WithKind("InstallPlan"))
518+
519+
err = r.Client.List(ctx, installPlanList, &client.ListOptions{Namespace: instance.Namespace})
520+
if err != nil {
521+
return err
522+
}
523+
for _, installPlan := range installPlanList.Items {
524+
Log.Info("Found installPlan", "name", installPlan.GetName())
525+
// this should have a list containing the CSV names of all the old/legacy service operator CSVs
526+
csvNames, found, err := uns.NestedSlice(installPlan.Object, "spec", "clusterServiceVersionNames")
527+
if err != nil {
528+
return err
529+
}
530+
if found {
531+
// just checking for the first one should be sufficient
532+
if isServiceOperatorResource(csvNames[0].(string)) {
533+
err = r.Client.Delete(ctx, &installPlan)
534+
if err != nil {
535+
if apierrors.IsNotFound(err) {
536+
Log.Info("Installplane not found on delete. Continuing...", "name", installPlan.GetName())
537+
continue
538+
}
539+
return err
540+
}
541+
Log.Info("Installplan deleted successfully", "name", installPlan.GetName())
542+
}
543+
}
544+
}
545+
546+
return nil
547+
548+
}
549+
550+
// postCleanupObsoleteResources - deletes CSVs for old service operator bundles
551+
func (r *OpenStackReconciler) postCleanupObsoleteResources(ctx context.Context, instance *operatorv1beta1.OpenStack) error {
552+
Log := r.GetLogger(ctx)
553+
554+
operatorGVR := schema.GroupVersionResource{
555+
Group: "operators.coreos.com",
556+
Version: "v1",
557+
Resource: "operators",
558+
}
559+
560+
// finally we can remove operator objects as all the refs have been cleaned up:
561+
// 1) CSVs
562+
// 2) Subscriptions
563+
// 3) CRD olm.managed references removed
564+
// 4) installPlan from old service operators removed
565+
operatorList := &uns.UnstructuredList{}
566+
operatorList.SetGroupVersionKind(operatorGVR.GroupVersion().WithKind("Operator"))
567+
err := r.Client.List(ctx, operatorList, &client.ListOptions{Namespace: instance.Namespace})
568+
if err != nil {
569+
return err
570+
}
571+
for _, operator := range operatorList.Items {
572+
Log.Info("Found Operator", "name", operator.GetName())
573+
if isServiceOperatorResource(operator.GetName()) {
574+
575+
refs, found, err := uns.NestedSlice(operator.Object, "status", "components", "refs")
576+
if err != nil {
577+
return err
578+
}
579+
if found {
580+
581+
// The horizon-operator.openstack-operators has references to old roles/bindings
582+
// the code below will delete those references before continuing
583+
for _, ref := range refs {
584+
refData := ref.(map[string]interface{})
585+
Log.Info("Deleting operator reference", "Reference", ref)
586+
obj := uns.Unstructured{}
587+
obj.SetName(refData["name"].(string))
588+
obj.SetNamespace(refData["namespace"].(string))
589+
apiParts := strings.Split(refData["apiVersion"].(string), "/")
590+
objGvk := schema.GroupVersionResource{
591+
Group: apiParts[0],
592+
Version: apiParts[1],
593+
Resource: refData["kind"].(string),
594+
}
595+
obj.SetGroupVersionKind(objGvk.GroupVersion().WithKind(refData["kind"].(string)))
596+
597+
// references from CRD's should be removed before this function is called
598+
// but this is a safeguard as we do not want to delete them
599+
if refData["kind"].(string) != "CustomResourceDefinition" {
600+
err = r.Client.Delete(ctx, &obj)
601+
if err != nil {
602+
if apierrors.IsNotFound(err) {
603+
Log.Info("Object not found on delete. Continuing...", "name", obj.GetName())
604+
continue
605+
}
606+
return err
607+
}
608+
}
609+
}
610+
611+
return fmt.Errorf("Requeuing/Found references for operator name: %s, refs: %v", operator.GetName(), refs)
612+
}
613+
// no refs found so we should be able to successfully delete the operator
614+
err = r.Client.Delete(ctx, &operator)
615+
if err != nil {
616+
return err
617+
}
618+
Log.Info("Operator deleted successfully", "name", operator.GetName())
619+
}
620+
}
621+
622+
return nil
623+
624+
}
625+
425626
// SetupWithManager sets up the controller with the Manager.
426627
func (r *OpenStackReconciler) SetupWithManager(mgr ctrl.Manager) error {
427628

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
# NOTE: this file is automatically generated by hack/sync-bindata.sh!
22

3-
export RELATED_IMAGE_BARBICAN_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/barbican-operator@sha256:e72c46447b50a78e9b0c60884c16fefd07501f4d199a7702126f811c1d13fd28
4-
export RELATED_IMAGE_CINDER_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/cinder-operator@sha256:b25b8c1d3f786804bd1f0e6e676d5630dc5403ab833a0a76de53068065dbdfbe
5-
export RELATED_IMAGE_DESIGNATE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/designate-operator@sha256:1dd77b9c7b0321f80baa4324d333df336aed10ec8fdd04f0edd735fbe1cf5cd5
6-
export RELATED_IMAGE_GLANCE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/glance-operator@sha256:27c0b5c7d671ae3b405ea800999e8d1edf01fe91688a3e542011e7f4b09dcacb
7-
export RELATED_IMAGE_HEAT_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/heat-operator@sha256:68aa95ce80e33704b6052b0ba1c071ff2fe364d220b8d0f5f667724f473bd47d
8-
export RELATED_IMAGE_HORIZON_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/horizon-operator@sha256:6e292a7c2f7b620ccdf6135cc949d82840db0d2c88440464345fae94d7104c51
9-
export RELATED_IMAGE_INFRA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/infra-operator@sha256:d4e6e3701328a25c0d6ae0a765cdffb2436387eb07bbb5a57211c67744ba41fe
3+
export RELATED_IMAGE_BARBICAN_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/barbican-operator@sha256:18980b0f85f4bfbdd41e2c5a05ac9eb3df208ac20f7d4b2f8fe0bb7c438c46d5
4+
export RELATED_IMAGE_CINDER_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/cinder-operator@sha256:92cd06506f633fbd36e3b0d0e145835304f3f8e197515c90a05974faabe4d491
5+
export RELATED_IMAGE_DESIGNATE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/designate-operator@sha256:16e1770edc2c621ac89fdca5a10d8110bd5b43249ad1aa3d0369c1892cb4053f
6+
export RELATED_IMAGE_GLANCE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/glance-operator@sha256:3376b637ae0249423be1a8b539b7e7c54a285194fb8a9095062f24a65600c3d5
7+
export RELATED_IMAGE_HEAT_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/heat-operator@sha256:aa30e36c7cf183f3aa63e90794ea6cb2d191c29cf6825bba19a19e855a8360ed
8+
export RELATED_IMAGE_HORIZON_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/horizon-operator@sha256:a7fda492ec0dfd798bf1315b6ee828c4febe898aa918b6b0107664d6dcbd6a81
9+
export RELATED_IMAGE_INFRA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/infra-operator@sha256:73741d7f4e713753311001cbea234ad77c70e834e7c2b03eec2ca37c372a9fcb
1010
export RELATED_IMAGE_IRONIC_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/ironic-operator@sha256:bad53185041f6003f3f573df9bbf248aa1c47a1060abcb5410201d74bcb08829
11-
export RELATED_IMAGE_KEYSTONE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/keystone-operator@sha256:b1a3a9b879758fa42c1fc5acb23d5f6435405a2977504d8cdfa72aaba373955f
12-
export RELATED_IMAGE_MANILA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/manila-operator@sha256:f33d7eda4988244f7b5a2d84f7054b5e6b70b02aa05aca82455b837a6295a9ab
13-
export RELATED_IMAGE_MARIADB_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/mariadb-operator@sha256:c8911c5d0eb1797e4440cf095c68e4129bbfc775d216d7a8d0f9cfe0a16f0967
14-
export RELATED_IMAGE_NEUTRON_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/neutron-operator@sha256:b120ee4d49ff8ab19b89bc68b10f8cbbd6b3bb2cee68c597b591e79be8dce92d
15-
export RELATED_IMAGE_NOVA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/nova-operator@sha256:e02e1374ce6458d1663c615d07fdaa2f1aad273ef7d94d58121b9a5c4522e8cd
16-
export RELATED_IMAGE_OCTAVIA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/octavia-operator@sha256:afb2f4458c83ba266815c744aff5bb32301a9510771b93378923af47e0769975
17-
export RELATED_IMAGE_OPENSTACK_BAREMETAL_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/openstack-baremetal-operator@sha256:919ecaf79f2094441b54972cc1752119b78bff4bd5d1c781083fe4205aeee196
18-
export RELATED_IMAGE_OVN_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/ovn-operator@sha256:f23d456960ef674272cb4306cffbf7d14b2b340156a145ae7b653255e57d372e
11+
export RELATED_IMAGE_KEYSTONE_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/keystone-operator@sha256:f5a15209a080f124c2ab9febb2ad9b65af1422d0cd066ec8c6a6abd8e1fd0e84
12+
export RELATED_IMAGE_MANILA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/manila-operator@sha256:72a1bef77126188f623eeae40b07359f111d720585e13c991c576c36ef5da3a7
13+
export RELATED_IMAGE_MARIADB_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/mariadb-operator@sha256:9340b805296c23e21833a37afe99e0e139a161d26edcf5c7a7b9a63529516bf0
14+
export RELATED_IMAGE_NEUTRON_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/neutron-operator@sha256:f9dc32cc86d8320306716e5e99977b9a9b360e2c0fe2b0ea4da440663198b358
15+
export RELATED_IMAGE_NOVA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/nova-operator@sha256:a373a561a5c99998f6eb4afb5b48350709ca2dd2b127fe3d323baa758eba751d
16+
export RELATED_IMAGE_OCTAVIA_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/octavia-operator@sha256:fef6a050193789bce817ceff852f4bf4a6f9ffbbd7b59494f1465f1c9d951c11
17+
export RELATED_IMAGE_OPENSTACK_BAREMETAL_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/openstack-baremetal-operator@sha256:f515dbdddef5cd1c3318ad370d987b2a5a24e10310cdf61f5ff55676264ffd30
18+
export RELATED_IMAGE_OVN_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/ovn-operator@sha256:b32720841ecf8460dad4a48760915b2bfa16b07156288917d86451a24a5eb7f9
1919
export RELATED_IMAGE_PLACEMENT_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/placement-operator@sha256:f25a277c9ed1bb6fbe3fd4eb8b71d8a771dc5fc9aff9e0064ced552ba7b5fc9b
2020
export RELATED_IMAGE_RABBITMQ_CLUSTER_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/rabbitmq-cluster-operator@sha256:225524223bf2a7f3a4ce95958fc9ca6fdab02745fb70374e8ff5bf1ddaceda4b
21-
export RELATED_IMAGE_SWIFT_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/swift-operator@sha256:c5550d38a452a76cf4c17967bc36b2c722411a2f499366112f9b013918827434
22-
export RELATED_IMAGE_TELEMETRY_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/telemetry-operator@sha256:fd6325fa52405a28129fdf0bde69429758f053ef58d53bc63756f91906b84cc0
21+
export RELATED_IMAGE_SWIFT_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/swift-operator@sha256:65d6dd43bf86a5de20fa34debcf105e5b7282ffe5d5108322e252132e12ea6e2
22+
export RELATED_IMAGE_TELEMETRY_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/telemetry-operator@sha256:91ea9e8572089079c528d46b993383cf5adc498ac1498849bf3f002a63228943
2323
export RELATED_IMAGE_TEST_OPERATOR_MANAGER_IMAGE_URL=quay.io/openstack-k8s-operators/test-operator@sha256:02489c6cf6a839478d19f7b0926e7ee701c95554e483a44fa759031bbee60929

pkg/operator/bindata/merge.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ func mergeLabels(current, updated *uns.Unstructured) {
205205
curLabels["openstack.openstack.org/managed"] = "true"
206206
}
207207

208+
// remove the olm.managed label this allows cleanup of the operator objects
209+
// from legacy service operator deployments prior to FR2
210+
delete(curLabels, "olm.managed")
211+
208212
updated.SetLabels(curLabels)
209213
}
210214

0 commit comments

Comments
 (0)