@@ -19,6 +19,7 @@ package controller
1919import (
2020 "context"
2121 "errors"
22+ "fmt"
2223
2324 appsv1 "k8s.io/api/apps/v1"
2425 corev1 "k8s.io/api/core/v1"
@@ -36,10 +37,10 @@ import (
3637)
3738
3839const (
39- deploymentNameIndexField = "spec.deployment"
40- statefulSetNameIndexField = "spec.statefulset "
41- secretNameIndexField = "spec.secret"
42- finalizerName = "agent.finalizers.lightrun.com"
40+ deploymentNameIndexField = "spec.deployment"
41+ workloadNameIndexField = "spec.workloadName "
42+ secretNameIndexField = "spec.secret"
43+ finalizerName = "agent.finalizers.lightrun.com"
4344)
4445
4546var err error
@@ -68,36 +69,77 @@ func (r *LightrunJavaAgentReconciler) Reconcile(ctx context.Context, req ctrl.Re
6869 }
6970
7071 // Determine which workload type to reconcile
71- if lightrunJavaAgent .Spec .DeploymentName != "" && lightrunJavaAgent .Spec .StatefulSetName != "" {
72- log .Error (nil , "Both DeploymentName and StatefulSetName are set. Only one should be specified" )
73- return r .errorStatus (ctx , lightrunJavaAgent , errors .New ("both deployment and statefulset specified" ))
74- } else if lightrunJavaAgent .Spec .DeploymentName != "" {
75- // Handle Deployment reconciliation (existing code)
72+ workloadType , err := r .determineWorkloadType (lightrunJavaAgent )
73+ if err != nil {
74+ log .Error (err , "failed to determine workload type" )
75+ return r .errorStatus (ctx , lightrunJavaAgent , err )
76+ }
77+ switch workloadType {
78+ case agentv1beta .WorkloadTypeDeployment :
7679 return r .reconcileDeployment (ctx , lightrunJavaAgent , req .Namespace )
77- } else if lightrunJavaAgent .Spec .StatefulSetName != "" {
78- // Handle StatefulSet reconciliation (to be implemented)
80+ case agentv1beta .WorkloadTypeStatefulSet :
7981 return r .reconcileStatefulSet (ctx , lightrunJavaAgent , req .Namespace )
80- } else {
81- log .Error (nil , "Neither DeploymentName nor StatefulSetName is set" )
82- return r .errorStatus (ctx , lightrunJavaAgent , errors .New ("no workload specified" ))
82+ default :
83+ return r .errorStatus (ctx , lightrunJavaAgent , fmt .Errorf ("unsupported workload type: %s" , workloadType ))
84+ }
85+ }
86+
87+ func (r * LightrunJavaAgentReconciler ) determineWorkloadType (lightrunJavaAgent * agentv1beta.LightrunJavaAgent ) (agentv1beta.WorkloadType , error ) {
88+ spec := lightrunJavaAgent .Spec
89+
90+ // === Case 1: Legacy only — DeploymentName only ===
91+ if spec .DeploymentName != "" && spec .WorkloadName == "" && spec .WorkloadType == "" {
92+ return agentv1beta .WorkloadTypeDeployment , nil
93+ }
94+
95+ // === Case 2: New fields — WorkloadName + WorkloadType ===
96+ if spec .DeploymentName == "" && spec .WorkloadName != "" {
97+ if spec .WorkloadType == "" {
98+ return "" , errors .New ("WorkloadType must be set when using WorkloadName" )
99+ }
100+ return spec .WorkloadType , nil
101+ }
102+
103+ // === Case 3: Misconfigured — Both names are set ===
104+ if spec .DeploymentName != "" && spec .WorkloadName != "" {
105+ // Same names still ambiguous — rely on workloadType
106+ if spec .DeploymentName == spec .WorkloadName {
107+ if spec .WorkloadType == "" {
108+ return "" , errors .New ("both DeploymentName and WorkloadName are set and equal; please remove DeploymentName and set workloadType explicitly" )
109+ }
110+ return spec .WorkloadType , nil
111+ }
112+ // Names differ — reject as invalid
113+ return "" , errors .New ("DeploymentName and WorkloadName are both set but differ; please use only WorkloadName with WorkloadType" )
83114 }
115+
116+ // === Case 4: Fully empty or malformed ===
117+ return "" , errors .New ("invalid configuration: must set either DeploymentName (legacy) or WorkloadName with WorkloadType" )
84118}
85119
86120// reconcileDeployment handles the reconciliation logic for Deployment workloads
87121func (r * LightrunJavaAgentReconciler ) reconcileDeployment (ctx context.Context , lightrunJavaAgent * agentv1beta.LightrunJavaAgent , namespace string ) (ctrl.Result , error ) {
88- log := r .Log .WithValues ("lightrunJavaAgent" , lightrunJavaAgent .Name , "deployment" , lightrunJavaAgent .Spec .DeploymentName )
122+ // Get the workload name - use DeploymentName for backward compatibility
123+ // or WorkloadName for newer CR versions
124+ deploymentName := lightrunJavaAgent .Spec .WorkloadName
125+ if deploymentName == "" && lightrunJavaAgent .Spec .DeploymentName != "" {
126+ // Fall back to legacy field if WorkloadName isn't set
127+ deploymentName = lightrunJavaAgent .Spec .DeploymentName
128+ }
129+
130+ log := r .Log .WithValues ("lightrunJavaAgent" , lightrunJavaAgent .Name , "deployment" , deploymentName )
89131 fieldManager := "lightrun-conrtoller"
90132
91133 deplNamespacedObj := client.ObjectKey {
92- Name : lightrunJavaAgent . Spec . DeploymentName ,
134+ Name : deploymentName ,
93135 Namespace : namespace ,
94136 }
95137 originalDeployment := & appsv1.Deployment {}
96138 err = r .Get (ctx , deplNamespacedObj , originalDeployment )
97139 if err != nil {
98140 // Deployment not found
99141 if client .IgnoreNotFound (err ) == nil {
100- log .Info ("Deployment not found. Verify name/namespace" , "Deployment" , lightrunJavaAgent . Spec . DeploymentName )
142+ log .Info ("Deployment not found. Verify name/namespace" , "Deployment" , deploymentName )
101143 // remove our finalizer from the list and update it.
102144 err = r .removeFinalizer (ctx , lightrunJavaAgent , finalizerName )
103145 if err != nil {
@@ -141,7 +183,7 @@ func (r *LightrunJavaAgentReconciler) reconcileDeployment(ctx context.Context, l
141183
142184 // Ensure that finalizer is in place
143185 if ! containsString (lightrunJavaAgent .ObjectMeta .Finalizers , finalizerName ) {
144- log .Info ("Start working on deployment" , "Deployment" , lightrunJavaAgent . Spec . DeploymentName )
186+ log .Info ("Start working on deployment" , "Deployment" , deploymentName )
145187 log .Info ("Adding finalizer" )
146188 err = r .addFinalizer (ctx , lightrunJavaAgent , finalizerName )
147189 if err != nil {
@@ -199,7 +241,7 @@ func (r *LightrunJavaAgentReconciler) reconcileDeployment(ctx context.Context, l
199241 return r .errorStatus (ctx , lightrunJavaAgent , err )
200242 }
201243
202- log .Info ("Deployment returned to original state" , "Deployment" , lightrunJavaAgent . Spec . DeploymentName )
244+ log .Info ("Deployment returned to original state" , "Deployment" , deploymentName )
203245 return r .successStatus (ctx , lightrunJavaAgent , reconcileTypeProgressing )
204246 } else {
205247 // Nothing to do here
@@ -245,7 +287,7 @@ func (r *LightrunJavaAgentReconciler) reconcileDeployment(ctx context.Context, l
245287 cmDataHash := configMapDataHash (cm .Data )
246288
247289 // Server side apply
248- log .V (2 ).Info ("Patching deployment, SSA" , "Deployment" , lightrunJavaAgent . Spec . DeploymentName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
290+ log .V (2 ).Info ("Patching deployment, SSA" , "Deployment" , deploymentName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
249291 err = r .patchDeployment (lightrunJavaAgent , secret , originalDeployment , deploymentApplyConfig , cmDataHash )
250292 if err != nil {
251293 log .Error (err , "unable to patch deployment" )
@@ -307,25 +349,25 @@ func (r *LightrunJavaAgentReconciler) reconcileDeployment(ctx context.Context, l
307349 }
308350
309351 // Update status to Healthy
310- log .V (1 ).Info ("Reconciling finished successfully" , "Deployment" , lightrunJavaAgent . Spec . DeploymentName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
352+ log .V (1 ).Info ("Reconciling finished successfully" , "Deployment" , deploymentName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
311353 return r .successStatus (ctx , lightrunJavaAgent , reconcileTypeReady )
312354}
313355
314356// reconcileStatefulSet handles the reconciliation logic for StatefulSet workloads
315357func (r * LightrunJavaAgentReconciler ) reconcileStatefulSet (ctx context.Context , lightrunJavaAgent * agentv1beta.LightrunJavaAgent , namespace string ) (ctrl.Result , error ) {
316- log := r .Log .WithValues ("lightrunJavaAgent" , lightrunJavaAgent .Name , "statefulSet" , lightrunJavaAgent .Spec .StatefulSetName )
358+ log := r .Log .WithValues ("lightrunJavaAgent" , lightrunJavaAgent .Name , "statefulSet" , lightrunJavaAgent .Spec .WorkloadName )
317359 fieldManager := "lightrun-controller"
318360
319361 stsNamespacedObj := client.ObjectKey {
320- Name : lightrunJavaAgent .Spec .StatefulSetName ,
362+ Name : lightrunJavaAgent .Spec .WorkloadName ,
321363 Namespace : namespace ,
322364 }
323365 originalStatefulSet := & appsv1.StatefulSet {}
324366 err = r .Get (ctx , stsNamespacedObj , originalStatefulSet )
325367 if err != nil {
326368 // StatefulSet not found
327369 if client .IgnoreNotFound (err ) == nil {
328- log .Info ("StatefulSet not found. Verify name/namespace" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName )
370+ log .Info ("StatefulSet not found. Verify name/namespace" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName )
329371 // remove our finalizer from the list and update it.
330372 err = r .removeFinalizer (ctx , lightrunJavaAgent , finalizerName )
331373 if err != nil {
@@ -346,13 +388,13 @@ func (r *LightrunJavaAgentReconciler) reconcileStatefulSet(ctx context.Context,
346388
347389 // Restore original StatefulSet (unpatch)
348390 // Volume and init container
349- log .Info ("Unpatching StatefulSet" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName )
391+ log .Info ("Unpatching StatefulSet" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName )
350392
351393 originalStatefulSet = & appsv1.StatefulSet {}
352394 err = r .Get (ctx , stsNamespacedObj , originalStatefulSet )
353395 if err != nil {
354396 if client .IgnoreNotFound (err ) == nil {
355- log .Info ("StatefulSet not found" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName )
397+ log .Info ("StatefulSet not found" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName )
356398 // remove our finalizer from the list and update it.
357399 log .Info ("Removing finalizer" )
358400 err = r .removeFinalizer (ctx , lightrunJavaAgent , finalizerName )
@@ -362,7 +404,7 @@ func (r *LightrunJavaAgentReconciler) reconcileStatefulSet(ctx context.Context,
362404 // Successfully removed finalizer and nothing to restore
363405 return r .successStatus (ctx , lightrunJavaAgent , reconcileTypeReady )
364406 }
365- log .Error (err , "unable to unpatch statefulset" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName )
407+ log .Error (err , "unable to unpatch statefulset" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName )
366408 return r .errorStatus (ctx , lightrunJavaAgent , err )
367409 }
368410
@@ -410,7 +452,7 @@ func (r *LightrunJavaAgentReconciler) reconcileStatefulSet(ctx context.Context,
410452 return r .errorStatus (ctx , lightrunJavaAgent , err )
411453 }
412454
413- log .Info ("StatefulSet returned to original state" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName )
455+ log .Info ("StatefulSet returned to original state" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName )
414456 return r .successStatus (ctx , lightrunJavaAgent , reconcileTypeProgressing )
415457 }
416458 // Nothing to do here
@@ -478,7 +520,7 @@ func (r *LightrunJavaAgentReconciler) reconcileStatefulSet(ctx context.Context,
478520 }
479521
480522 // Server side apply for StatefulSet changes
481- log .V (2 ).Info ("Patching StatefulSet" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
523+ log .V (2 ).Info ("Patching StatefulSet" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
482524 err = r .patchStatefulSet (lightrunJavaAgent , secret , originalStatefulSet , statefulSetApplyConfig , cmDataHash )
483525 if err != nil {
484526 log .Error (err , "failed to patch statefulset" )
@@ -503,12 +545,12 @@ func (r *LightrunJavaAgentReconciler) reconcileStatefulSet(ctx context.Context,
503545 }
504546
505547 // Client side patch (we can't rollback JAVA_TOOL_OPTIONS env with server side apply)
506- log .V (2 ).Info ("Patching Java Env" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
548+ log .V (2 ).Info ("Patching Java Env" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
507549 originalStatefulSet = & appsv1.StatefulSet {}
508550 err = r .Get (ctx , stsNamespacedObj , originalStatefulSet )
509551 if err != nil {
510552 if client .IgnoreNotFound (err ) == nil {
511- log .Info ("StatefulSet not found" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName )
553+ log .Info ("StatefulSet not found" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName )
512554 err = r .removeFinalizer (ctx , lightrunJavaAgent , finalizerName )
513555 if err != nil {
514556 return r .errorStatus (ctx , lightrunJavaAgent , err )
@@ -538,51 +580,57 @@ func (r *LightrunJavaAgentReconciler) reconcileStatefulSet(ctx context.Context,
538580 }
539581
540582 // Update status to Healthy
541- log .V (1 ).Info ("Reconciling finished successfully" , "StatefulSet" , lightrunJavaAgent .Spec .StatefulSetName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
583+ log .V (1 ).Info ("Reconciling finished successfully" , "StatefulSet" , lightrunJavaAgent .Spec .WorkloadName , "LightunrJavaAgent" , lightrunJavaAgent .Name )
542584 return r .successStatus (ctx , lightrunJavaAgent , reconcileTypeReady )
543585}
544586
545- // SetupWithManager sets up the controller with the Manager.
587+ // SetupWithManager configures the controller with the Manager and sets up watches and indexers.
588+ // It creates several field indexers to enable efficient lookups of LightrunJavaAgent CRs based on:
589+ // - DeploymentName (legacy field)
590+ // - WorkloadName (newer field that replaces DeploymentName)
591+ // - SecretName
592+ //
593+ // It also sets up watches for Deployments, StatefulSets, and Secrets so the controller can
594+ // react to changes in these resources that are referenced by LightrunJavaAgent CRs.
546595func (r * LightrunJavaAgentReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
547- // Add spec.container_selector.deployment field to cache for future filtering
596+ // Index field for deployments - allows looking up LightrunJavaAgents by deploymentName
597+ // This is used for legacy support where DeploymentName was used instead of WorkloadName
598+ // TODO: remove this once we deprecate deploymentNameIndexField
548599 err = mgr .GetFieldIndexer ().IndexField (
549600 context .Background (),
550601 & agentv1beta.LightrunJavaAgent {},
551602 deploymentNameIndexField ,
552603 func (object client.Object ) []string {
553- lightrunJavaAgent := object .(* agentv1beta.LightrunJavaAgent )
554-
555- if lightrunJavaAgent .Spec .DeploymentName == "" {
604+ agent := object .(* agentv1beta.LightrunJavaAgent )
605+ if agent .Spec .DeploymentName == "" {
556606 return nil
557607 }
558-
559- return []string {lightrunJavaAgent .Spec .DeploymentName }
608+ r . Log . Info ( "Indexing DeploymentName" , "DeploymentName" , agent . Spec . DeploymentName )
609+ return []string {agent .Spec .DeploymentName }
560610 })
561-
562611 if err != nil {
563612 return err
564613 }
565614
566- // Add spec.container_selector.statefulset field to cache for future filtering
615+ // Index field for workloads by name - allows looking up LightrunJavaAgents by WorkloadName
567616 err = mgr .GetFieldIndexer ().IndexField (
568617 context .Background (),
569618 & agentv1beta.LightrunJavaAgent {},
570- statefulSetNameIndexField ,
619+ workloadNameIndexField ,
571620 func (object client.Object ) []string {
572- lightrunJavaAgent := object .(* agentv1beta.LightrunJavaAgent )
573-
574- if lightrunJavaAgent .Spec .StatefulSetName == "" {
621+ agent := object .(* agentv1beta.LightrunJavaAgent )
622+ if agent .Spec .WorkloadName == "" {
575623 return nil
576624 }
577-
578- return []string {lightrunJavaAgent .Spec .StatefulSetName }
625+ r . Log . Info ( "Indexing WorkloadName" , "WorkloadName" , agent . Spec . WorkloadName )
626+ return []string {agent .Spec .WorkloadName }
579627 })
580-
581628 if err != nil {
582629 return err
583630 }
584631
585- // Add spec.secret field to cache for future filtering
632+ // Index field for secrets - allows looking up LightrunJavaAgents by SecretName
633+ // This enables the controller to find LightrunJavaAgents affected by Secret changes
586634 err = mgr .GetFieldIndexer ().IndexField (
587635 context .Background (),
588636 & agentv1beta.LightrunJavaAgent {},
@@ -601,6 +649,12 @@ func (r *LightrunJavaAgentReconciler) SetupWithManager(mgr ctrl.Manager) error {
601649 return err
602650 }
603651
652+ // Configure the controller builder:
653+ // - For: register LightrunJavaAgent as the primary resource this controller reconciles
654+ // - Watches: set up event handlers to watch for changes in related resources:
655+ // * Deployments: reconcile LightrunJavaAgents when their target Deployment changes
656+ // * StatefulSets: reconcile LightrunJavaAgents when their target StatefulSet changes
657+ // * Secrets: reconcile LightrunJavaAgents when their referenced Secret changes
604658 return ctrl .NewControllerManagedBy (mgr ).
605659 For (& agentv1beta.LightrunJavaAgent {}).
606660 Watches (
0 commit comments