Repository: syself/cluster-api-provider-hetzner
Pull request #712 (2023-03-30 07:29:30)

:seedling: Fix error handling of nil errors in controllers
**What this PR does / why we need it**:
After switching to standard lib (fmt) for errors, there is a different behavior. fmt does not return nil with fmt.Errorf if err == nil. This did not work with breakReconcile, so we remove it.

**TODOs**:
- [x] squashed commits
- [ ] includes documentation
- [x] adds unit tests




Patch:
diff --git a/controllers/hcloudmachine_controller.go b/controllers/hcloudmachine_controller.go
index e067bed6..7ad23565 100644
--- a/controllers/hcloudmachine_controller.go
+++ b/controllers/hcloudmachine_controller.go
@@ -163,11 +163,14 @@ func (r *HCloudMachineReconciler) reconcileDelete(ctx context.Context, machineSc
 	hcloudMachine := machineScope.HCloudMachine
 
 	// Delete servers.
-	if result, brk, err := breakReconcile(server.NewService(machineScope).Delete(ctx)); brk {
-		return result, fmt.Errorf("failed to delete servers for HCloudMachine %s/%s: %w",
-			hcloudMachine.Namespace, hcloudMachine.Name, err)
+	result, err := server.NewService(machineScope).Delete(ctx)
+	if err != nil {
+		return result, fmt.Errorf("failed to delete servers for HCloudMachine %s/%s: %w", hcloudMachine.Namespace, hcloudMachine.Name, err)
+	}
+	emptyResult := reconcile.Result{}
+	if result != emptyResult {
+		return result, nil
 	}
-
 	// Machine is deleted so remove the finalizer.
 	controllerutil.RemoveFinalizer(machineScope.HCloudMachine, infrav1.MachineFinalizer)
 
@@ -187,12 +190,13 @@ func (r *HCloudMachineReconciler) reconcileNormal(ctx context.Context, machineSc
 	}
 
 	// reconcile server
-	if result, brk, err := breakReconcile(server.NewService(machineScope).Reconcile(ctx)); brk {
+	result, err := server.NewService(machineScope).Reconcile(ctx)
+	if err != nil {
 		return result, fmt.Errorf("failed to reconcile server for HCloudMachine %s/%s: %w",
 			hcloudMachine.Namespace, hcloudMachine.Name, err)
 	}
 
-	return reconcile.Result{}, nil
+	return result, nil
 }
 
 // SetupWithManager sets up the controller with the Manager.
diff --git a/controllers/hcloudmachinetemplate_controller.go b/controllers/hcloudmachinetemplate_controller.go
index 320294d8..077772f7 100644
--- a/controllers/hcloudmachinetemplate_controller.go
+++ b/controllers/hcloudmachinetemplate_controller.go
@@ -123,7 +123,7 @@ func (r *HCloudMachineTemplateReconciler) Reconcile(ctx context.Context, req rec
 
 func (r *HCloudMachineTemplateReconciler) reconcile(ctx context.Context, machineTemplateScope *scope.HCloudMachineTemplateScope) (reconcile.Result, error) {
 	machineTemplateScope.Info("Reconciling HCloudMachineTemplate")
-	hcloudMachine := machineTemplateScope.HCloudMachineTemplate
+	hcloudMachineTemplate := machineTemplateScope.HCloudMachineTemplate
 
 	// If the HCloudMachineTemplate doesn't have our finalizer, add it.
 	controllerutil.AddFinalizer(machineTemplateScope.HCloudMachineTemplate, infrav1.MachineFinalizer)
@@ -134,9 +134,9 @@ func (r *HCloudMachineTemplateReconciler) reconcile(ctx context.Context, machine
 	}
 
 	// reconcile machine template
-	if result, brk, err := breakReconcile(machinetemplate.NewService(machineTemplateScope).Reconcile(ctx)); brk {
-		return result, fmt.Errorf("failed to reconcile machine template for HCloudMachineTemplate %s/%s: %w",
-			hcloudMachine.Namespace, hcloudMachine.Name, err)
+	if err := machinetemplate.NewService(machineTemplateScope).Reconcile(ctx); err != nil {
+		return reconcile.Result{}, fmt.Errorf("failed to reconcile machine template for HCloudMachineTemplate %s/%s: %w",
+			hcloudMachineTemplate.Namespace, hcloudMachineTemplate.Name, err)
 	}
 
 	return reconcile.Result{}, nil
diff --git a/controllers/hcloudremediation_controller.go b/controllers/hcloudremediation_controller.go
index 29f5ba46..9e7d6755 100644
--- a/controllers/hcloudremediation_controller.go
+++ b/controllers/hcloudremediation_controller.go
@@ -176,12 +176,13 @@ func (r *HCloudRemediationReconciler) reconcileNormal(ctx context.Context, remed
 	hcloudRemediation := remediationScope.HCloudRemediation
 
 	// reconcile bare metal remediation
-	if result, brk, err := breakReconcile(hcloudremediation.NewService(remediationScope).Reconcile(ctx)); brk {
+	result, err := hcloudremediation.NewService(remediationScope).Reconcile(ctx)
+	if err != nil {
 		return result, fmt.Errorf("failed to reconcile server for HCloudRemediation %s/%s: %w",
 			hcloudRemediation.Namespace, hcloudRemediation.Name, err)
 	}
 
-	return reconcile.Result{}, nil
+	return result, nil
 }
 
 // SetupWithManager sets up the controller with the Manager.
diff --git a/controllers/hetznerbaremetalhost_controller.go b/controllers/hetznerbaremetalhost_controller.go
index 43d3e7a9..73492683 100644
--- a/controllers/hetznerbaremetalhost_controller.go
+++ b/controllers/hetznerbaremetalhost_controller.go
@@ -58,7 +58,7 @@ type HetznerBareMetalHostReconciler struct {
 //+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=hetznerbaremetalhosts/finalizers,verbs=update
 
 // Reconcile implements the reconcilement of HetznerBareMetalHost objects.
-func (r *HetznerBareMetalHostReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
+func (r *HetznerBareMetalHostReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, reterr error) {
 	log := ctrl.LoggerFrom(ctx)
 
 	log.Info("Reconciling baremetal host", "name", req.Name)
@@ -89,9 +89,10 @@ func (r *HetznerBareMetalHostReconciler) Reconcile(ctx context.Context, req ctrl
 
 	// Certain cases need to be handled here and not later in the host state machine.
 	// If res != nil, then we should return, otherwise not.
-	res, err := r.reconcileSelectedStates(ctx, bmHost)
-	if res != nil {
-		return *res, err
+	res, err = r.reconcileSelectedStates(ctx, bmHost)
+	emptyResult := reconcile.Result{}
+	if res != emptyResult || err != nil {
+		return res, err
 	}
 
 	hetznerCluster := &infrav1.HetznerCluster{}
@@ -116,10 +117,9 @@ func (r *HetznerBareMetalHostReconciler) Reconcile(ctx context.Context, req ctrl
 
 	// Get secrets. Return when result != nil.
 	osSSHSecret, rescueSSHSecret, res, err := r.getSecrets(ctx, *secretManager, bmHost, hetznerCluster)
-	if res != nil {
-		return *res, err
+	if err != nil || res != emptyResult {
+		return res, err
 	}
-
 	// Create the scope.
 	hostScope, err := scope.NewBareMetalHostScope(ctx, scope.BareMetalHostScopeParams{
 		Logger:               log,
@@ -149,18 +149,15 @@ func (r *HetznerBareMetalHostReconciler) reconcile(
 	ctx context.Context,
 	hostScope *scope.BareMetalHostScope,
 ) (reconcile.Result, error) {
-	if result, brk, err := breakReconcile(host.NewService(hostScope).Reconcile(ctx)); brk {
-		return result, errors.Wrapf(
-			err,
-			"failed to reconcile HetznerBareMetalHost %s/%s",
-			hostScope.HetznerBareMetalHost.Namespace,
-			hostScope.HetznerBareMetalHost.Name,
-		)
+	result, err := host.NewService(hostScope).Reconcile(ctx)
+	if err != nil {
+		return result, fmt.Errorf("failed to reconcile HetznerBareMetalHost %s/%s: %w",
+			hostScope.HetznerBareMetalHost.Namespace, hostScope.HetznerBareMetalHost.Name, err)
 	}
-	return reconcile.Result{}, nil
+	return result, nil
 }
 
-func (r *HetznerBareMetalHostReconciler) reconcileSelectedStates(ctx context.Context, bmHost *infrav1.HetznerBareMetalHost) (*ctrl.Result, error) {
+func (r *HetznerBareMetalHostReconciler) reconcileSelectedStates(ctx context.Context, bmHost *infrav1.HetznerBareMetalHost) (res ctrl.Result, err error) {
 	log := ctrl.LoggerFrom(ctx)
 	switch bmHost.Spec.Status.ProvisioningState {
 	// Handle StateNone: check whether needs to be provisioned or deleted.
@@ -176,11 +173,11 @@ func (r *HetznerBareMetalHostReconciler) reconcileSelectedStates(ctx context.Con
 		if needsUpdate {
 			err := r.Update(ctx, bmHost)
 			if err != nil {
-				return nil, errors.Wrap(err, "failed to add finalizer")
+				return res, errors.Wrap(err, "failed to add finalizer")
 			}
 		}
 
-		return &ctrl.Result{RequeueAfter: 10 * time.Second}, nil
+		return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
 
 		// Handle StateDeleting
 	case infrav1.StateDeleting:
@@ -188,17 +185,17 @@ func (r *HetznerBareMetalHostReconciler) reconcileSelectedStates(ctx context.Con
 
 		if !utils.StringInList(bmHost.Finalizers, infrav1.BareMetalHostFinalizer) {
 			log.Info("Ready to be deleted")
-			return nil, nil
+			return res, nil
 		}
 
 		bmHost.Finalizers = utils.FilterStringFromList(bmHost.Finalizers, infrav1.BareMetalHostFinalizer)
 		if err := r.Update(context.Background(), bmHost); err != nil {
-			return nil, errors.Wrap(err, "failed to remove finalizer")
+			return res, errors.Wrap(err, "failed to remove finalizer")
 		}
 		log.Info("Cleanup complete. Removed finalizer", "remaining", bmHost.Finalizers)
-		return nil, nil
+		return res, nil
 	}
-	return nil, nil
+	return res, nil
 }
 
 func (r *HetznerBareMetalHostReconciler) getSecrets(
@@ -209,9 +206,10 @@ func (r *HetznerBareMetalHostReconciler) getSecrets(
 ) (
 	osSSHSecret *corev1.Secret,
 	rescueSSHSecret *corev1.Secret,
-	res *ctrl.Result,
+	res ctrl.Result,
 	reterr error,
 ) {
+	emptyResult := reconcile.Result{}
 	if bmHost.Spec.Status.SSHSpec != nil {
 		var err error
 		osSSHSecretNamespacedName := types.NamespacedName{Namespace: bmHost.Namespace, Name: bmHost.Spec.Status.SSHSpec.SecretRef.Name}
@@ -220,13 +218,14 @@ func (r *HetznerBareMetalHostReconciler) getSecrets(
 			if apierrors.IsNotFound(err) {
 				bmHost.SetError(infrav1.PreparationError, infrav1.ErrorMessageMissingOSSSHSecret)
 
-				if res, brk, err := breakReconcile(host.SaveHostAndReturn(ctx, r.Client, bmHost)); brk {
-					return nil, nil, &res, err
+				result, err := host.SaveHostAndReturn(ctx, r.Client, bmHost)
+				if result != emptyResult || err != nil {
+					return nil, nil, res, err
 				}
 
-				return nil, nil, &ctrl.Result{RequeueAfter: host.CalculateBackoff(bmHost.Spec.Status.ErrorCount)}, nil
+				return nil, nil, ctrl.Result{RequeueAfter: host.CalculateBackoff(bmHost.Spec.Status.ErrorCount)}, nil
 			}
-			return nil, nil, nil, errors.Wrap(err, "failed to get secret")
+			return nil, nil, res, errors.Wrap(err, "failed to get secret")
 		}
 
 		rescueSSHSecretNamespacedName := types.NamespacedName{Namespace: bmHost.Namespace, Name: hetznerCluster.Spec.SSHKeys.RobotRescueSecretRef.Name}
@@ -235,16 +234,17 @@ func (r *HetznerBareMetalHostReconciler) getSecrets(
 			if apierrors.IsNotFound(err) {
 				bmHost.SetError(infrav1.PreparationError, infrav1.ErrorMessageMissingRescueSSHSecret)
 
-				if res, brk, err := breakReconcile(host.SaveHostAndReturn(ctx, r.Client, bmHost)); brk {
-					return nil, nil, &res, err
+				result, err := host.SaveHostAndReturn(ctx, r.Client, bmHost)
+				if result != emptyResult || err != nil {
+					return nil, nil, result, err
 				}
 
-				return nil, nil, &ctrl.Result{RequeueAfter: host.CalculateBackoff(bmHost.Spec.Status.ErrorCount)}, nil
+				return nil, nil, ctrl.Result{RequeueAfter: host.CalculateBackoff(bmHost.Spec.Status.ErrorCount)}, nil
 			}
-			return nil, nil, nil, errors.Wrap(err, "failed to acquire secret")
+			return nil, nil, res, errors.Wrap(err, "failed to acquire secret")
 		}
 	}
-	return osSSHSecret, rescueSSHSecret, nil, nil
+	return osSSHSecret, rescueSSHSecret, res, nil
 }
 
 func getAndValidateRobotCredentials(
@@ -296,8 +296,10 @@ func hetznerSecretErrorResult(
 	case *secretutil.ResolveSecretRefError:
 		bmHost.SetError(infrav1.PreparationError, infrav1.ErrorMessageMissingHetznerSecret)
 
-		if res, brk, err := breakReconcile(host.SaveHostAndReturn(ctx, client, bmHost)); brk {
-			return res, err
+		result, err := host.SaveHostAndReturn(ctx, client, bmHost)
+		emptyResult := reconcile.Result{}
+		if result != emptyResult || err != nil {
+			return result, err
 		}
 		backoff := host.CalculateBackoff(bmHost.Spec.Status.ErrorCount)
 		res = ctrl.Result{RequeueAfter: backoff}
@@ -305,9 +307,7 @@ func hetznerSecretErrorResult(
 	case *bmclient.CredentialsValidationError:
 		bmHost.SetError(infrav1.PreparationError, infrav1.ErrorMessageMissingOrInvalidSecretData)
 
-		if res, brk, err := breakReconcile(host.SaveHostAndReturn(ctx, client, bmHost)); brk {
-			return res, err
-		}
+		res, err = host.SaveHostAndReturn(ctx, client, bmHost)
 
 	default:
 		return ctrl.Result{}, errors.Wrap(err, "An unhandled failure occurred")
diff --git a/controllers/hetznerbaremetalmachine_controller.go b/controllers/hetznerbaremetalmachine_controller.go
index a9c918dd..76d0008d 100644
--- a/controllers/hetznerbaremetalmachine_controller.go
+++ b/controllers/hetznerbaremetalmachine_controller.go
@@ -149,7 +149,8 @@ func (r *HetznerBareMetalMachineReconciler) Reconcile(ctx context.Context, req r
 func (r *HetznerBareMetalMachineReconciler) reconcileDelete(ctx context.Context, machineScope *scope.BareMetalMachineScope) (reconcile.Result, error) {
 	machineScope.Info("Reconciling HetznerBareMetalMachine delete")
 	// delete servers
-	if result, brk, err := breakReconcile(baremetal.NewService(machineScope).Delete(ctx)); brk {
+	result, err := baremetal.NewService(machineScope).Delete(ctx)
+	if err != nil {
 		var requeueError *scope.RequeueAfterError
 		if ok := errors.As(err, &requeueError); ok {
 			return reconcile.Result{Requeue: true, RequeueAfter: requeueError.GetRequeueAfter()}, nil
@@ -157,11 +158,14 @@ func (r *HetznerBareMetalMachineReconciler) reconcileDelete(ctx context.Context,
 		return result, fmt.Errorf("failed to delete servers for HetznerBareMetalMachine %s/%s: %w",
 			machineScope.BareMetalMachine.Namespace, machineScope.BareMetalMachine.Name, err)
 	}
-
+	emptyResult := reconcile.Result{}
+	if result != emptyResult {
+		return result, nil
+	}
 	// Machine is deleted so remove the finalizer.
 	controllerutil.RemoveFinalizer(machineScope.BareMetalMachine, infrav1.BareMetalMachineFinalizer)
 
-	return reconcile.Result{}, nil
+	return result, nil
 }
 
 func (r *HetznerBareMetalMachineReconciler) reconcileNormal(ctx context.Context, machineScope *scope.BareMetalMachineScope) (reconcile.Result, error) {
@@ -176,12 +180,13 @@ func (r *HetznerBareMetalMachineReconciler) reconcileNormal(ctx context.Context,
 	}
 
 	// reconcile server
-	if result, brk, err := breakReconcile(baremetal.NewService(machineScope).Reconcile(ctx)); brk {
+	result, err := baremetal.NewService(machineScope).Reconcile(ctx)
+	if err != nil {
 		return result, fmt.Errorf("failed to reconcile server for HetznerBareMetalMachine %s/%s: %w",
 			machineScope.BareMetalMachine.Namespace, machineScope.BareMetalMachine.Name, err)
 	}
 
-	return reconcile.Result{}, nil
+	return result, nil
 }
 
 // SetupWithManager sets up the controller with the Manager.
diff --git a/controllers/hetznerbaremetalremediation_controller.go b/controllers/hetznerbaremetalremediation_controller.go
index 4d3888da..bf1fe382 100644
--- a/controllers/hetznerbaremetalremediation_controller.go
+++ b/controllers/hetznerbaremetalremediation_controller.go
@@ -157,12 +157,13 @@ func (r *HetznerBareMetalRemediationReconciler) reconcileNormal(ctx context.Cont
 	bareMetalRemediation := remediationScope.BareMetalRemediation
 
 	// reconcile bare metal remediation
-	if result, brk, err := breakReconcile(remediation.NewService(remediationScope).Reconcile(ctx)); brk {
+	result, err := remediation.NewService(remediationScope).Reconcile(ctx)
+	if err != nil {
 		return result, fmt.Errorf("failed to reconcile server for BareMetalRemediation %s/%s: %w",
 			bareMetalRemediation.Namespace, bareMetalRemediation.Name, err)
 	}
 
-	return reconcile.Result{}, nil
+	return result, nil
 }
 
 // SetupWithManager sets up the controller with the Manager.
diff --git a/controllers/utils.go b/controllers/utils.go
deleted file mode 100644
index f1b4570d..00000000
--- a/controllers/utils.go
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-Copyright 2023 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package controllers
-
-import "sigs.k8s.io/controller-runtime/pkg/reconcile"
-
-func breakReconcile(ctrl *reconcile.Result, err error) (reconcile.Result, bool, error) {
-	c := reconcile.Result{}
-	if ctrl != nil {
-		c = *ctrl
-	}
-	return c, ctrl != nil || err != nil, err
-}
diff --git a/pkg/services/baremetal/baremetal/baremetal.go b/pkg/services/baremetal/baremetal/baremetal.go
index ad1475cf..8e433ce5 100644
--- a/pkg/services/baremetal/baremetal/baremetal.go
+++ b/pkg/services/baremetal/baremetal/baremetal.go
@@ -74,7 +74,7 @@ func NewService(scope *scope.BareMetalMachineScope) *Service {
 }
 
 // Reconcile implements reconcilement of HetznerBareMetalMachines.
-func (s *Service) Reconcile(ctx context.Context) (_ *reconcile.Result, err error) {
+func (s *Service) Reconcile(ctx context.Context) (res reconcile.Result, err error) {
 	s.scope.Info("Reconciling bare metal machine")
 
 	// Make sure bootstrap data is available and populated. If not, return, we
@@ -87,7 +87,7 @@ func (s *Service) Reconcile(ctx context.Context) (_ *reconcile.Result, err error
 			clusterv1.ConditionSeverityInfo,
 			"bootstrap not ready yet",
 		)
-		return nil, nil
+		return res, nil
 	}
 
 	conditions.MarkTrue(
@@ -105,36 +105,36 @@ func (s *Service) Reconcile(ctx context.Context) (_ *reconcile.Result, err error
 
 	// update the machine
 	if err := s.update(ctx); err != nil {
-		return nil, fmt.Errorf("failed to update machine: %w", err)
+		return checkForRequeueError(err, "failed to update machine")
 	}
 
 	// set providerID if necessary
 	if err := s.setProviderID(ctx); err != nil {
-		return nil, fmt.Errorf("failed to set providerID: %w", err)
+		return res, fmt.Errorf("failed to set providerID: %w", err)
 	}
 
 	// set machine ready
 	s.scope.BareMetalMachine.Status.Ready = true
 	conditions.MarkTrue(s.scope.BareMetalMachine, infrav1.InstanceReadyCondition)
 
-	return nil, nil
+	return res, nil
 }
 
 // Delete implements delete method of bare metal machine.
-func (s *Service) Delete(ctx context.Context) (_ *reconcile.Result, err error) {
+func (s *Service) Delete(ctx context.Context) (res reconcile.Result, err error) {
 	s.scope.Info("Deleting bare metal machine")
 
 	// get host - ignore if not found
 	host, helper, err := s.getAssociatedHost(ctx)
 	if err != nil && !apierrors.IsNotFound(err) {
-		return nil, fmt.Errorf("failed to get associated host: %w", err)
+		return res, fmt.Errorf("failed to get associated host: %w", err)
 	}
 
 	if host != nil && host.Spec.ConsumerRef != nil {
 		// remove control plane as load balancer target
 		if s.scope.IsControlPlane() && s.scope.HetznerCluster.Spec.ControlPlaneLoadBalancer.Enabled {
 			if err := s.removeAttachedServerOfLoadBalancer(ctx, host); err != nil {
-				return nil, fmt.Errorf("failed to delete attached server of load balancer: %w", err)
+				return res, fmt.Errorf("failed to delete attached server of load balancer: %w", err)
 			}
 		}
 
@@ -145,23 +145,23 @@ func (s *Service) Delete(ctx context.Context) (_ *reconcile.Result, err error) {
 			// remove the ownerRef to this host, even if the consumerRef references another machine
 			host.OwnerReferences = s.removeOwnerRef(host.OwnerReferences)
 
-			return nil, nil
+			return res, nil
 		}
 
 		if removeMachineSpecsFromHost(host) {
 			// Patch the host object. If the error is NotFound, do not return the error.
 			if err := analyzePatchError(helper.Patch(ctx, host), true); err != nil {
-				return nil, fmt.Errorf("failed to patch host: %w", err)
+				return checkForRequeueError(err, "failed to patch host")
 			}
 
 			s.scope.Info("Patched BaremetalHost while deprovisioning, requeuing")
-			return nil, &scope.RequeueAfterError{}
+			return reconcile.Result{Requeue: true}, nil
 		}
 
 		// check if deprovisioning is done
 		if host.Spec.Status.ProvisioningState != infrav1.StateNone {
 			s.scope.Info("Deprovisioning BaremetalHost, requeuing", "provisioning state", host.Spec.Status.ProvisioningState)
-			return nil, &scope.RequeueAfterError{RequeueAfter: requeueAfter}
+			return reconcile.Result{RequeueAfter: requeueAfter}, nil
 		}
 
 		// deprovisiong is done - remove all references of host
@@ -177,7 +177,7 @@ func (s *Service) Delete(ctx context.Context) (_ *reconcile.Result, err error) {
 
 		// patch host object
 		if err := analyzePatchError(helper.Patch(ctx, host), true); err != nil {
-			return nil, fmt.Errorf("failed to patch host: %w", err)
+			return checkForRequeueError(err, "failed to patch host")
 		}
 	}
 
@@ -189,7 +189,7 @@ func (s *Service) Delete(ctx context.Context) (_ *reconcile.Result, err error) {
 		"HetznerBareMetalMachine with name %s deleted",
 		s.scope.Name(),
 	)
-	return nil, nil
+	return res, nil
 }
 
 // update updates a machine and is invoked by the Machine Controller.
@@ -814,16 +814,16 @@ func splitHostKey(key string) (namespace, name string) {
 	return parts[0], parts[1]
 }
 
-func checkForRequeueError(err error, errMessage string) (*reconcile.Result, error) {
+func checkForRequeueError(err error, errMessage string) (res reconcile.Result, reterr error) {
 	if err == nil {
-		return nil, nil
+		return res, nil
 	}
 	var requeueError *scope.RequeueAfterError
 	if ok := errors.As(err, &requeueError); ok {
-		return &reconcile.Result{Requeue: true, RequeueAfter: requeueError.GetRequeueAfter()}, nil
+		return reconcile.Result{Requeue: true, RequeueAfter: requeueError.GetRequeueAfter()}, nil
 	}
 
-	return nil, fmt.Errorf("%s: %w", errMessage, err)
+	return res, fmt.Errorf("%s: %w", errMessage, err)
 }
 
 func providerIDFromServerID(serverID int) string {
diff --git a/pkg/services/baremetal/host/host.go b/pkg/services/baremetal/host/host.go
index 31cebb3d..f8fdfc16 100644
--- a/pkg/services/baremetal/host/host.go
+++ b/pkg/services/baremetal/host/host.go
@@ -68,7 +68,7 @@ func NewService(scope *scope.BareMetalHostScope) *Service {
 }
 
 // Reconcile implements reconcilement of HetznerBareMetalHosts.
-func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
+func (s *Service) Reconcile(ctx context.Context) (result ctrl.Result, err error) {
 	s.scope.Info("Reconciling baremetal host", "name", s.scope.HetznerBareMetalHost.Name)
 
 	initialState := s.scope.HetznerBareMetalHost.Spec.Status.ProvisioningState
@@ -79,9 +79,9 @@ func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
 
 	// reconcile state
 	actResult := hostStateMachine.ReconcileState(ctx)
-	result, err := actResult.Result()
+	result, err = actResult.Result()
 	if err != nil {
-		return &ctrl.Result{Requeue: true}, fmt.Errorf("action %q failed: %w", initialState, err)
+		return ctrl.Result{Requeue: true}, fmt.Errorf("action %q failed: %w", initialState, err)
 	}
 
 	// save host if it changed during reconciliation
@@ -89,7 +89,7 @@ func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
 		return SaveHostAndReturn(ctx, s.scope.Client, s.scope.HetznerBareMetalHost)
 	}
 
-	return &result, nil
+	return result, nil
 }
 
 func (s *Service) recordActionFailure(errorType infrav1.ErrorType, errorMessage string) actionFailed {
@@ -99,17 +99,17 @@ func (s *Service) recordActionFailure(errorType infrav1.ErrorType, errorMessage
 }
 
 // SaveHostAndReturn saves host object, updates LastUpdated in host status and returns the reconcile Result.
-func SaveHostAndReturn(ctx context.Context, client client.Client, host *infrav1.HetznerBareMetalHost) (*reconcile.Result, error) {
+func SaveHostAndReturn(ctx context.Context, client client.Client, host *infrav1.HetznerBareMetalHost) (res reconcile.Result, err error) {
 	t := metav1.Now()
 	host.Spec.Status.LastUpdated = &t
 
 	if err := client.Update(ctx, host); err != nil {
 		if apierrors.IsConflict(err) {
-			return &reconcile.Result{Requeue: true}, nil
+			return reconcile.Result{Requeue: true}, nil
 		}
-		return nil, fmt.Errorf("failed to update host object: %w", err)
+		return res, fmt.Errorf("failed to update host object: %w", err)
 	}
-	return nil, nil
+	return res, nil
 }
 
 func (s *Service) getSSHKeysAndUpdateStatus() (osSSHSecret *corev1.Secret, rescueSSHSecret *corev1.Secret, err error) {
@@ -266,6 +266,9 @@ func (s *Service) actionPreparing() actionResult {
 }
 
 func (s *Service) ensureSSHKey(sshSecretRef infrav1.SSHSecretRef, sshSecret *corev1.Secret) (infrav1.SSHKey, actionResult) {
+	if sshSecret == nil {
+		return infrav1.SSHKey{}, actionError{err: fmt.Errorf("ssh secret is nil")}
+	}
 	hetznerSSHKeys, err := s.scope.RobotClient.ListSSHKeys()
 	if err != nil {
 		if models.IsError(err, models.ErrorCodeRateLimitExceeded) {
diff --git a/pkg/services/baremetal/remediation/remediation.go b/pkg/services/baremetal/remediation/remediation.go
index cdeb9008..34f94c89 100644
--- a/pkg/services/baremetal/remediation/remediation.go
+++ b/pkg/services/baremetal/remediation/remediation.go
@@ -50,7 +50,7 @@ func NewService(scope *scope.BareMetalRemediationScope) *Service {
 }
 
 // Reconcile implements reconcilement of Hetzner bare metal remediation.
-func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
+func (s *Service) Reconcile(ctx context.Context) (res ctrl.Result, err error) {
 	log := ctrl.LoggerFrom(ctx)
 
 	log.Info("Reconciling baremetal remediation", "name", s.scope.BareMetalRemediation.Name)
@@ -59,7 +59,7 @@ func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
 	host, helper, err := s.getUnhealthyHost(ctx)
 	if err != nil {
 		s.scope.Error(err, "unable to find a host for unhealthy machine")
-		return nil, errors.Wrapf(err, "unable to find a host for unhealthy machine")
+		return res, errors.Wrapf(err, "unable to find a host for unhealthy machine")
 	}
 
 	// If host is not in state provisioned, then remediate immediately
@@ -67,7 +67,7 @@ func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
 		log.Info("Deleting host without remediation", "provisioningState", host.Spec.Status.ProvisioningState)
 		if err := s.setOwnerRemediatedConditionNew(ctx); err != nil {
 			s.scope.Error(err, "error setting cluster api conditions")
-			return nil, errors.Wrapf(err, "error setting cluster api conditions")
+			return res, errors.Wrapf(err, "error setting cluster api conditions")
 		}
 	}
 
@@ -75,7 +75,7 @@ func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
 
 	if remediationType != infrav1.RemediationTypeReboot {
 		s.scope.Info("unsupported remediation strategy")
-		return nil, nil
+		return res, nil
 	}
 
 	if remediationType == infrav1.RemediationTypeReboot {
@@ -92,17 +92,17 @@ func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
 		default:
 		}
 	}
-	return nil, nil
+	return res, nil
 }
 
-func (s *Service) handlePhaseRunning(ctx context.Context, host *infrav1.HetznerBareMetalHost, helper *patch.Helper) (*ctrl.Result, error) {
+func (s *Service) handlePhaseRunning(ctx context.Context, host *infrav1.HetznerBareMetalHost, helper *patch.Helper) (res ctrl.Result, err error) {
 	// host is not rebooted yet
 	if s.scope.BareMetalRemediation.Status.LastRemediated == nil {
 		s.scope.Info("Rebooting the host")
 		err := s.setRebootAnnotation(ctx, host, helper)
 		if err != nil {
 			s.scope.Error(err, "error setting reboot annotation")
-			return nil, errors.Wrap(err, "error setting reboot annotation")
+			return res, errors.Wrap(err, "error setting reboot annotation")
 		}
 		now := metav1.Now()
 		s.scope.BareMetalRemediation.Status.LastRemediated = &now
@@ -117,7 +117,7 @@ func (s *Service) handlePhaseRunning(ctx context.Context, host *infrav1.HetznerB
 			err := s.setRebootAnnotation(ctx, host, helper)
 			if err != nil {
 				s.scope.Error(err, "error setting reboot annotation")
-				return nil, errors.Wrapf(err, "error setting reboot annotation")
+				return res, errors.Wrapf(err, "error setting reboot annotation")
 			}
 			now := metav1.Now()
 			s.scope.BareMetalRemediation.Status.LastRemediated = &now
@@ -126,15 +126,15 @@ func (s *Service) handlePhaseRunning(ctx context.Context, host *infrav1.HetznerB
 
 		if nextRemediation > 0 {
 			// Not yet time to remediate, requeue
-			return &ctrl.Result{RequeueAfter: nextRemediation}, nil
+			return ctrl.Result{RequeueAfter: nextRemediation}, nil
 		}
 	} else {
 		s.scope.BareMetalRemediation.Status.Phase = infrav1.PhaseWaiting
 	}
-	return nil, nil
+	return res, nil
 }
 
-func (s *Service) handlePhaseWaiting(ctx context.Context) (*ctrl.Result, error) {
+func (s *Service) handlePhaseWaiting(ctx context.Context) (res ctrl.Result, err error) {
 	okToStop, nextCheck := s.timeToRemediate(s.scope.BareMetalRemediation.Spec.Strategy.Timeout.Duration)
 
 	if okToStop {
@@ -146,15 +146,15 @@ func (s *Service) handlePhaseWaiting(ctx context.Context) (*ctrl.Result, error)
 		err := s.setOwnerRemediatedConditionNew(ctx)
 		if err != nil {
 			s.scope.Error(err, "error setting cluster api conditions")
-			return nil, errors.Wrapf(err, "error setting cluster api conditions")
+			return res, errors.Wrapf(err, "error setting cluster api conditions")
 		}
 	}
 
 	if nextCheck > 0 {
 		// Not yet time to stop remediation, requeue
-		return &ctrl.Result{RequeueAfter: nextCheck}, nil
+		return ctrl.Result{RequeueAfter: nextCheck}, nil
 	}
-	return nil, nil
+	return res, nil
 }
 
 func (s *Service) getUnhealthyHost(ctx context.Context) (*infrav1.HetznerBareMetalHost, *patch.Helper, error) {
diff --git a/pkg/services/hcloud/machinetemplate/machinetemplate.go b/pkg/services/hcloud/machinetemplate/machinetemplate.go
index 07620a1b..415a4c51 100644
--- a/pkg/services/hcloud/machinetemplate/machinetemplate.go
+++ b/pkg/services/hcloud/machinetemplate/machinetemplate.go
@@ -28,7 +28,6 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/cluster-api/util/record"
-	ctrl "sigs.k8s.io/controller-runtime"
 )
 
 // Service defines struct with HCloudMachineTemplate scope to reconcile HCloud machine templates.
@@ -44,14 +43,14 @@ func NewService(scope *scope.HCloudMachineTemplateScope) *Service {
 }
 
 // Reconcile implements reconcilement of HCloud machines.
-func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
+func (s *Service) Reconcile(ctx context.Context) error {
 	capacity, err := s.getCapacity(ctx)
 	if err != nil {
-		return nil, errors.Wrap(err, "failed to get capacity")
+		return errors.Wrap(err, "failed to get capacity")
 	}
 
 	s.scope.HCloudMachineTemplate.Status.Capacity = capacity
-	return nil, nil
+	return nil
 }
 
 func (s *Service) getCapacity(ctx context.Context) (corev1.ResourceList, error) {
diff --git a/pkg/services/hcloud/remediation/remediation.go b/pkg/services/hcloud/remediation/remediation.go
index 77a4042a..ce92f0fb 100644
--- a/pkg/services/hcloud/remediation/remediation.go
+++ b/pkg/services/hcloud/remediation/remediation.go
@@ -45,12 +45,12 @@ func NewService(scope *scope.HCloudRemediationScope) *Service {
 }
 
 // Reconcile implements reconcilement of HCloudRemediation.
-func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
+func (s *Service) Reconcile(ctx context.Context) (res ctrl.Result, err error) {
 	s.scope.Info("Reconciling hcloud remediation", "name", s.scope.HCloudRemediation.Name)
 
 	server, err := s.findServer(ctx)
 	if err != nil {
-		return nil, fmt.Errorf("failed to find the server of unhealthy machine: %w", err)
+		return res, fmt.Errorf("failed to find the server of unhealthy machine: %w", err)
 	}
 
 	remediationType := s.scope.HCloudRemediation.Spec.Strategy.Type
@@ -58,7 +58,7 @@ func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
 	if remediationType != infrav1.RemediationTypeReboot {
 		s.scope.Info("unsupported remediation strategy")
 		record.Warnf(s.scope.HCloudRemediation, "UnsupportedRemdiationStrategy", "remediation strategy %q is unsupported", remediationType)
-		return nil, nil
+		return res, nil
 	}
 
 	if remediationType == infrav1.RemediationTypeReboot {
@@ -75,16 +75,16 @@ func (s *Service) Reconcile(ctx context.Context) (_ *ctrl.Result, err error) {
 		default:
 		}
 	}
-	return nil, nil
+	return res, nil
 }
 
-func (s *Service) handlePhaseRunning(ctx context.Context, server *hcloud.Server) (*ctrl.Result, error) {
+func (s *Service) handlePhaseRunning(ctx context.Context, server *hcloud.Server) (res ctrl.Result, err error) {
 	// server is not rebooted yet
 	if s.scope.HCloudRemediation.Status.LastRemediated == nil {
 		s.scope.Info("Rebooting the server")
 
 		if err := s.rebootServer(ctx, server); err != nil {
-			return nil, fmt.Errorf("failed to reboot server: %w", err)
+			return res, fmt.Errorf("failed to reboot server: %w", err)
 		}
 
 		now := metav1.Now()
@@ -102,7 +102,7 @@ func (s *Service) handlePhaseRunning(ctx context.Context, server *hcloud.Server)
 			s.scope.Info("Rebooting the server")
 
 			if err := s.rebootServer(ctx, server); err != nil {
-				return nil, fmt.Errorf("failed to reboot server: %w", err)
+				return res, fmt.Errorf("failed to reboot server: %w", err)
 			}
 
 			now := metav1.Now()
@@ -112,15 +112,15 @@ func (s *Service) handlePhaseRunning(ctx context.Context, server *hcloud.Server)
 
 		if nextRemediation > 0 {
 			// Not yet time to remediate, requeue
-			return &ctrl.Result{RequeueAfter: nextRemediation}, nil
+			return ctrl.Result{RequeueAfter: nextRemediation}, nil
 		}
 	} else {
 		s.scope.HCloudRemediation.Status.Phase = infrav1.PhaseWaiting
 	}
-	return nil, nil
+	return res, nil
 }
 
-func (s *Service) handlePhaseWaiting(ctx context.Context) (*ctrl.Result, error) {
+func (s *Service) handlePhaseWaiting(ctx context.Context) (res ctrl.Result, err error) {
 	okToStop, nextCheck := s.timeToRemediate()
 
 	if okToStop {
@@ -130,15 +130,15 @@ func (s *Service) handlePhaseWaiting(ctx context.Context) (*ctrl.Result, error)
 		// preflight checks and handles the Machine deletion
 
 		if err := s.setOwnerRemediatedCondition(ctx); err != nil {
-			return nil, fmt.Errorf("failed to set conditions on CAPI machine: %w", err)
+			return res, fmt.Errorf("failed to set conditions on CAPI machine: %w", err)
 		}
 	}
 
 	if nextCheck > 0 {
 		// Not yet time to stop remediation, requeue
-		return &ctrl.Result{RequeueAfter: nextCheck}, nil
+		return ctrl.Result{RequeueAfter: nextCheck}, nil
 	}
-	return nil, nil
+	return res, nil
 }
 
 func (s *Service) findServer(ctx context.Context) (*hcloud.Server, error) {
diff --git a/pkg/services/hcloud/server/server.go b/pkg/services/hcloud/server/server.go
index 9ab83ef2..f1dfd664 100644
--- a/pkg/services/hcloud/server/server.go
+++ b/pkg/services/hcloud/server/server.go
@@ -53,11 +53,11 @@ func NewService(scope *scope.MachineScope) *Service {
 }
 
 // Reconcile implements reconcilement of HCloud machines.
-func (s *Service) Reconcile(ctx context.Context) (_ *reconcile.Result, err error) {
+func (s *Service) Reconcile(ctx context.Context) (res reconcile.Result, err error) {
 	// detect failure domain
 	failureDomain, err := s.scope.GetFailureDomain()
 	if err != nil {
-		return nil, fmt.Errorf("failed to get failure domain: %w", err)
+		return res, fmt.Errorf("failed to get failure domain: %w", err)
 	}
 
 	// set in status of machine
@@ -72,7 +72,7 @@ func (s *Service) Reconcile(ctx context.Context) (_ *reconcile.Result, err error
 			clusterv1.ConditionSeverityInfo,
 			"bootstrap not ready yet",
 		)
-		return &reconcile.Result{RequeueAfter: 10 * time.Second}, nil
+		return reconcile.Result{RequeueAfter: 10 * time.Second}, nil
 	}
 
 	conditions.MarkTrue(s.scope.HCloudMachine, infrav1.InstanceBootstrapReadyCondition)
@@ -80,14 +80,14 @@ func (s *Service) Reconcile(ctx context.Context) (_ *reconcile.Result, err error
 	// try to find an existing server
 	server, err := s.findServer(ctx)
 	if err != nil {
-		return nil, fmt.Errorf("failed to get server: %w", err)
+		return res, fmt.Errorf("failed to get server: %w", err)
 	}
 
 	// if no server is found we have to create one
 	if server == nil {
 		server, err = s.createServer(ctx)
 		if err != nil {
-			return nil, fmt.Errorf("failed to create server: %w", err)
+			return res, fmt.Errorf("failed to create server: %w", err)
 		}
 
 		record.Eventf(s.scope.HCloudMachine, "SuccessfulCreate", "Created new server with id %d", server.ID)
@@ -106,17 +106,17 @@ func (s *Service) Reconcile(ctx context.Context) (_ *reconcile.Result, err error
 		// If we don't return here, the condition InstanceReady would get marked as true in this
 		// case. However, if the server is stuck and does not power on, we should not mark the
 		// condition InstanceReady as true to be able to remediate the server after a timeout.
-		return &reconcile.Result{RequeueAfter: 10 * time.Second}, nil
+		return reconcile.Result{RequeueAfter: 10 * time.Second}, nil
 	case hcloud.ServerStatusRunning: // Do nothing
 	default:
 		s.scope.SetReady(false)
 		s.scope.V(1).Info("server not in running state", "server", server.Name, "status", server.Status)
-		return &reconcile.Result{RequeueAfter: 2 * time.Second}, nil
+		return reconcile.Result{RequeueAfter: 2 * time.Second}, nil
 	}
 
 	// check whether server is attached to the network
 	if err := s.reconcileNetworkAttachment(ctx, server); err != nil {
-		return nil, fmt.Errorf("failed to reconcile network attachement: %w", err)
+		return res, fmt.Errorf("failed to reconcile network attachement: %w", err)
 	}
 
 	providerID := providerIDFromServerID(server.ID)
@@ -125,39 +125,39 @@ func (s *Service) Reconcile(ctx context.Context) (_ *reconcile.Result, err error
 		s.scope.SetProviderID(providerID)
 		s.scope.SetReady(true)
 		conditions.MarkTrue(s.scope.HCloudMachine, infrav1.InstanceReadyCondition)
-		return nil, nil
+		return res, nil
 	}
 
 	// all control planes have to be attached to the load balancer if it exists
 	if err := s.reconcileLoadBalancerAttachment(ctx, server); err != nil {
-		return nil, fmt.Errorf("failed to reconcile load balancer attachement: %w", err)
+		return res, fmt.Errorf("failed to reconcile load balancer attachement: %w", err)
 	}
 
 	s.scope.SetProviderID(providerID)
 	s.scope.SetReady(true)
 	conditions.MarkTrue(s.scope.HCloudMachine, infrav1.InstanceReadyCondition)
 
-	return nil, nil
+	return res, nil
 }
 
 // Delete implements delete method of server.
-func (s *Service) Delete(ctx context.Context) (_ *reconcile.Result, err error) {
+func (s *Service) Delete(ctx context.Context) (res reconcile.Result, err error) {
 	// find current server
 	server, err := s.findServer(ctx)
 	if err != nil {
-		return nil, fmt.Errorf("failed to find server: %w", err)
+		return res, fmt.Errorf("failed to find server: %w", err)
 	}
 
 	// if no server has been found, then nothing can be deleted
 	if server == nil {
 		s.scope.V(1).Info("Unable to locate HCloud server by ID or tags")
 		record.Warnf(s.scope.HCloudMachine, "NoInstanceFound", "Unable to find matching HCloud server for %s", s.scope.Name())
-		return nil, nil
+		return res, nil
 	}
 
 	if s.scope.IsControlPlane() && s.scope.HetznerCluster.Spec.ControlPlaneLoadBalancer.Enabled {
 		if err := s.deleteServerOfLoadBalancer(ctx, server); err != nil {
-			return nil, fmt.Errorf("failed to delete attached server of loadbalancer: %w", err)
+			return res, fmt.Errorf("failed to delete attached server of loadbalancer: %w", err)
 		}
 	}
 
@@ -169,7 +169,7 @@ func (s *Service) Delete(ctx context.Context) (_ *reconcile.Result, err error) {
 		return s.handleDeleteServerStatusOff(ctx, server)
 	}
 
-	return &reconcile.Result{RequeueAfter: 5 * time.Second}, nil
+	return reconcile.Result{RequeueAfter: 5 * time.Second}, nil
 }
 
 func (s *Service) reconcileNetworkAttachment(ctx context.Context, server *hcloud.Server) error {
@@ -452,7 +452,7 @@ func (s *Service) getServerImage(ctx context.Context) (*hcloud.Image, error) {
 	return images[0], nil
 }
 
-func (s *Service) handleServerStatusOff(ctx context.Context, server *hcloud.Server) (*reconcile.Result, error) {
+func (s *Service) handleServerStatusOff(ctx context.Context, server *hcloud.Server) (res reconcile.Result, err error) {
 	// Check if server is in ServerStatusOff and turn it on. This is to avoid a bug of Hetzner where
 	// sometimes machines are created and not turned on
 
@@ -470,12 +470,12 @@ func (s *Service) handleServerStatusOff(ctx context.Context, server *hcloud.Serv
 						"exceeded rate limit with calling hcloud function PowerOnServer",
 					)
 				}
-				return nil, fmt.Errorf("failed to power on server: %w", err)
+				return res, fmt.Errorf("failed to power on server: %w", err)
 			}
 		} else {
 			// Timed out. Set failure reason
 			s.scope.SetError("reached timeout of waiting for machines that are switched off", capierrors.CreateMachineError)
-			return nil, nil
+			return res, nil
 		}
 	} else {
 		// No condition set yet. Try to power server on.
@@ -487,7 +487,7 @@ func (s *Service) handleServerStatusOff(ctx context.Context, server *hcloud.Serv
 					"exceeded rate limit with calling hcloud function PowerOnServer",
 				)
 			}
-			return nil, fmt.Errorf("failed to power on server: %w", err)
+			return res, fmt.Errorf("failed to power on server: %w", err)
 		}
 		conditions.MarkFalse(
 			s.scope.HCloudMachine,
@@ -499,10 +499,10 @@ func (s *Service) handleServerStatusOff(ctx context.Context, server *hcloud.Serv
 	}
 
 	// Try again in 30 sec.
-	return &reconcile.Result{RequeueAfter: 30 * time.Second}, nil
+	return reconcile.Result{RequeueAfter: 30 * time.Second}, nil
 }
 
-func (s *Service) handleDeleteServerStatusRunning(ctx context.Context, server *hcloud.Server) (*reconcile.Result, error) {
+func (s *Service) handleDeleteServerStatusRunning(ctx context.Context, server *hcloud.Server) (res reconcile.Result, err error) {
 	// Check if the server has been tried to shut down already and if so,
 	// whether the time of the last condition change + maxWaitTime is already in the past.
 	// With one of these two conditions true, delete server immediately. Otherwise, shut it down and requeue.
@@ -518,14 +518,14 @@ func (s *Service) handleDeleteServerStatusRunning(ctx context.Context, server *h
 					"exceeded rate limit with calling hcloud function ShutdownServer",
 				)
 			}
-			return nil, fmt.Errorf("failed to shutdown server: %w", err)
+			return res, fmt.Errorf("failed to shutdown server: %w", err)
 		}
 		conditions.MarkFalse(s.scope.HCloudMachine,
 			infrav1.InstanceReadyCondition,
 			infrav1.InstanceTerminatedReason,
 			clusterv1.ConditionSeverityInfo,
 			"Instance has been shut down")
-		return &reconcile.Result{RequeueAfter: 30 * time.Second}, nil
+		return reconcile.Result{RequeueAfter: 30 * time.Second}, nil
 	}
 	if err := s.scope.HCloudClient.DeleteServer(ctx, server); err != nil {
 		if hcloud.IsError(err, hcloud.ErrorCodeRateLimitExceeded) {
@@ -536,7 +536,7 @@ func (s *Service) handleDeleteServerStatusRunning(ctx context.Context, server *h
 			)
 		}
 		record.Warnf(s.scope.HCloudMachine, "FailedDeleteHCloudServer", "Failed to delete HCloud server %s", s.scope.Name())
-		return nil, fmt.Errorf("failed to delete server: %w", err)
+		return res, fmt.Errorf("failed to delete server: %w", err)
 	}
 
 	record.Eventf(
@@ -545,10 +545,10 @@ func (s *Service) handleDeleteServerStatusRunning(ctx context.Context, server *h
 		"HCloud server %s deleted",
 		s.scope.Name(),
 	)
-	return nil, nil
+	return res, nil
 }
 
-func (s *Service) handleDeleteServerStatusOff(ctx context.Context, server *hcloud.Server) (*reconcile.Result, error) {
+func (s *Service) handleDeleteServerStatusOff(ctx context.Context, server *hcloud.Server) (res reconcile.Result, err error) {
 	if err := s.scope.HCloudClient.DeleteServer(ctx, server); err != nil {
 		if hcloud.IsError(err, hcloud.ErrorCodeRateLimitExceeded) {
 			conditions.MarkTrue(s.scope.HCloudMachine, infrav1.RateLimitExceeded)
@@ -558,7 +558,7 @@ func (s *Service) handleDeleteServerStatusOff(ctx context.Context, server *hclou
 			)
 		}
 		record.Warnf(s.scope.HCloudMachine, "FailedDeleteHCloudServer", "Failed to delete HCloud server %s", s.scope.Name())
-		return nil, fmt.Errorf("failed to delete server: %w", err)
+		return res, fmt.Errorf("failed to delete server: %w", err)
 	}
 
 	record.Eventf(
@@ -567,7 +567,7 @@ func (s *Service) handleDeleteServerStatusOff(ctx context.Context, server *hclou
 		"HCloud server %s deleted",
 		s.scope.Name(),
 	)
-	return nil, nil
+	return res, nil
 }
 
 func (s *Service) deleteServerOfLoadBalancer(ctx context.Context, server *hcloud.Server) error {


Test patch:
diff --git a/pkg/services/baremetal/baremetal/baremetal_test.go b/pkg/services/baremetal/baremetal/baremetal_test.go
index 53377e0c..997a5466 100644
--- a/pkg/services/baremetal/baremetal/baremetal_test.go
+++ b/pkg/services/baremetal/baremetal/baremetal_test.go
@@ -794,7 +794,7 @@ var _ = Describe("Test splitHostKey", func() {
 var _ = Describe("Test checkForRequeueError", func() {
 	type testCaseCheckForRequeueError struct {
 		err            error
-		expectedResult *reconcile.Result
+		expectedResult reconcile.Result
 		expectedErrMsg string
 	}
 
@@ -814,17 +814,17 @@ var _ = Describe("Test checkForRequeueError", func() {
 		},
 		Entry("Nil error", testCaseCheckForRequeueError{
 			err:            nil,
-			expectedResult: nil,
+			expectedResult: reconcile.Result{},
 			expectedErrMsg: "",
 		}),
 		Entry("Requeue error", testCaseCheckForRequeueError{
 			err:            &scope.RequeueAfterError{RequeueAfter: 30 * time.Second},
-			expectedResult: &reconcile.Result{Requeue: true, RequeueAfter: 30 * time.Second},
+			expectedResult: reconcile.Result{Requeue: true, RequeueAfter: 30 * time.Second},
 			expectedErrMsg: "",
 		}),
 		Entry("Other error", testCaseCheckForRequeueError{
 			err:            fmt.Errorf("other error"),
-			expectedResult: nil,
+			expectedResult: reconcile.Result{},
 			expectedErrMsg: "test message: other error",
 		}),
 	)
diff --git a/pkg/services/hcloud/server/server_test.go b/pkg/services/hcloud/server/server_test.go
index 192a88d5..c1b2053a 100644
--- a/pkg/services/hcloud/server/server_test.go
+++ b/pkg/services/hcloud/server/server_test.go
@@ -199,7 +199,7 @@ var _ = Describe("handleServerStatusOff", func() {
 		service := newTestService(hcloudMachine, client)
 		res, err := service.handleServerStatusOff(context.Background(), server)
 		Expect(err).To(Succeed())
-		Expect(res).Should(Equal(&reconcile.Result{RequeueAfter: 30 * time.Second}))
+		Expect(res).Should(Equal(reconcile.Result{RequeueAfter: 30 * time.Second}))
 		Expect(conditions.GetReason(hcloudMachine, infrav1.InstanceReadyCondition)).To(Equal(infrav1.ServerOffReason))
 		Expect(server.Status).To(Equal(hcloud.ServerStatusRunning))
 	})
@@ -209,7 +209,7 @@ var _ = Describe("handleServerStatusOff", func() {
 		service := newTestService(hcloudMachine, client)
 		res, err := service.handleServerStatusOff(context.Background(), server)
 		Expect(err).To(Succeed())
-		Expect(res).Should(Equal(&reconcile.Result{RequeueAfter: 30 * time.Second}))
+		Expect(res).Should(Equal(reconcile.Result{RequeueAfter: 30 * time.Second}))
 		Expect(server.Status).To(Equal(hcloud.ServerStatusRunning))
 		Expect(conditions.GetReason(hcloudMachine, infrav1.InstanceReadyCondition)).To(Equal(infrav1.ServerOffReason))
 	})
@@ -226,8 +226,8 @@ var _ = Describe("handleServerStatusOff", func() {
 		service := newTestService(hcloudMachine, client)
 		res, err := service.handleServerStatusOff(context.Background(), server)
 		Expect(err).To(Succeed())
-		var nilResult *reconcile.Result
-		Expect(res).Should(Equal(nilResult))
+		var emptyResult reconcile.Result
+		Expect(res).Should(Equal(emptyResult))
 		Expect(server.Status).To(Equal(hcloud.ServerStatusOff))
 		Expect(hcloudMachine.Status.FailureMessage).Should(Equal(pointer.String("reached timeout of waiting for machines that are switched off")))
 	})
@@ -237,7 +237,7 @@ var _ = Describe("handleServerStatusOff", func() {
 		service := newTestService(hcloudMachine, client)
 		res, err := service.handleServerStatusOff(context.Background(), server)
 		Expect(err).To(Succeed())
-		Expect(res).Should(Equal(&reconcile.Result{RequeueAfter: 30 * time.Second}))
+		Expect(res).Should(Equal(reconcile.Result{RequeueAfter: 30 * time.Second}))
 		Expect(conditions.GetReason(hcloudMachine, infrav1.InstanceReadyCondition)).To(Equal(infrav1.ServerOffReason))
 		Expect(server.Status).To(Equal(hcloud.ServerStatusRunning))
 	})