Skip to content

Commit 664cc1d

Browse files
jtschellingclaude
andcommitted
fix(janitor): apply ExtRR review feedback (init, dispatch comment, RBAC msg)
* Drop the defensive nil-spec check in Reconcile. With failurePolicy=Fail on the validating webhook, a poison-pill object can never land on the apiserver — the check was dead code. * Combine reconcileInitialize's two passes into one. setInitialConditions already re-fetches before patching, so the rv bump from the finalizer Update doesn't matter; we save a watch round-trip per ExtRR. * Tighten the dispatch default-case comment: Released=False (terminal apply failure) also falls through here, not just Released=True awaiting the external system. * Rename the forbidden-error condition message from "forbidden to patch node ..." to "RBAC forbids patching node ..." so the operator-visible reason is unambiguous. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 067f3d5 commit 664cc1d

2 files changed

Lines changed: 14 additions & 27 deletions

File tree

janitor/pkg/controller/externalremediationrequest_controller.go

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,6 @@ func (r *ExternalRemediationRequestReconciler) Reconcile(ctx context.Context, re
121121
return ctrl.Result{}, client.IgnoreNotFound(err)
122122
}
123123

124-
// Belt-and-suspenders: the validating webhook normally guarantees these,
125-
// but a webhook outage shouldn't crashloop the controller.
126-
if extrr.Spec == nil || extrr.Spec.HealthEvent == nil || extrr.Spec.HealthEvent.NodeName == "" {
127-
slog.ErrorContext(ctx, "ExternalRemediationRequest missing required spec fields; webhook bypass?",
128-
"name", extrr.Name, "namespace", extrr.Namespace)
129-
130-
return ctrl.Result{}, nil
131-
}
132-
133124
annotations := extrr.GetAnnotations()
134125

135126
ctx, span := tracing.StartSpanWithLinkFromTraceContext(
@@ -181,9 +172,9 @@ func (r *ExternalRemediationRequestReconciler) needsInitialization(
181172
return false
182173
}
183174

184-
// reconcileInitialize adds the finalizer then seeds initial Unknown conditions
185-
// across two passes. Each step writes only what's missing, so a partial
186-
// initialization recovers cleanly on re-reconcile.
175+
// reconcileInitialize adds the finalizer and seeds initial Unknown conditions
176+
// in a single pass. setInitialConditions re-fetches before patching, so it
177+
// tolerates the rv bump from the finalizer Update.
187178
func (r *ExternalRemediationRequestReconciler) reconcileInitialize(
188179
ctx context.Context, extrrObj *nvsentinelv1.ExternalRemediationRequest,
189180
) (ctrl.Result, error) {
@@ -196,8 +187,6 @@ func (r *ExternalRemediationRequestReconciler) reconcileInitialize(
196187
}
197188

198189
slog.InfoContext(ctx, "Added cleanup finalizer to ExternalRemediationRequest", "name", extrrObj.Name)
199-
// The own-kind watch re-enqueues on this metadata Update; no requeue needed.
200-
return ctrl.Result{}, nil
201190
}
202191

203192
changed, err := r.setInitialConditions(ctx, extrrObj)
@@ -269,7 +258,8 @@ func (r *ExternalRemediationRequestReconciler) dispatch(
269258
return r.reconcileNoOpOnFalse(ctx, extrrObj)
270259

271260
default:
272-
// Released, awaiting the external system.
261+
// Steady state: Released=True awaiting the external system, or
262+
// Released=False after a terminal apply failure.
273263
return ctrl.Result{}, nil
274264
}
275265
}
@@ -341,7 +331,7 @@ func (r *ExternalRemediationRequestReconciler) reconcileApply(
341331

342332
if err := r.Patch(ctx, nodeToUpdate, client.StrategicMergeFrom(&node)); err != nil {
343333
if apierrors.IsForbidden(err) {
344-
msg := fmt.Sprintf("forbidden to patch node %q: %v", nodeName, err)
334+
msg := fmt.Sprintf("RBAC forbids patching node %q: %v", nodeName, err)
345335
slog.ErrorContext(ctx, "release taint apply forbidden by RBAC",
346336
"extrr", extrrObj.Name, "node", nodeName, "error", err)
347337

janitor/pkg/controller/externalremediationrequest_controller_test.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,14 @@ var _ = Describe("ExternalRemediationRequest Controller", func() {
203203
Expect(r.Client.Create(ctx, extrrObj)).To(Succeed())
204204

205205
key := ctrlclient.ObjectKey{Name: extrrObj.Name, Namespace: extrrObj.Namespace}
206-
// Init-only: the apply path would requeue forever waiting for the Node.
207-
for i := 0; i < 2; i++ {
208-
_, err := r.Reconcile(ctx, reconcile.Request{NamespacedName: key})
209-
Expect(err).NotTo(HaveOccurred(), "init pass %d", i+1)
210-
}
206+
// One pass to init; don't drive further or the apply path will requeue
207+
// forever waiting for the Node.
208+
_, err := r.Reconcile(ctx, reconcile.Request{NamespacedName: key})
209+
Expect(err).NotTo(HaveOccurred())
211210

212211
Expect(r.Client.Delete(ctx, extrrObj)).To(Succeed())
213212

214-
_, err := r.Reconcile(ctx, reconcile.Request{NamespacedName: key})
213+
_, err = r.Reconcile(ctx, reconcile.Request{NamespacedName: key})
215214
Expect(err).NotTo(HaveOccurred())
216215

217216
var got nvsentinelv1.ExternalRemediationRequest
@@ -774,11 +773,9 @@ var _ = Describe("ExternalRemediationRequest Controller apply path (branch 3)",
774773

775774
key := ctrlclient.ObjectKey{Name: extrrObj.Name, Namespace: extrrObj.Namespace}
776775

777-
// Drive the two init passes (finalizer + status) before hitting the apply branch.
778-
for i := 0; i < 2; i++ {
779-
_, err := r.Reconcile(ctx, reconcile.Request{NamespacedName: key})
780-
Expect(err).NotTo(HaveOccurred(), "init pass %d", i+1)
781-
}
776+
// First pass inits; second hits the apply branch and requeues.
777+
_, err := r.Reconcile(ctx, reconcile.Request{NamespacedName: key})
778+
Expect(err).NotTo(HaveOccurred())
782779

783780
result, err := r.Reconcile(ctx, reconcile.Request{NamespacedName: key})
784781
Expect(err).NotTo(HaveOccurred(), "missing Node must not propagate as a reconcile error")

0 commit comments

Comments
 (0)