@@ -250,6 +250,39 @@ func (r *OpenStackReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
250250 return ctrl.Result {}, err
251251 }
252252
253+ // Check if OPENSTACK_RELEASE_VERSION has changed - if so, delete all owned resources
254+ // This is a one-time fix to handle incompatible upgrades
255+ if instance .Status .ReleaseVersion != nil && * instance .Status .ReleaseVersion != openstackReleaseVersion {
256+ Log .Info ("OpenStack release version changed, deleting all owned resources" ,
257+ "old" , * instance .Status .ReleaseVersion ,
258+ "new" , openstackReleaseVersion )
259+
260+ if err := r .deleteAllOwnedResources (ctx , instance ); err != nil {
261+ instance .Status .Conditions .Set (condition .FalseCondition (
262+ operatorv1beta1 .OpenStackOperatorReadyCondition ,
263+ condition .ErrorReason ,
264+ condition .SeverityWarning ,
265+ operatorv1beta1 .OpenStackOperatorErrorMessage ,
266+ err ))
267+ return ctrl.Result {}, err
268+ }
269+
270+ // Reset the container image status to force re-application of CRDs and RBAC
271+ instance .Status .ContainerImage = nil
272+
273+ // Update the release version in status
274+ instance .Status .ReleaseVersion = & openstackReleaseVersion
275+
276+ // Requeue to allow resources to be deleted before recreating
277+ Log .Info ("Resources deleted, requeuing to recreate with new version" )
278+ return ctrl.Result {RequeueAfter : time .Duration (5 ) * time .Second }, nil
279+ }
280+
281+ // Set the release version if not set
282+ if instance .Status .ReleaseVersion == nil {
283+ instance .Status .ReleaseVersion = & openstackReleaseVersion
284+ }
285+
253286 if err := r .applyManifests (ctx , instance ); err != nil {
254287 instance .Status .Conditions .Set (condition .FalseCondition (
255288 operatorv1beta1 .OpenStackOperatorReadyCondition ,
@@ -316,6 +349,107 @@ func (r *OpenStackReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
316349
317350}
318351
352+ func (r * OpenStackReconciler ) deleteAllOwnedResources (ctx context.Context , instance * operatorv1beta1.OpenStack ) error {
353+ Log := r .GetLogger (ctx )
354+ Log .Info ("Deleting all owned resources for release version upgrade" )
355+
356+ // Delete all owned deployments
357+ deployments := & appsv1.DeploymentList {}
358+ err := r .List (ctx , deployments , & client.ListOptions {Namespace : instance .Namespace })
359+ if err != nil {
360+ return errors .Wrap (err , "failed to list deployments" )
361+ }
362+ for _ , deployment := range deployments .Items {
363+ if metav1 .IsControlledBy (& deployment , instance ) {
364+ Log .Info ("Deleting deployment" , "name" , deployment .Name )
365+ err := r .Delete (ctx , & deployment )
366+ if err != nil && ! apierrors .IsNotFound (err ) {
367+ return errors .Wrapf (err , "failed to delete deployment %s" , deployment .Name )
368+ }
369+ }
370+ }
371+
372+ // Delete all owned service accounts
373+ serviceAccounts := & corev1.ServiceAccountList {}
374+ err = r .List (ctx , serviceAccounts , & client.ListOptions {Namespace : instance .Namespace })
375+ if err != nil {
376+ return errors .Wrap (err , "failed to list service accounts" )
377+ }
378+ for _ , sa := range serviceAccounts .Items {
379+ if metav1 .IsControlledBy (& sa , instance ) {
380+ Log .Info ("Deleting service account" , "name" , sa .Name )
381+ err := r .Delete (ctx , & sa )
382+ if err != nil && ! apierrors .IsNotFound (err ) {
383+ return errors .Wrapf (err , "failed to delete service account %s" , sa .Name )
384+ }
385+ }
386+ }
387+
388+ // Delete all owned services
389+ services := & corev1.ServiceList {}
390+ err = r .List (ctx , services , & client.ListOptions {Namespace : instance .Namespace })
391+ if err != nil {
392+ return errors .Wrap (err , "failed to list services" )
393+ }
394+ for _ , svc := range services .Items {
395+ if metav1 .IsControlledBy (& svc , instance ) {
396+ Log .Info ("Deleting service" , "name" , svc .Name )
397+ err := r .Delete (ctx , & svc )
398+ if err != nil && ! apierrors .IsNotFound (err ) {
399+ return errors .Wrapf (err , "failed to delete service %s" , svc .Name )
400+ }
401+ }
402+ }
403+
404+ // Delete all owned configmaps
405+ configMaps := & corev1.ConfigMapList {}
406+ err = r .List (ctx , configMaps , & client.ListOptions {Namespace : instance .Namespace })
407+ if err != nil {
408+ return errors .Wrap (err , "failed to list configmaps" )
409+ }
410+ for _ , cm := range configMaps .Items {
411+ if metav1 .IsControlledBy (& cm , instance ) {
412+ Log .Info ("Deleting configmap" , "name" , cm .Name )
413+ err := r .Delete (ctx , & cm )
414+ if err != nil && ! apierrors .IsNotFound (err ) {
415+ return errors .Wrapf (err , "failed to delete configmap %s" , cm .Name )
416+ }
417+ }
418+ }
419+
420+ // Delete webhooks (these are cluster-scoped and not owned, but managed by label)
421+ valWebhooks , err := r .Kclient .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().List (ctx , metav1.ListOptions {
422+ LabelSelector : "openstack.openstack.org/managed=true" ,
423+ })
424+ if err != nil {
425+ return errors .Wrap (err , "failed listing validating webhook configurations" )
426+ }
427+ for _ , webhook := range valWebhooks .Items {
428+ Log .Info ("Deleting validating webhook" , "name" , webhook .Name )
429+ err := r .Kclient .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (ctx , webhook .Name , metav1.DeleteOptions {})
430+ if err != nil && ! apierrors .IsNotFound (err ) {
431+ return errors .Wrapf (err , "failed to delete validating webhook %s" , webhook .Name )
432+ }
433+ }
434+
435+ mutWebhooks , err := r .Kclient .AdmissionregistrationV1 ().MutatingWebhookConfigurations ().List (ctx , metav1.ListOptions {
436+ LabelSelector : "openstack.openstack.org/managed=true" ,
437+ })
438+ if err != nil {
439+ return errors .Wrap (err , "failed listing mutating webhook configurations" )
440+ }
441+ for _ , webhook := range mutWebhooks .Items {
442+ Log .Info ("Deleting mutating webhook" , "name" , webhook .Name )
443+ err := r .Kclient .AdmissionregistrationV1 ().MutatingWebhookConfigurations ().Delete (ctx , webhook .Name , metav1.DeleteOptions {})
444+ if err != nil && ! apierrors .IsNotFound (err ) {
445+ return errors .Wrapf (err , "failed to delete mutating webhook %s" , webhook .Name )
446+ }
447+ }
448+
449+ Log .Info ("All owned resources deleted successfully" )
450+ return nil
451+ }
452+
319453func (r * OpenStackReconciler ) reconcileDelete (ctx context.Context , instance * operatorv1beta1.OpenStack , helper * helper.Helper ) (ctrl.Result , error ) {
320454 Log := r .GetLogger (ctx )
321455 Log .Info ("Reconciling OpenStack initialization resource delete" )
0 commit comments