fix(tunnel): distinguish transient Get errors from "no tunnel-targeted parent"#158
Open
kochj23 wants to merge 1 commit into
Open
fix(tunnel): distinguish transient Get errors from "no tunnel-targeted parent"#158kochj23 wants to merge 1 commit into
kochj23 wants to merge 1 commit into
Conversation
…d parent" findTunnelTargetedParentRef `continue`d past a candidate parent on ANY Get failure — folding both a definitively-missing parent (NotFound) and a transient apiserver glitch (cache resync hole, timeout) into the same all-nil "no tunnel-targeted parent" result. After jacaudi#145 added deactivation pruning at the HTTPRoute/TLSRoute `parent == nil` branch, that conflation became harmful: a transient Get error on the parent Gateway (or its CloudflareTunnel) could spuriously delete every CloudflareDNSRecord CR the source ever emitted, briefly tearing down the Cloudflare-side DNS + TXT records before re-emission. Branch on the error type at the two Get sites in findTunnelTargetedParentRef: - apierrors.IsNotFound(err) -> continue (parent definitively absent). - any other error -> return it, so the reconcile requeues. The HTTPRoute/TLSRoute reconcile paths already surface a non-nil error from findTunnelTargetedParent as `return reconcile.Result{}, err`, so returning the error here is sufficient to convert a transient failure into a requeue instead of a spurious deactivation prune. Closes jacaudi#146 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
findTunnelTargetedParentRef(internal/controller/tunnel/attach.go)continued past a candidate parent on any Get failure — a singlecontinuehandled bothapierrors.IsNotFound(parent definitively absent) and transient errors (apiserver glitch, cache resync hole, timeout). When every candidate parent fails to Get, the function returns(nil parent, nil err), which the reconcile reads as "no tunnel-targeted parent."Before #145 this was harmless. After #145 added deactivation pruning at the HTTPRoute/TLSRoute
parent == nilbranch, a transient Get error on the parent Gateway (or itsCloudflareTunnel) can spuriously delete everyCloudflareDNSRecordCR the source ever emitted, triggering a delete-and-reemit churn that briefly tears down the Cloudflare-side DNS + TXT records (issue #146).Fix
Branch on the error type at the two Get sites in
findTunnelTargetedParentRef(the Gateway Get and theCloudflareTunnelGet):apierrors.IsNotFound(err)→continue(parent definitively doesn't exist).The HTTPRoute and TLSRoute reconcile paths already surface a non-nil error from
findTunnelTargetedParentasreturn reconcile.Result{}, err, so returning the transient error here is sufficient to convert it into a requeue instead of falling into the deactivation prune. No change to the reconcile paths was required.Root cause (from the issue)
Per the issue's field-evidence note: an operator reports a CR briefly disappearing without a deliberate annotation change; logs at the time would show the
orphan-prune failed during deactivation sweepmessage absent (the prune call succeeded) followed by re-emission a moment later. The risk is observability churn (events, briefly-missing Cloudflare-side records), not data loss — but this fix removes the spurious-prune path entirely for transient errors.Tests
Extended
TestFindTunnelTargetedParentRef(fake-client unit tests,sigs.k8s.io/controller-runtime/pkg/client/fake+interceptor). Category coverage:CloudflareTunnelCR (existing).CloudflareTunnelGet (new,interceptor.Funcs).NotFoundon the Gateway still skips (returns all-nil, no error), proving the branch keys onapierrors.IsNotFound(new).findTunnelTargetedParentRefis a pure per-call read with no shared mutable state; controller-runtime serializes reconciles per object via the workqueue.go build ./...,go vet ./..., andgo test ./...all pass locally (Go 1.26). Thetest/envtestintegration suite passed in this environment; maintainer CI should of course re-run it.Closes #146
🤖 Generated with Claude Code