From 8fb503f90b05e6faff42b72dca1e0943450551da Mon Sep 17 00:00:00 2001 From: Lukas Piwowarski Date: Fri, 21 Nov 2025 07:34:28 -0500 Subject: [PATCH] Manually remove OLS installPlan on cleanup When the OpenStackLightspeed operator is installed via OLM, the OLM automatically adds ownership to the OLS installPlan created by our OLS Subcription, resultingin two owners: - The OLS Subscription (created by us) - The OpenStackLightspeed Subscription (created during the installation of the OpenStackLightspeed operator) When uninstalling the OLS operator (because the user removed the OpenStackLightspeed instance) while OpenStackLightspeed continues running, the installPlan remains orphaned because the OpenStackLightspeed Subscription still exists. This change manually removes the installPlan during cleanup to prevent orphaned resources. --- internal/controller/ols_install.go | 117 +++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 31 deletions(-) diff --git a/internal/controller/ols_install.go b/internal/controller/ols_install.go index 1afb5bf2..9749c817 100644 --- a/internal/controller/ols_install.go +++ b/internal/controller/ols_install.go @@ -282,6 +282,17 @@ func UninstallInstanceOwnedOLSOperator( return true, nil } + // When the operator is installed via OLM, the OpenStack Lightspeed Subscription + // is also set as an owner of its InstallPlan, resulting in the InstallPlan having + // both the OLS Subscription and the OpenStackLightspeed resources as owners. + // When uninstalling the OLS operator, only the OLS Subscription owner reference is removed, + // which causes the InstallPlans to remain and accumulate over time. To avoid this, + // we explicitly attempt to delete the relevant InstallPlan to prevent leftovers. + _, err = DeleteOLSOperatorInstallPlan(ctx, helper, instance) + if err != nil { + return false, err + } + if err := helper.GetClient().Delete(ctx, OLSOperatorCSV); err != nil { return false, err } @@ -289,63 +300,107 @@ func UninstallInstanceOwnedOLSOperator( OLSOperatorCSV, err = GetOLSOperatorCSV(ctx, helper) if err != nil { return false, err - } else if OLSOperatorCSV == nil { - return true, nil + } else if OLSOperatorCSV != nil { + return false, nil } - return false, nil + OLSInstallPlan, err := GetOLSOperatorInstallPlan(ctx, helper, instance) + if err != nil { + return false, err + } else if OLSInstallPlan != nil { + return false, nil + } + + return true, nil } -// ApproveOLSOperatorInstallPlan - checks for any pending, unapproved InstallPlans associated with the -// OpenShift Lightspeed Operator (OLS operator) within the namespace of the provided OpenStackLightspeed instance, -// and approves them. The function lists all InstallPlans in the instance's namespace, identifies those linked to -// the OLS operator and not yet approved, and updates their status to approved. Returns true if a pending -// InstallPlan is successfully approved. Returns false if there are no InstallPlans to be approved, -// and returns false with an error if any occurs during the listing or approval process. -func ApproveOLSOperatorInstallPlan( +// GetOLSOperatorInstallPlan returns the InstallPlan that was used to install +// the OpenShift Lightspeed Operator (OLS Operator). It searches for an InstallPlan +// whose ClusterServiceVersion name matches the OLS Operator prefix and the +// recommended OLS version. If such an InstallPlan exists, it is returned; otherwise, +// the function returns nil. +func GetOLSOperatorInstallPlan( ctx context.Context, helper *common_helper.Helper, instance *apiv1beta1.OpenStackLightspeed, -) (bool, error) { +) (*operatorsv1alpha1.InstallPlan, error) { var installPlans operatorsv1alpha1.InstallPlanList err := helper.GetClient().List(ctx, &installPlans, client.InNamespace(instance.Namespace)) if err != nil { - return false, err + return nil, err } recommendedOLSVersion, err := GetRecommendedOLSVersion() if err != nil { - return false, err + return nil, err } for _, installPlan := range installPlans.Items { - // Continue if the InstallPlan does not have any CSVs associated with it. - if len(installPlan.Spec.ClusterServiceVersionNames) == 0 { - continue + var isOLSOperatorCSV bool + for _, csvName := range installPlan.Spec.ClusterServiceVersionNames { + if strings.HasPrefix(csvName, OLSOperatorName) && strings.HasSuffix(csvName, recommendedOLSVersion) { + isOLSOperatorCSV = true + break + } } - isOLSOperatorCSV := strings.HasPrefix(installPlan.Spec.ClusterServiceVersionNames[0], OLSOperatorName) - if !isOLSOperatorCSV { - continue + if isOLSOperatorCSV { + return &installPlan, nil } + } - isCorrectVersion := strings.HasSuffix(installPlan.Spec.ClusterServiceVersionNames[0], recommendedOLSVersion) - if !isCorrectVersion { - continue - } + return nil, nil +} - installPlan.Spec.Approved = true - err = helper.GetClient().Update(ctx, &installPlan) - if err != nil && k8s_errors.IsConflict(err) { - return false, nil - } else if err != nil { - return false, err - } +// ApproveOLSOperatorInstallPlan approves the InstallPlan that is responsible for installing +// the OpenShift Lightspeed Operator (OLS Operator) in the given OpenStackLightspeed instance's +// namespace. It sets the Approved field to true and updates the InstallPlan resource in the cluster. +// Returns true if the approval succeeds, false and an error otherwise. +func ApproveOLSOperatorInstallPlan( + ctx context.Context, + helper *common_helper.Helper, + instance *apiv1beta1.OpenStackLightspeed, +) (bool, error) { + installPlan, err := GetOLSOperatorInstallPlan(ctx, helper, instance) + if err != nil { + return false, err + } else if installPlan == nil { + return false, nil + } + + installPlan.Spec.Approved = true + err = helper.GetClient().Update(ctx, installPlan) + if err != nil { + return false, err + } + + return true, nil +} +// DeleteOLSOperatorInstallPlan deletes the InstallPlan associated with installing the +// OpenShift Lightspeed Operator (OLS Operator) in the specified OpenStackLightspeed instance's +// namespace. If the InstallPlan does not exist, the function returns true. It returns true +// if the deletion succeeds or the InstallPlan was not found, and false with an error otherwise. +func DeleteOLSOperatorInstallPlan( + ctx context.Context, + helper *common_helper.Helper, + instance *apiv1beta1.OpenStackLightspeed, +) (bool, error) { + installPlan, err := GetOLSOperatorInstallPlan(ctx, helper, instance) + if err != nil { + return false, err + } else if installPlan == nil { return true, nil } - return false, nil + err = helper.GetClient().Delete(ctx, installPlan) + if err != nil && k8s_errors.IsNotFound(err) { + return true, nil + } else if err != nil { + return false, err + } + + return true, nil } // GetOLSSubscriptionName generates a unique subscription name for the OpenStack Lightspeed Operator