tailscale/tailscale
Early return in serviceHandlerForIngress skips matching Tailscale Ingresses, causing missed reconciliations
Summary
Context: The
serviceHandlerForIngressfunction incmd/k8s-operator/operator.gois a Kubernetes controller handler that determines which Ingresses need to be reconciled when a backend Service changes.Bug: The function uses
return nilinstead ofcontinuewhen encountering an Ingress with a non-matching ingress class name.Actual vs. expected: Instead of skipping non-matching Ingresses and continuing to check the rest, the function exits early, preventing any subsequent Ingresses from being processed.
Impact: Tailscale Ingresses may fail to update when their backend Services change, leading to stale routing configuration that directs traffic to wrong or unavailable backends.
Code with bug
Location: cmd/k8s-operator/operator.go
Example
Consider a cluster with three Ingresses all using the same backend Service:
ingress-nginx(class: “nginx”)ingress-tailscale-1(class: “tailscale”)ingress-tailscale-2(class: “tailscale”)
When the backend Service changes, the handler should return [ingress-tailscale-1, ingress-tailscale-2] to reconcile both Tailscale Ingresses.
Buggy behavior:
Expected behavior:
The bug is non-deterministic because Kubernetes List() ordering is not guaranteed. It only manifests when:
Multiple ingress controllers exist in the cluster
A Service is shared across Ingresses with different classes
A non-Tailscale Ingress appears before a Tailscale Ingress in the list
This explains why the bug may work correctly in simple test environments but fail unpredictably in production clusters with multiple ingress controllers.
Recommended fix
Change the early return to a continue statement: