@@ -77,40 +77,52 @@ func (r *KubeconfigRBACReconciler) reconcile(ctx context.Context, config *operat
7777 return r .handleDeletion (ctx , config )
7878 }
7979
80- // NB: Reconciling a Kubeconfig assumes that the authz settings are immutable, i.e. it is not
81- // possible to first configure RBAC for workspace A and then update the Kubeconfig to mean workspace B.
80+ oldCluster := config .Status .Authorization .ProvisionedCluster
81+ newCluster := ""
82+ if auth := config .Spec .Authorization ; auth != nil {
83+ newCluster = auth .ClusterRoleBindings .Cluster
84+ }
85+
86+ // All `return nil` here are because the Kubeconfig has been modified and will be requeued anyway.
87+
88+ // If there was something provisioned, but the spec changed, we have to unprovision first.
89+ if oldCluster != "" && newCluster != oldCluster {
90+ if err := r .unprovisionCluster (ctx , config ); err != nil {
91+ return err
92+ }
8293
83- // No auth configured right now and since there is no finalizer, we also have nothing to
84- // potentially clean up, hence we're done here.
85- if config .Spec .Authorization == nil && ! slices .Contains (config .Finalizers , cleanupFinalizer ) {
8694 return nil
8795 }
8896
89- // If there is any kind of authorization configured, first we ensure our own finalizer.
90- if config .Spec .Authorization != nil {
91- updated , err := r .ensureFinalizer (ctx , config )
92- if err != nil {
93- return fmt .Errorf ("failed to ensure cleanup finalizer: %w" , err )
97+ // If nothing is configured (anymore), allwe have to do is get rid of the finalizer
98+ if newCluster == "" {
99+ if err := r .removeFinalizer (ctx , config ); err != nil {
100+ return fmt .Errorf ("failed to remove cleanup finalizer: %w" , err )
94101 }
95102
96- if updated {
97- return nil // will requeue because we changed the object
98- }
103+ return nil
104+ }
105+
106+ // Otherwise we ensure the finalizer exists, because we will soon ensure the bindings.
107+ if updated , err := r .ensureFinalizer (ctx , config ); err != nil {
108+ return fmt .Errorf ("failed to ensure cleanup finalizer: %w" , err )
109+ } else if updated {
110+ return nil
111+ }
112+
113+ // Before we actually create anything, remember the cluster so if something happens,
114+ // we can properly cleanup any leftovers.
115+ if updated , err := r .patchProvisionedCluster (ctx , config , newCluster ); err != nil {
116+ return fmt .Errorf ("failed to update status: %w" , err )
117+ } else if updated {
118+ return nil
99119 }
100120
101121 // Make sure whatever is in the workspace matches what is configured in the Kubeconfig
102122 if err := r .reconcileBindings (ctx , config ); err != nil {
103123 return fmt .Errorf ("failed to ensure ClusterRoleBindings: %w" , err )
104124 }
105125
106- // If nothing is configured, now it the perfect time to remove our finalizer again
107- // so that for future reconciliations, we quickly know that we can ignore this Kubeconfig.
108- if config .Spec .Authorization == nil {
109- if err := r .removeFinalizer (ctx , config ); err != nil {
110- return fmt .Errorf ("failed to remove cleanup finalizer: %w" , err )
111- }
112- }
113-
114126 return nil
115127}
116128
@@ -172,48 +184,66 @@ func (r *KubeconfigRBACReconciler) handleDeletion(ctx context.Context, kc *opera
172184 return nil
173185 }
174186
175- // This should always be true, unless cleanup succeeded but removing the finalizer failed in a
176- // previous reconcile cycle.
177- if cluster := kc .Status .Authorization .ProvisionedCluster ; cluster != "" {
178- targetClient , err := client .NewInternalKubeconfigClient (ctx , r .Client , kc , logicalcluster .Name (cluster ), nil )
179- if err != nil {
180- return fmt .Errorf ("failed to create client to kubeconfig target: %w" , err )
181- }
187+ if err := r .unprovisionCluster (ctx , kc ); err != nil {
188+ return err
189+ }
182190
183- // find all existing bindings
184- ownerLabels := kubeconfig .OwnerLabels (kc )
185- crbList := & rbacv1.ClusterRoleBindingList {}
186- if err := targetClient .List (ctx , crbList , ctrlruntimeclient .MatchingLabels (ownerLabels )); err != nil {
187- return fmt .Errorf ("failed to list existing ClusterRoleBindings: %w" , err )
188- }
191+ // when all are gone, remove the finalizer
192+ if err := r .removeFinalizer (ctx , kc ); err != nil {
193+ return fmt .Errorf ("failed to remove cleanup finalizer: %w" , err )
194+ }
195+
196+ return nil
197+ }
189198
190- // delete all of them
191- logger := log .FromContext (ctx )
199+ func (r * KubeconfigRBACReconciler ) unprovisionCluster (ctx context.Context , kc * operatorv1alpha1.Kubeconfig ) error {
200+ cluster := kc .Status .Authorization .ProvisionedCluster
201+ if cluster == "" {
202+ return nil
203+ }
192204
193- for _ , crb := range crbList .Items {
194- logger .V (2 ).WithValues ("name" , crb .Name ).Info ("Deleting ClusterRoleBinding" )
205+ targetClient , err := client .NewInternalKubeconfigClient (ctx , r .Client , kc , logicalcluster .Name (cluster ), nil )
206+ if err != nil {
207+ return fmt .Errorf ("failed to create client to kubeconfig target: %w" , err )
208+ }
195209
196- if err := targetClient .Delete (ctx , & crb ); err != nil {
197- return fmt .Errorf ("failed to delete ClusterRoleBinding %s: %w" , crb .Name , err )
198- }
199- }
210+ // find all existing bindings
211+ ownerLabels := kubeconfig .OwnerLabels (kc )
212+ crbList := & rbacv1.ClusterRoleBindingList {}
213+ if err := targetClient .List (ctx , crbList , ctrlruntimeclient .MatchingLabels (ownerLabels )); err != nil {
214+ return fmt .Errorf ("failed to list existing ClusterRoleBindings: %w" , err )
215+ }
216+
217+ // delete all of them
218+ logger := log .FromContext (ctx )
200219
201- // clean status
202- oldKubeconfig := kc . DeepCopy ( )
203- kc . Status . Authorization . ProvisionedCluster = ""
204- if err := r . Status (). Patch ( ctx , kc , ctrlruntimeclient . MergeFrom ( oldKubeconfig ) ); err != nil {
205- return fmt .Errorf ("failed to finish cleanup by updating status : %w" , err )
220+ for _ , crb := range crbList . Items {
221+ logger . V ( 2 ). WithValues ( "name" , crb . Name ). Info ( "Deleting ClusterRoleBinding" )
222+
223+ if err := targetClient . Delete ( ctx , & crb ); err != nil {
224+ return fmt .Errorf ("failed to delete ClusterRoleBinding %s : %w" , crb . Name , err )
206225 }
207226 }
208227
209- // when all are gone, remove the finalizer
210- if err := r .removeFinalizer (ctx , kc ); err != nil {
211- return fmt .Errorf ("failed to remove cleanup finalizer : %w" , err )
228+ // clean status
229+ if _ , err := r .patchProvisionedCluster (ctx , kc , "" ); err != nil {
230+ return fmt .Errorf ("failed to finish unprovisioning : %w" , err )
212231 }
213232
214233 return nil
215234}
216235
236+ func (r * KubeconfigRBACReconciler ) patchProvisionedCluster (ctx context.Context , kc * operatorv1alpha1.Kubeconfig , newValue string ) (updated bool , err error ) {
237+ if newValue == kc .Status .Authorization .ProvisionedCluster {
238+ return false , nil
239+ }
240+
241+ oldKubeconfig := kc .DeepCopy ()
242+ kc .Status .Authorization .ProvisionedCluster = newValue
243+
244+ return true , r .Status ().Patch (ctx , kc , ctrlruntimeclient .MergeFrom (oldKubeconfig ))
245+ }
246+
217247func (r * KubeconfigRBACReconciler ) ensureFinalizer (ctx context.Context , config * operatorv1alpha1.Kubeconfig ) (updated bool , err error ) {
218248 finalizers := sets .New (config .GetFinalizers ()... )
219249 if finalizers .Has (cleanupFinalizer ) {
0 commit comments