@@ -22,6 +22,7 @@ import (
2222 "strings"
2323 "time"
2424
25+ "github.com/go-logr/logr"
2526 netboxv1 "github.com/netbox-community/netbox-operator/api/v1"
2627 "github.com/netbox-community/netbox-operator/pkg/netbox/api"
2728 "github.com/netbox-community/netbox-operator/pkg/netbox/models"
@@ -74,11 +75,18 @@ func (r *IpAddressClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reque
7475 return ctrl.Result {}, nil
7576 }
7677
78+ // Defer status update to ensure it happens regardless of how we exit
79+ // This follows Kubernetes controller best practices
80+ defer func () {
81+ if err := r .updateStatus (ctx , o , req .NamespacedName , debugLogger ); err != nil {
82+ logger .Error (err , "failed to update IpAddressClaim status in deferred call" )
83+ }
84+ }()
85+
7786 // Set ready to false initially
7887 if apismeta .FindStatusCondition (o .Status .Conditions , netboxv1 .ConditionReadyFalseNewResource .Type ) == nil {
79- err := r .EventStatusRecorder .Report (ctx , o , netboxv1 .ConditionReadyFalseNewResource , corev1 .EventTypeNormal , nil )
80- if err != nil {
81- return ctrl.Result {}, fmt .Errorf ("failed to initialise Ready condition: %w, " , err )
88+ if err := r .EventStatusRecorder .Report (ctx , o , netboxv1 .ConditionReadyFalseNewResource , corev1 .EventTypeNormal , nil ); err != nil {
89+ return ctrl.Result {}, fmt .Errorf ("failed to initialise Ready condition: %w" , err )
8290 }
8391 }
8492
@@ -94,7 +102,7 @@ func (r *IpAddressClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reque
94102 if err != nil {
95103 // return error if not a notfound error
96104 if ! apierrors .IsNotFound (err ) {
97- return ctrl.Result {}, err
105+ return ctrl.Result {}, fmt . Errorf ( "failed to get IpAddress: %w" , err )
98106 }
99107
100108 debugLogger .Info ("ipaddress object matching ipaddress claim was not found, creating new ipaddress object" )
@@ -106,7 +114,7 @@ func (r *IpAddressClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reque
106114 }
107115 ll , err := leaselocker .NewLeaseLocker (r .RestConfig , leaseLockerNSN , req .Namespace + "/" + ipAddressName )
108116 if err != nil {
109- return ctrl.Result {}, err
117+ return ctrl.Result {}, fmt . Errorf ( "failed to create lease locker: %w" , err )
110118 }
111119
112120 lockCtx , cancel := context .WithCancel (ctx )
@@ -159,71 +167,45 @@ func (r *IpAddressClaimReconciler) Reconcile(ctx context.Context, req ctrl.Reque
159167
160168 // 6.a create the IPAddress object
161169 ipAddressResource := generateIpAddressFromIpAddressClaim (o , ipAddressModel .IpAddress , logger )
162- err = controllerutil .SetControllerReference (o , ipAddressResource , r .Scheme )
163- if err != nil {
164- return ctrl.Result {}, err
170+ if err := controllerutil .SetControllerReference (o , ipAddressResource , r .Scheme ); err != nil {
171+ return ctrl.Result {}, fmt .Errorf ("failed to set controller reference: %w" , err )
165172 }
166173
167- err = r .Client .Create (ctx , ipAddressResource )
168- if err != nil {
174+ if err := r .Client .Create (ctx , ipAddressResource ); err != nil {
169175 if errReport := r .EventStatusRecorder .Report (ctx , o , netboxv1 .ConditionIpAssignedFalse , corev1 .EventTypeWarning , err ); errReport != nil {
170176 return ctrl.Result {}, errReport
171177 }
172- return ctrl.Result {}, err
178+ return ctrl.Result {}, fmt . Errorf ( "failed to create IpAddress: %w" , err )
173179 }
174180
175- err = r .EventStatusRecorder .Report (ctx , o , netboxv1 .ConditionIpAssignedTrue , corev1 .EventTypeNormal , nil )
176- if err != nil {
177- return ctrl.Result {}, err
181+ if err := r .EventStatusRecorder .Report (ctx , o , netboxv1 .ConditionIpAssignedTrue , corev1 .EventTypeNormal , nil ); err != nil {
182+ return ctrl.Result {}, fmt .Errorf ("failed to report IpAssigned condition: %w" , err )
178183 }
179184
180185 } else {
181186 // 6.b update fields of IPAddress object
182187 debugLogger .Info ("update ipaddress resource" )
183- err := r .Client .Get (ctx , ipAddressLookupKey , ipAddress )
184- if err != nil {
185- return ctrl.Result {}, err
188+ if err := r .Client .Get (ctx , ipAddressLookupKey , ipAddress ); err != nil {
189+ return ctrl.Result {}, fmt .Errorf ("failed to get IpAddress for update: %w" , err )
186190 }
187191
188192 updatedIpAddressSpec := generateIpAddressSpec (o , ipAddress .Spec .IpAddress , logger )
189- _ , err = ctrl .CreateOrUpdate (ctx , r .Client , ipAddress , func () error {
193+ _ , err : = ctrl .CreateOrUpdate (ctx , r .Client , ipAddress , func () error {
190194 // only add the mutable fields here
191195 ipAddress .Spec .CustomFields = updatedIpAddressSpec .CustomFields
192196 ipAddress .Spec .Comments = updatedIpAddressSpec .Comments
193197 ipAddress .Spec .Description = updatedIpAddressSpec .Description
194198 ipAddress .Spec .PreserveInNetbox = updatedIpAddressSpec .PreserveInNetbox
195- err = controllerutil .SetControllerReference (o , ipAddress , r .Scheme )
196- if err != nil {
197- return err
199+ if err := controllerutil .SetControllerReference (o , ipAddress , r .Scheme ); err != nil {
200+ return fmt .Errorf ("failed to set controller reference: %w" , err )
198201 }
199202 return nil
200203 })
201204 if err != nil {
202- return ctrl.Result {}, err
205+ return ctrl.Result {}, fmt . Errorf ( "failed to update IpAddress: %w" , err )
203206 }
204207 }
205208
206- // 7. update IPAddressClaim Ready status
207- debugLogger .Info ("update ipaddressclaim status" )
208-
209- if apismeta .IsStatusConditionTrue (ipAddress .Status .Conditions , "Ready" ) {
210- debugLogger .Info ("ipaddress status ready true" )
211- o .Status .IpAddress = ipAddress .Spec .IpAddress
212- o .Status .IpAddressDotDecimal = strings .Split (ipAddress .Spec .IpAddress , "/" )[0 ]
213- o .Status .IpAddressName = ipAddress .Name
214- err := r .EventStatusRecorder .Report (ctx , o , netboxv1 .ConditionIpClaimReadyTrue , corev1 .EventTypeNormal , nil )
215- if err != nil {
216- return ctrl.Result {}, err
217- }
218- } else {
219- debugLogger .Info ("ipaddress status ready false" )
220- err := r .EventStatusRecorder .Report (ctx , o , netboxv1 .ConditionIpClaimReadyFalse , corev1 .EventTypeWarning , nil )
221- if err != nil {
222- return ctrl.Result {}, err
223- }
224- return ctrl.Result {Requeue : true }, nil
225- }
226-
227209 logger .Info ("reconcile loop finished" )
228210 return ctrl.Result {}, nil
229211}
@@ -235,3 +217,38 @@ func (r *IpAddressClaimReconciler) SetupWithManager(mgr ctrl.Manager) error {
235217 Owns (& netboxv1.IpAddress {}).
236218 Complete (r )
237219}
220+
221+ // updateStatus updates the IpAddressClaim status based on the current state of the owned IpAddress.
222+ // This function is called as a deferred function in Reconcile to ensure status is always updated.
223+ func (r * IpAddressClaimReconciler ) updateStatus (ctx context.Context , claim * netboxv1.IpAddressClaim , lookupKey types.NamespacedName , debugLogger logr.Logger ) error {
224+ debugLogger .Info ("updating ipaddressclaim status" )
225+
226+ // Fetch the latest IpAddress object
227+ ipAddress := & netboxv1.IpAddress {}
228+ if err := r .Client .Get (ctx , lookupKey , ipAddress ); err != nil {
229+ if apierrors .IsNotFound (err ) {
230+ // IpAddress doesn't exist yet, this is expected during creation
231+ debugLogger .Info ("ipaddress not found, skipping status update" )
232+ return nil
233+ }
234+ return fmt .Errorf ("failed to get IpAddress for status update: %w" , err )
235+ }
236+
237+ // Update status based on IpAddress readiness
238+ if apismeta .IsStatusConditionTrue (ipAddress .Status .Conditions , "Ready" ) {
239+ debugLogger .Info ("ipaddress status ready true" )
240+ claim .Status .IpAddress = ipAddress .Spec .IpAddress
241+ claim .Status .IpAddressDotDecimal = strings .Split (ipAddress .Spec .IpAddress , "/" )[0 ]
242+ claim .Status .IpAddressName = ipAddress .Name
243+ if err := r .EventStatusRecorder .Report (ctx , claim , netboxv1 .ConditionIpClaimReadyTrue , corev1 .EventTypeNormal , nil ); err != nil {
244+ return fmt .Errorf ("failed to report IpClaimReady true condition: %w" , err )
245+ }
246+ } else {
247+ debugLogger .Info ("ipaddress status ready false" )
248+ if err := r .EventStatusRecorder .Report (ctx , claim , netboxv1 .ConditionIpClaimReadyFalse , corev1 .EventTypeWarning , nil ); err != nil {
249+ return fmt .Errorf ("failed to report IpClaimReady false condition: %w" , err )
250+ }
251+ }
252+
253+ return nil
254+ }
0 commit comments