Skip to content

Commit c4d020c

Browse files
committed
first unprovision the old, then provision the new cluster
On-behalf-of: @SAP [email protected]
1 parent d869068 commit c4d020c

File tree

1 file changed

+80
-50
lines changed

1 file changed

+80
-50
lines changed

internal/controller/kubeconfig-rbac/controller.go

Lines changed: 80 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
217247
func (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

Comments
 (0)