Skip to content

Commit 5d126b1

Browse files
authored
Merge pull request #5971 from killianmuldoon/fix/error-reporting
🐛 Remove generated names from error messages to reduce reconciliation
2 parents 5b9922a + 78eec8e commit 5d126b1

File tree

2 files changed

+94
-19
lines changed

2 files changed

+94
-19
lines changed

internal/controllers/topology/cluster/reconcile_state.go

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package cluster
1919
import (
2020
"context"
2121
"fmt"
22+
"strings"
2223

2324
"github.com/pkg/errors"
2425
corev1 "k8s.io/api/core/v1"
@@ -184,27 +185,26 @@ func (r *Reconciler) reconcileControlPlane(ctx context.Context, s *scope.Scope)
184185

185186
cpInfraRef, err := contract.ControlPlane().MachineTemplate().InfrastructureRef().Get(s.Desired.ControlPlane.Object)
186187
if err != nil {
187-
return errors.Wrapf(err, "failed to update %s", tlog.KObj{Obj: s.Desired.ControlPlane.InfrastructureMachineTemplate})
188+
return errors.Wrapf(err, "failed to reconcile %s", tlog.KObj{Obj: s.Desired.ControlPlane.InfrastructureMachineTemplate})
188189
}
189190

190191
// Create or update the MachineInfrastructureTemplate of the control plane.
191-
err = r.reconcileReferencedTemplate(ctx, reconcileReferencedTemplateInput{
192+
if err = r.reconcileReferencedTemplate(ctx, reconcileReferencedTemplateInput{
192193
cluster: s.Current.Cluster,
193194
ref: cpInfraRef,
194195
current: s.Current.ControlPlane.InfrastructureMachineTemplate,
195196
desired: s.Desired.ControlPlane.InfrastructureMachineTemplate,
196197
compatibilityChecker: check.ObjectsAreCompatible,
197198
templateNamePrefix: controlPlaneInfrastructureMachineTemplateNamePrefix(s.Current.Cluster.Name),
198199
},
199-
)
200-
if err != nil {
201-
return errors.Wrapf(err, "failed to update %s", tlog.KObj{Obj: s.Desired.ControlPlane.InfrastructureMachineTemplate})
200+
); err != nil {
201+
return err
202202
}
203203

204204
// The controlPlaneObject.Spec.machineTemplate.infrastructureRef has to be updated in the desired object
205205
err = contract.ControlPlane().MachineTemplate().InfrastructureRef().Set(s.Desired.ControlPlane.Object, refToUnstructured(cpInfraRef))
206206
if err != nil {
207-
return errors.Wrapf(err, "failed to update %s", tlog.KObj{Obj: s.Desired.ControlPlane.Object})
207+
return errors.Wrapf(err, "failed to reconcile %s", tlog.KObj{Obj: s.Desired.ControlPlane.Object})
208208
}
209209
}
210210

@@ -227,7 +227,7 @@ func (r *Reconciler) reconcileControlPlane(ctx context.Context, s *scope.Scope)
227227
},
228228
},
229229
}); err != nil {
230-
return errors.Wrapf(err, "failed to update %s", tlog.KObj{Obj: s.Desired.ControlPlane.Object})
230+
return err
231231
}
232232

233233
// If the ControlPlane has defined a current or desired MachineHealthCheck attempt to reconcile it.
@@ -242,7 +242,7 @@ func (r *Reconciler) reconcileControlPlane(ctx context.Context, s *scope.Scope)
242242

243243
// Reconcile the current and desired state of the MachineHealthCheck.
244244
if err := r.reconcileMachineHealthCheck(ctx, s.Current.ControlPlane.MachineHealthCheck, s.Desired.ControlPlane.MachineHealthCheck); err != nil {
245-
return errors.Wrapf(err, "failed to update %s", tlog.KObj{Obj: s.Desired.ControlPlane.MachineHealthCheck})
245+
return err
246246
}
247247
}
248248
return nil
@@ -403,21 +403,21 @@ func (r *Reconciler) createMachineDeployment(ctx context.Context, cluster *clust
403403
cluster: cluster,
404404
desired: md.InfrastructureMachineTemplate,
405405
}); err != nil {
406-
return errors.Wrapf(err, "failed to create %s", tlog.KObj{Obj: md.Object})
406+
return errors.Wrapf(err, "failed to create %s", md.Object.Kind)
407407
}
408408

409409
bootstrapCtx, _ := log.WithObject(md.BootstrapTemplate).Into(ctx)
410410
if err := r.reconcileReferencedTemplate(bootstrapCtx, reconcileReferencedTemplateInput{
411411
cluster: cluster,
412412
desired: md.BootstrapTemplate,
413413
}); err != nil {
414-
return errors.Wrapf(err, "failed to create %s", tlog.KObj{Obj: md.Object})
414+
return errors.Wrapf(err, "failed to create %s", md.Object.Kind)
415415
}
416416

417417
log = log.WithObject(md.Object)
418418
log.Infof(fmt.Sprintf("Creating %s", tlog.KObj{Obj: md.Object}))
419419
if err := r.Client.Create(ctx, md.Object.DeepCopy()); err != nil {
420-
return errors.Wrapf(err, "failed to create %s", tlog.KObj{Obj: md.Object})
420+
return createErrorWithoutObjectName(err, md.Object)
421421
}
422422
r.recorder.Eventf(cluster, corev1.EventTypeNormal, createEventReason, "Created %q", tlog.KObj{Obj: md.Object})
423423

@@ -429,7 +429,7 @@ func (r *Reconciler) createMachineDeployment(ctx context.Context, cluster *clust
429429
*ownerReferenceTo(md.Object),
430430
})
431431
if err := r.reconcileMachineHealthCheck(ctx, nil, md.MachineHealthCheck); err != nil {
432-
return errors.Wrapf(err, "failed to create %s", tlog.KObj{Obj: md.MachineHealthCheck})
432+
return err
433433
}
434434
}
435435
return nil
@@ -448,7 +448,7 @@ func (r *Reconciler) updateMachineDeployment(ctx context.Context, cluster *clust
448448
templateNamePrefix: infrastructureMachineTemplateNamePrefix(cluster.Name, mdTopologyName),
449449
compatibilityChecker: check.ObjectsAreCompatible,
450450
}); err != nil {
451-
return errors.Wrapf(err, "failed to update %s", tlog.KObj{Obj: currentMD.Object})
451+
return errors.Wrapf(err, "failed to reconcile %s", tlog.KObj{Obj: currentMD.Object})
452452
}
453453

454454
bootstrapCtx, _ := log.WithObject(desiredMD.BootstrapTemplate).Into(ctx)
@@ -460,13 +460,13 @@ func (r *Reconciler) updateMachineDeployment(ctx context.Context, cluster *clust
460460
templateNamePrefix: bootstrapTemplateNamePrefix(cluster.Name, mdTopologyName),
461461
compatibilityChecker: check.ObjectsAreInTheSameNamespace,
462462
}); err != nil {
463-
return errors.Wrapf(err, "failed to update %s", tlog.KObj{Obj: currentMD.Object})
463+
return errors.Wrapf(err, "failed to reconcile %s", tlog.KObj{Obj: currentMD.Object})
464464
}
465465

466466
// Patch MachineHealthCheck for the MachineDeployment.
467467
if desiredMD.MachineHealthCheck != nil || currentMD.MachineHealthCheck != nil {
468468
if err := r.reconcileMachineHealthCheck(ctx, currentMD.MachineHealthCheck, desiredMD.MachineHealthCheck); err != nil {
469-
return errors.Wrapf(err, "failed to update %s", tlog.KObj{Obj: desiredMD.MachineHealthCheck})
469+
return err
470470
}
471471
}
472472

@@ -525,7 +525,7 @@ func (r *Reconciler) deleteMachineDeployment(ctx context.Context, cluster *clust
525525
// delete MachineHealthCheck for the MachineDeployment.
526526
if md.MachineHealthCheck != nil {
527527
if err := r.reconcileMachineHealthCheck(ctx, md.MachineHealthCheck, nil); err != nil {
528-
return errors.Wrapf(err, "failed to delete %s", tlog.KObj{Obj: md.MachineHealthCheck})
528+
return err
529529
}
530530
}
531531
log.Infof("Deleting %s", tlog.KObj{Obj: md.Object})
@@ -587,7 +587,7 @@ func (r *Reconciler) reconcileReferencedObject(ctx context.Context, in reconcile
587587
return errors.Wrapf(err, "failed to create a copy of %s with the managed field annotation", tlog.KObj{Obj: in.desired})
588588
}
589589
if err := r.Client.Create(ctx, desiredWithManagedFieldAnnotation); err != nil {
590-
return errors.Wrapf(err, "failed to create %s", tlog.KObj{Obj: desiredWithManagedFieldAnnotation})
590+
return createErrorWithoutObjectName(err, desiredWithManagedFieldAnnotation)
591591
}
592592
r.recorder.Eventf(in.cluster, corev1.EventTypeNormal, createEventReason, "Created %q", tlog.KObj{Obj: in.desired})
593593
return nil
@@ -664,7 +664,7 @@ func (r *Reconciler) reconcileReferencedTemplate(ctx context.Context, in reconci
664664
return errors.Wrapf(err, "failed to create a copy of %s with the managed field annotation", tlog.KObj{Obj: in.desired})
665665
}
666666
if err := r.Client.Create(ctx, desiredWithManagedFieldAnnotation); err != nil {
667-
return errors.Wrapf(err, "failed to create %s", tlog.KObj{Obj: desiredWithManagedFieldAnnotation})
667+
return createErrorWithoutObjectName(err, desiredWithManagedFieldAnnotation)
668668
}
669669
r.recorder.Eventf(in.cluster, corev1.EventTypeNormal, createEventReason, "Created %q", tlog.KObj{Obj: in.desired})
670670
return nil
@@ -716,7 +716,7 @@ func (r *Reconciler) reconcileReferencedTemplate(ctx context.Context, in reconci
716716
return errors.Wrapf(err, "failed to create a copy of %s with the managed field annotation", tlog.KObj{Obj: in.desired})
717717
}
718718
if err := r.Client.Create(ctx, desiredWithManagedFieldAnnotation); err != nil {
719-
return errors.Wrapf(err, "failed to create %s", tlog.KObj{Obj: desiredWithManagedFieldAnnotation})
719+
return createErrorWithoutObjectName(err, desiredWithManagedFieldAnnotation)
720720
}
721721
r.recorder.Eventf(in.cluster, corev1.EventTypeNormal, createEventReason, "Created %q as a replacement for %q (template rotation)", tlog.KObj{Obj: in.desired}, in.ref.Name)
722722

@@ -727,3 +727,29 @@ func (r *Reconciler) reconcileReferencedTemplate(ctx context.Context, in reconci
727727

728728
return nil
729729
}
730+
731+
// createErrorWithoutObjectName removes the name of the object from the error message. As each new Create call involves an
732+
// object with a unique generated name each error appears to be a different error. As the errors are being surfaced in a condition
733+
// on the Cluster, the name is removed here to prevent each creation error from triggering a new reconciliation.
734+
func createErrorWithoutObjectName(err error, obj client.Object) error {
735+
var statusError *apierrors.StatusError
736+
if errors.As(err, &statusError) {
737+
if statusError.Status().Details != nil {
738+
var causes []string
739+
for _, cause := range statusError.Status().Details.Causes {
740+
causes = append(causes, fmt.Sprintf("%s: %s: %s", cause.Type, cause.Field, cause.Message))
741+
}
742+
var msg string
743+
if len(causes) > 0 {
744+
msg = fmt.Sprintf("failed to create %s.%s: %s", statusError.Status().Details.Kind, statusError.Status().Details.Group, strings.Join(causes, " "))
745+
} else {
746+
msg = fmt.Sprintf("failed to create %s.%s", statusError.Status().Details.Kind, statusError.Status().Details.Group)
747+
}
748+
// Replace the statusError message with the constructed message.
749+
statusError.ErrStatus.Message = msg
750+
return statusError
751+
}
752+
}
753+
// If this isn't a StatusError return a more generic error with the object details.
754+
return errors.Wrapf(err, "failed to create %s", tlog.KObj{Obj: obj})
755+
}

internal/controllers/topology/cluster/reconcile_state_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package cluster
1919
import (
2020
"context"
2121
"fmt"
22+
"net/http"
2223
"regexp"
2324
"testing"
2425
"time"
@@ -2081,3 +2082,51 @@ func TestReconciler_reconcileMachineHealthCheck(t *testing.T) {
20812082
})
20822083
}
20832084
}
2085+
2086+
func Test_createErrorWithoutObjectName(t *testing.T) {
2087+
originalError := &apierrors.StatusError{
2088+
ErrStatus: metav1.Status{
2089+
Status: metav1.StatusFailure,
2090+
Code: http.StatusUnprocessableEntity,
2091+
Reason: metav1.StatusReasonInvalid,
2092+
Message: "DockerMachineTemplate.infrastructure.cluster.x-k8s.io \"docker-template-one\" is invalid: spec.template.spec.preLoadImages: Invalid value: \"array\": spec.template.spec.preLoadImages in body must be of type string: \"array\"",
2093+
Details: &metav1.StatusDetails{
2094+
Group: "infrastructure.cluster.x-k8s.io",
2095+
Kind: "DockerMachineTemplate",
2096+
Name: "docker-template-one",
2097+
Causes: []metav1.StatusCause{
2098+
{
2099+
Type: "FieldValueInvalid",
2100+
Message: "Invalid value: \"array\": spec.template.spec.preLoadImages in body must be of type string: \"array\"",
2101+
Field: "spec.template.spec.preLoadImages",
2102+
},
2103+
},
2104+
},
2105+
}}
2106+
wantError := &apierrors.StatusError{
2107+
ErrStatus: metav1.Status{
2108+
Status: metav1.StatusFailure,
2109+
Code: http.StatusUnprocessableEntity,
2110+
Reason: metav1.StatusReasonInvalid,
2111+
// The only difference between the two objects should be in the Message section.
2112+
Message: "failed to create DockerMachineTemplate.infrastructure.cluster.x-k8s.io: FieldValueInvalid: spec.template.spec.preLoadImages: Invalid value: \"array\": spec.template.spec.preLoadImages in body must be of type string: \"array\"",
2113+
Details: &metav1.StatusDetails{
2114+
Group: "infrastructure.cluster.x-k8s.io",
2115+
Kind: "DockerMachineTemplate",
2116+
Name: "docker-template-one",
2117+
Causes: []metav1.StatusCause{
2118+
{
2119+
Type: "FieldValueInvalid",
2120+
Message: "Invalid value: \"array\": spec.template.spec.preLoadImages in body must be of type string: \"array\"",
2121+
Field: "spec.template.spec.preLoadImages",
2122+
},
2123+
},
2124+
},
2125+
},
2126+
}
2127+
t.Run("Transform a create error correctly", func(t *testing.T) {
2128+
g := NewWithT(t)
2129+
err := createErrorWithoutObjectName(originalError, nil)
2130+
g.Expect(err).To(Equal(wantError), cmp.Diff(err, wantError))
2131+
})
2132+
}

0 commit comments

Comments
 (0)