@@ -31,6 +31,7 @@ import (
3131 corev1 "k8s.io/api/core/v1"
3232 apierrors "k8s.io/apimachinery/pkg/api/errors"
3333 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+ "k8s.io/apimachinery/pkg/runtime"
3435 "k8s.io/apimachinery/pkg/runtime/schema"
3536 "k8s.io/apimachinery/pkg/types"
3637 ctrl "sigs.k8s.io/controller-runtime"
@@ -215,13 +216,15 @@ func SetupWorkspaceTemplateApply(mgr ctrl.Manager, l logging.Logger) error {
215216 client : mgr .GetClient (),
216217 log : l ,
217218 record : event .NewAPIRecorder (mgr .GetEventRecorderFor (controllerName )),
219+ scheme : mgr .GetScheme (),
218220 })
219221}
220222
221223type workspaceTemplateApplyReconciler struct {
222224 client client.Client
223225 log logging.Logger
224226 record event.Recorder
227+ scheme * runtime.Scheme
225228}
226229
227230func (r * workspaceTemplateApplyReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
@@ -282,6 +285,29 @@ func (r *workspaceTemplateApplyReconciler) Reconcile(ctx context.Context, req ct
282285 return ctrl.Result {}, err
283286 }
284287
288+ // If a Workspace with the expected name already exists (e.g., from previous versions),
289+ // adopt it by setting ownerReference and updating status instead of failing creation.
290+ existing := & tfv1beta1.Workspace {}
291+ if err := r .client .Get (ctx , types.NamespacedName {
292+ Name : workspaceName ,
293+ Namespace : cr .Namespace ,
294+ }, existing ); err == nil {
295+ // Ensure owner reference to this WorkspaceTemplateApply so GC can cascade on delete.
296+ if setErr := controllerutil .SetControllerReference (cr , existing , r .scheme ); setErr == nil {
297+ _ = r .client .Update (ctx , existing )
298+ }
299+ // Update status to reflect adoption
300+ cr .Status .WorkspaceName = existing .GetName ()
301+ cr .Status .Applied = true
302+ now := metav1 .Now ()
303+ cr .Status .LastAppliedTime = & now
304+ if uerr := r .client .Status ().Update (ctx , cr ); uerr != nil {
305+ return ctrl.Result {}, uerr
306+ }
307+ r .record .Event (cr , event .Normal (reasonCreatedWorkspace , "Adopted existing Workspace" ))
308+ return ctrl.Result {RequeueAfter : requeueAfterStatus }, nil
309+ }
310+
285311 // Create Workspace from template
286312 workspace := & tfv1beta1.Workspace {
287313 ObjectMeta : metav1.ObjectMeta {
@@ -296,6 +322,12 @@ func (r *workspaceTemplateApplyReconciler) Reconcile(ctx context.Context, req ct
296322 workspace .Spec .WriteConnectionSecretToReference = cr .Spec .WriteConnectionSecretToRef
297323 }
298324
325+ // Set owner reference so Workspace is garbage-collected when WTA is deleted.
326+ if err := controllerutil .SetControllerReference (cr , workspace , r .scheme ); err != nil {
327+ log .Debug ("Failed to set owner reference on Workspace" , "error" , err )
328+ return ctrl.Result {}, err
329+ }
330+
299331 if err := r .client .Create (ctx , workspace ); err != nil {
300332 log .Debug (errCreateWorkspace , "error" , err )
301333 return ctrl.Result {}, err
@@ -393,6 +425,13 @@ func (r *workspaceTemplateApplyReconciler) reconcileWorkspaceStatus(ctx context.
393425 return ctrl.Result {}, err
394426 }
395427
428+ // Ensure owner reference to WTA is present on Workspace (adopt if missing).
429+ if ! hasOwnerReference (workspace .GetOwnerReferences (), cr .APIVersion , cr .Kind , cr .Name , string (cr .UID )) {
430+ if err := controllerutil .SetControllerReference (cr , workspace , r .scheme ); err == nil {
431+ _ = r .client .Update (ctx , workspace )
432+ }
433+ }
434+
396435 // Copy conditions from workspace to WorkspaceTemplateApply
397436 cr .Status .Conditions = workspace .Status .Conditions
398437
@@ -419,3 +458,13 @@ func (r *workspaceTemplateApplyReconciler) reconcileWorkspaceStatus(ctx context.
419458 r .record .Event (cr , event .Normal (reasonWorkspaceReady , "Workspace is synced and ready" ))
420459 return ctrl.Result {}, nil
421460}
461+
462+ // hasOwnerReference returns true if an owner reference matching the given attributes exists.
463+ func hasOwnerReference (refs []metav1.OwnerReference , apiVersion , kind , name , uid string ) bool {
464+ for _ , ref := range refs {
465+ if ref .APIVersion == apiVersion && ref .Kind == kind && ref .Name == name && string (ref .UID ) == uid {
466+ return true
467+ }
468+ }
469+ return false
470+ }
0 commit comments