Skip to content

fix(masque): Keep a connector's /96 when its grant record fails - #1195

Merged
privateip merged 2 commits into
mainfrom
fix/issue-978
Oct 28, 2026
Merged

privateip merged 2 commits into
mainfrom
fix/issue-978

Conversation

@privateip

@privateip privateip commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #978.

Summary

A failed grant-record write no longer strands a connector's /96 allocation.

A retry with the same connector keeps its /96, a lost reply points the operator at rotate, and retire tombstones an allocation that has no record.

The lab suite also gains a check that return traffic reaches addresses in several subdivisions of one /96 through its single aggregate route.

Test plan

grant create allocates the connector's /96 in the ledger before it writes the grant record. When that write failed, the ledger kept an entry no record pointed at. grant retire could not tombstone it, because Retire reads the record first, and a plain retry allocated a second /96. Each failure cost one of the ledger's 256 entries for good.

Create now names the connector and its /96 when the record write fails, so creating the same connector again reuses that allocation. When the record already exists after a lost reply, the error says to rotate it. Retire tombstones the allocation of a connector that has no record by finding it in the authority's ledgers, then disables the record in case a racing create wrote one. Serving and refresh refuse a retired entry, so neither the stranded /96 nor a raced record serves after the retire.

Refs #978
Add the M12 row to TestMASQUE. The probe client sends from four addresses in two /112 subdivisions of VPC 10's connector /96 to a workload on the compute node, a workload on the fixture's node and the off-fabric host through NAT. Every pair must be answered. While the connector is connected, the row requires exactly one BGPAdvertisement of the connector's route, carrying only the /96, and none for a longer prefix inside it.

edgeprobe connectip takes several -src addresses for this. The provider guide now says how return traffic reaches addresses inside the /96.

Fixes #978
@privateip privateip self-assigned this Oct 28, 2026
@privateip
privateip requested a review from a team as a code owner October 28, 2026 17:53
@privateip
privateip marked this pull request as draft October 28, 2026 17:55
@privateip privateip changed the title fix(masque): Keep connector /96s on failed grants; test M12 routing fix(masque): Keep a connector's /96 when its grant record fails Oct 28, 2026
@polylane

polylane Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Warning

Polylane could not verify the production impact of this pull request.

I checked the production account: no workload runs masque-authority (633 deployments, 1041 pods, 560 namespaces live) and only its CLI imports the changed package. Cloud Monitoring and log reads for the account returned no series.

View the full analysis →

Also considered · 3 refuted
  • Refuted · Retire's new ledger scan needs ConfigMap list rights an authority pod may not hold · main already lists ConfigMaps with the identical label selector in Ledgers.Init (ledger.go:143), so any identity that runs the authority's ledger commands already needs configmaps list.
  • Refuted · The new Retire path tombstones an allocation that is still serving traffic · The authority service retires EdgeService objects in its own path (authority.go:534) and never calls Grants.Retire, so the only caller is the explicit operator command.
  • Refuted · A new masque-authority revision deploys to production on merge · Nothing builds, publishes or runs this binary in the connected account, so no deploy follows the merge.
Analysed against 1 cloud account and 1 repository

View in Polylane Pause assessments

Polylane could not find the cloud resources this repository manages, so this review looked at the entire cloud account. Connect this repository to its resources and the next review will focus on exactly what this code deploys to.

Connect resources

Polylane analysed 09b0f3d for production impact. You can ask follow-ups by mentioning @polylane in a comment.

Rate this review: 👍 helpful · 👎 not helpful · 😕 confusing

@privateip

Copy link
Copy Markdown
Collaborator Author

Second-pass verdicts on head 09b0f3d.

pr-rereviewer: VERDICT: merge. It ran gh pr view 1195 --json title,body,isDraft,headRefOid; git rev-parse HEAD; git log --show-signature main..HEAD.

pr-conventions-reviewer: VERDICT: merge. It ran gh pr view 1195 --json title,body,commits,files,baseRefName,isDraft; git diff --stat 6b333af...09b0f3d; git diff of docs and authority files; gh pr list --state open.

First pass: pr-adversary returned merge after running go test -race ./internal/edge/masque/authority/, go vet ./tests/edgeconformance/..., and reading the Create, Retire and ledger paths. The conventions reviewer's first pass held on PR text only, and those findings were applied in the fix phase (title, body, draft state), so no code changed between passes.

CI is green on the head: every check passed, including Build, Lint, Unit Tests, Unit Tests (root), E2E Tests and Manifests. The debug-image and service-api publish jobs were skipped by design.

Auto-merge is enabled with the merge method, the only one the main ruleset allows. The ruleset requires one approving review, a code owner review and approval of the last push, so the PR waits for a human approval.

@ecv ecv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve.

The fix keeps the stranded /96 and makes it recoverable instead of reclaiming it, which is the safe choice: no path here frees a /96 a live grant could hold. Ledgers.Allocate already returns an existing entry for the same connector, so a retry reuses it, and Retire now tombstones a record-less allocation.

  • Create with the same connector and VPC/cell after a Secret create failure reuses the entry (Allocate is idempotent for a non-retired connector), so the 256 cap is not consumed again. Ledger writes still go through Ledgers.update, a conditional Update on the read's resourceVersion with conflict retry; the PR adds only a read (holding) and no new write path.
  • retireUnrecorded runs only when Disable returns NotFound, then tombstones and re-runs Disable to catch a racing Create. A Create whose record lands after that second Disable leaves an enabled record on a retired allocation. The recorded Retire has the same window (Disable, then ledger Retire), so this is no worse than before.
  • Context cancel mid-Create after Allocate leaves the entry exactly like a Secret failure; the same retry or Retire recovers it. A lost reply on the Secret create gives AlreadyExists on retry, and the error points at grant rotate.
  • Conformance changes only add the M12-aggregate-routing row and multi--src support in edgeprobe connectip. No assertion is removed, and with one or no -src the send loop behaves as before.

Follow-ups:

  • The grant Secret is named by connector alone (RecordName), but the ledger is keyed by VPC and cell. A --connector that already has a record in another VPC or cell passes Allocate in the new ledger and then hits AlreadyExists, which strands an entry there. The new message tells the user to rotate, which is wrong for that case, and Retire of the recorded connector never tombstones the other ledger's entry. Check for an existing record before allocating, or make Retire sweep every ledger holding the name.
  • A stranded entry is only recovered if the operator reads the error and retries or retires. Nothing sweeps record-less allocations, so a Create that dies (process kill) before returning the error still leaks until someone finds it. A periodic reconcile of ledger entries without records would close it.

@privateip
privateip merged commit 561c112 into main Oct 28, 2026
23 checks passed
@privateip
privateip deleted the fix/issue-978 branch October 28, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement /96 connector addressing and aggregate return routing

2 participants