What happens
On a failed evaluation (evaluateRuleForNode returns an error, e.g. a non-retryable taint patch failure), processNodeAgainstAllRules still appends a zero-value NodeEvaluation{} to the rule-status patch:
https://github.com/kubernetes-sigs/node-readiness-controller/blob/main/internal/controller/node_controller.go#L159-L206
Every required field on NodeEvaluation (nodeName, conditionResults, taintStatus, lastEvaluationTime) is omitempty, so the empty entry serializes to {} and the apiserver rejects the entire Status().Patch with a 422:
status.nodeEvaluations[0].conditionResults: Required value,
status.nodeEvaluations[0].lastEvaluationTime: Required value,
status.nodeEvaluations[0].nodeName: Required value,
status.nodeEvaluations[0].taintStatus: Required value
The patch is atomic, and the FailedNodes update (recorded via recordNodeFailure) is bundled into the same patch, so it is rejected too. Net effect: on a failed evaluation nothing is persisted to rule.status, and the only signal is a controller log line. Confirmed via envtest.
Expected
Skip the empty NodeEvaluation on the failure path and still persist the FailedNodes entry, so the failure is visible via kubectl get nodereadinessrule -o yaml.
Reproduce
Make a node's taint patch fail persistently with a non-conflict error (e.g. RBAC-deny the nodes patch) so evaluateRuleForNode returns an error: the controller logs the 422 above and failedNodes never appears for that node.
/kind bug
What happens
On a failed evaluation (
evaluateRuleForNodereturns an error, e.g. a non-retryable taint patch failure),processNodeAgainstAllRulesstill appends a zero-valueNodeEvaluation{}to the rule-status patch:https://github.com/kubernetes-sigs/node-readiness-controller/blob/main/internal/controller/node_controller.go#L159-L206
Every required field on
NodeEvaluation(nodeName,conditionResults,taintStatus,lastEvaluationTime) isomitempty, so the empty entry serializes to{}and the apiserver rejects the entireStatus().Patchwith a 422:The patch is atomic, and the
FailedNodesupdate (recorded viarecordNodeFailure) is bundled into the same patch, so it is rejected too. Net effect: on a failed evaluation nothing is persisted torule.status, and the only signal is a controller log line. Confirmed via envtest.Expected
Skip the empty
NodeEvaluationon the failure path and still persist theFailedNodesentry, so the failure is visible viakubectl get nodereadinessrule -o yaml.Reproduce
Make a node's taint patch fail persistently with a non-conflict error (e.g. RBAC-deny the
nodespatch) soevaluateRuleForNodereturns an error: the controller logs the 422 above andfailedNodesnever appears for that node./kind bug