Skip to content

fix(rt): honor gateway expose Not in the connectivity matrix - #1913

Merged
Frostman merged 8 commits into
masterfrom
pau/rt-gw-peering-expose-not
Sep 3, 2026
Merged

fix(rt): honor gateway expose Not in the connectivity matrix#1913
Frostman merged 8 commits into
masterfrom
pau/rt-gw-peering-expose-not

Conversation

@pau-hedgehog

@pau-hedgehog pau-hedgehog commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

isVPCSubnetPresentInPeering handled expose Not entries inconsistently depending on their position in expose.IPs: an include listed first silently ignored the exclusion, an exclusion listed first returned reachCheckUnsupported. Same configuration, opposite verdicts, decided by list order.

Per the gateway API a Not is a standalone entry alongside the includes, and it narrows an already-exposed range down to individual addresses, which a per-subnet boolean cannot express. The helper now scans every entry of an expose before answering, so the verdict no longer depends on order, and it returns that expose's Not ranges in Reachability.SourceExclusions and DestExclusions. Callers that know the endpoint addresses apply them: populateConnectivityMatrix records Deny as it builds the expectation, and TestConnectivity narrows its own verdict with the server addresses it already discovered, so a Not is covered off the matrix path as well. Both directions are denied, matching the dataplane: traffic addressed to an excluded host is dropped, and the excluded host's own traffic has no return path across the peering.

An expose entry setting none of cidr, not or vpcSubnet is a malformed peering that admission rejects, so it now returns a plain error instead of reachCheckUnsupported.

Two release-test cases. Gateway Peering Expose Not peers the first two VPCs and excludes the address of a server discovered in the second. Gateway Peering Expose Not Unused is the same setup but excludes an address nothing uses, derived from the subnet and checked against every discovered endpoint IP. With one server per VPC the first case cannot distinguish the excluded host being correctly denied from the whole exposed subnet going dark; the second can.

Fixes #1906

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Gateway peering Not CIDRs are added to expose construction and connectivity-matrix expectations. Connectivity checks accept optional matrices. Unit and multi-VPC tests cover ordering, host scoping, peering scoping, and unused exclusions.

Changes

Gateway expose exclusion support

Layer / File(s) Summary
Gateway expose exclusion construction
pkg/hhfab/rt_utils.go, pkg/hhfab/rt_multi_vpc_single_subnet_suite.go
Gateway peering options accept VPC-specific exclusion CIDRs. buildExposes appends standalone Not entries. Helpers select unused IPv4 host addresses and discover subnet servers.
Connectivity matrix exclusion model
pkg/hhfab/testing.go, pkg/hhfab/rt_utils.go
Gateway Not ranges are applied to matching endpoint pairs as deny expectations. Subnet exposure matching handles Not entries independently of entry order. Connectivity checks can use a provided matrix.
Exclusion behavior validation
pkg/hhfab/expose_not_test.go, pkg/hhfab/rt_multi_vpc_single_subnet_suite.go
Tests cover expose ordering, malformed and exclusion-only entries, host selection, host scoping, unused addresses, peering scoping, and gateway peering connectivity.

Possibly related PRs

Suggested reviewers: copilot, frostman

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #1906 by making Not handling order-independent and applying exclusions to affected endpoint pairs.
Out of Scope Changes check ✅ Passed The implementation and tests remain within the linked issue objectives, including bounded host scanning and exclusion coverage.
Docstring Coverage ✅ Passed Docstring coverage is 91.21% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title clearly summarizes the primary change: honoring gateway expose Not exclusions in the connectivity matrix.
Description check ✅ Passed The description directly explains the order-dependent bug, the endpoint-level exclusion handling, and the added release tests.

Comment @coderabbitai help to get the list of available commands.

@pau-hedgehog
pau-hedgehog force-pushed the pau/rt-gw-peering-expose-not branch from f701c36 to fffd135 Compare July 29, 2026 22:36
@pau-hedgehog pau-hedgehog changed the title test(rt): add gateway peering expose Not release-test coverage fix(rt): honor gateway expose Not in the connectivity matrix Jul 30, 2026
Base automatically changed from ema/test-connectivity to master July 30, 2026 07:35
@pau-hedgehog
pau-hedgehog requested a review from Copilot July 30, 2026 09:55
@pau-hedgehog

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes order-dependent handling of GatewayPeering expose.IPs[].not exclusions in the release-test expectation model by making subnet-level reachability checks ignore Not entries and then applying the exclusions at the per-endpoint-IP level when building the connectivity matrix. It also expands the release-test infrastructure to build and use a matrix of discovered endpoints (server attachments + externals), enabling more precise modeling for scenarios like exclusions and NAT overlays.

Changes:

  • Make isVPCSubnetPresentInPeering skip Not entries (order-independent), and apply Not exclusions as per-endpoint denies in populateConnectivityMatrix.
  • Refactor VLAB/release-test flow to collect endpoints, build a ConnectivityMatrix, and run matrix-driven connectivity checks across multiple suites and NAT scenarios.
  • Add unit tests and new release-test cases covering GatewayPeering Expose Not behavior (both excluding an in-use host and excluding an unused host).

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/hhfab/vlabrunner.go Update SetupVPCs call site to ignore the new endpoints return value.
pkg/hhfab/testing.go Add reachCheckUnsupported, update SetupVPCs to return endpoints, add matrix population + gateway expose Not exclusion application.
pkg/hhfab/rt_utils.go Extend gateway peering spec helpers to emit Not entries; add helper to pick an unused host address and find servers in a subnet.
pkg/hhfab/rt_static_external.go Update test function signature to accept a connectivity matrix.
pkg/hhfab/rt_single_vpc_suite.go Thread connectivity matrix through suite tests; use matrix-driven connectivity during failover steps.
pkg/hhfab/rt_on_ready_suite.go Update test function signature to accept (unused) connectivity matrix.
pkg/hhfab/rt_no_vpc_suite.go Update test function signatures to accept (unused) connectivity matrix.
pkg/hhfab/rt_nat_tests.go Refactor NAT tests to use matrix repopulation + NAT overlays rather than ad-hoc probing.
pkg/hhfab/rt_nat_external_tests.go Refactor external NAT tests to use matrix overlays and add ping-stability checks.
pkg/hhfab/rt_multi_vpc_single_subnet_suite.go Add two new release-test cases for GatewayPeering Expose Not; migrate relevant tests to matrix-driven connectivity checks.
pkg/hhfab/rt_multi_vpc_multi_subnet_suite.go Update test function signatures and migrate subnet-filtering test to matrix-driven connectivity.
pkg/hhfab/rt_eslag_fallback.go Update test function signature to accept (unused) connectivity matrix.
pkg/hhfab/rt_base.go Change suite runner to build/pass a ConnectivityMatrix from SetupVPCs-discovered endpoints.
pkg/hhfab/matrix.go Introduce ConnectivityMatrix model, overlays, and a matrix-driven connectivity runner (ping/iperf/curl + port-forward gating).
pkg/hhfab/expose_not_test.go Add unit tests for Not ordering and matrix exclusion application semantics.
pkg/hhfab/endpoints.go Add endpoint discovery (CollectServerEndpoints) and matrix endpoint reconciliation (ReplaceServerEndpoints).
pkg/hhfab/endpoints_test.go Add unit tests for ReplaceServerEndpoints.
pkg/hhfab/cmdvlab.go Change DoVLABSetupVPCs to return discovered endpoints.
cmd/hhfab/main.go Update CLI call site to ignore DoVLABSetupVPCs’s new endpoints return value.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/hhfab/matrix.go Outdated
Comment thread pkg/hhfab/testing.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
pkg/hhfab/rt_single_vpc_suite.go (1)

195-195: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Name the unused matrix parameter _ where the body was not migrated. These functions declare matrix *ConnectivityMatrix but never reference it — they still call DoVLABTestConnectivity. Elsewhere in the same PR (rt_eslag_fallback.go, rt_no_vpc_suite.go, rt_on_ready_suite.go) the unused parameter is spelled _, which makes "not yet matrix-driven" explicit and avoids linter noise.

  • pkg/hhfab/rt_single_vpc_suite.go#L195-L195: rename to _ *ConnectivityMatrix in spineFailoverTest.
  • pkg/hhfab/rt_single_vpc_suite.go#L326-L326: rename to _ *ConnectivityMatrix in gatewayFailoverTest.
  • pkg/hhfab/rt_single_vpc_suite.go#L639-L639: rename to _ *ConnectivityMatrix in meshFailoverTest.
  • pkg/hhfab/rt_single_vpc_suite.go#L773-L773: rename to _ *ConnectivityMatrix in noRestrictionsTest.
  • pkg/hhfab/rt_single_vpc_suite.go#L791-L791: rename to _ *ConnectivityMatrix in singleVPCWithRestrictionsTest.
  • pkg/hhfab/rt_single_vpc_suite.go#L926-L926: rename to _ *ConnectivityMatrix in dnsNtpMtuTest.
  • pkg/hhfab/rt_single_vpc_suite.go#L1109-L1109: rename to _ *ConnectivityMatrix in dhcpRenewalTest.
  • pkg/hhfab/rt_single_vpc_suite.go#L1528-L1528: rename to _ *ConnectivityMatrix in roceBasicTest.
  • pkg/hhfab/rt_static_external.go#L25-L25: rename to _ *ConnectivityMatrix in staticExternalPeeringTest.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/hhfab/rt_single_vpc_suite.go` at line 195, Rename the unused matrix
parameter to _ *ConnectivityMatrix in spineFailoverTest, gatewayFailoverTest,
meshFailoverTest, noRestrictionsTest, singleVPCWithRestrictionsTest,
dnsNtpMtuTest, dhcpRenewalTest, and roceBasicTest in
pkg/hhfab/rt_single_vpc_suite.go, and staticExternalPeeringTest in
pkg/hhfab/rt_static_external.go. Preserve the existing function behavior and
calls to DoVLABTestConnectivity.
pkg/hhfab/testing.go (1)

1861-1888: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider memoizing reachability lookups per (src, dst) server name.

IsServerReachable is keyed only on server names, so every additional endpoint on the same server repeats an identical query. With multi-attachment servers this multiplies the peering/VPC lookups without changing the result. A small map[[2]string]Reachability cache would keep the loop O(servers²) instead of O(endpoints²), and it also makes the documented TODO easier to replace later.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/hhfab/testing.go` around lines 1861 - 1888, The
populateConnectivityMatrix function repeatedly calls IsServerReachable for
endpoint pairs sharing the same source and destination server names. Add a local
cache keyed by the two server names, reuse cached Reachability results before
querying, and preserve the existing unsupported-error handling and connectivity
expectations.
pkg/hhfab/matrix.go (1)

630-645: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

toolboxMutexes is write-only.

The map is built and populated but never read anywhere in the matrix path; it only serves as the "already seen" guard. Drop it and use a plain map[string]struct{} (or key off sshByServer) unless a follow-up commit intends to serialize toolbox invocations.

♻️ Proposed cleanup
 	sshByServer := map[string]*sshutil.Config{}
-	toolboxMutexes := map[string]*sync.Mutex{}
 	for _, ep := range matrix.AllEndpoints {
 		if ep.Server == nil {
 			continue
 		}
 		name := ep.Server.Name
-		if _, ok := toolboxMutexes[name]; ok {
+		if _, ok := sshByServer[name]; ok {
 			continue
 		}
 		ssh, ok := sshConfigs[name]
 		if !ok {
 			return fmt.Errorf("no ssh config for server %q referenced by matrix", name) //nolint:goerr113
 		}
 		sshByServer[name] = ssh
-		toolboxMutexes[name] = &sync.Mutex{}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/hhfab/matrix.go` around lines 630 - 645, Replace the write-only
toolboxMutexes map in the matrix endpoint loop with a plain seen-server set, or
reuse sshByServer as the duplicate guard. Preserve the existing behavior of
skipping repeated server names and returning the missing SSH configuration error
for unseen servers; do not retain mutex values unless they are actually used to
serialize toolbox invocations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/hhfab/rt_base.go`:
- Around line 417-425: Update VPCPeeringTestCtx.setupTest’s noSetup branch to
discover the attached server endpoints and build a populated connectivity matrix
instead of returning NewConnectivityMatrix() directly. Reuse
CollectServerEndpoints and BuildConnectivityMatrix (or the existing equivalent
flow), preserve WaitReady, and propagate endpoint-discovery or
matrix-construction errors.

In `@pkg/hhfab/rt_utils.go`:
- Around line 827-847: Update pickUnusedHostAddress to compute hostCount using
uint64 so a /0 prefix does not overflow and produce an invalid loop bound;
preserve the existing offset range and candidate-address construction while
ensuring the loop remains correctly bounded for all valid IPv4 prefix lengths.
- Around line 1279-1325: Update findAllServersInSubnet and the related
single-server helper to handle NativeVLAN attachments by using the base
interface name without a “.0” suffix when subnet.VLAN is zero; retain the VLAN
suffix for tagged VLANs.

---

Nitpick comments:
In `@pkg/hhfab/matrix.go`:
- Around line 630-645: Replace the write-only toolboxMutexes map in the matrix
endpoint loop with a plain seen-server set, or reuse sshByServer as the
duplicate guard. Preserve the existing behavior of skipping repeated server
names and returning the missing SSH configuration error for unseen servers; do
not retain mutex values unless they are actually used to serialize toolbox
invocations.

In `@pkg/hhfab/rt_single_vpc_suite.go`:
- Line 195: Rename the unused matrix parameter to _ *ConnectivityMatrix in
spineFailoverTest, gatewayFailoverTest, meshFailoverTest, noRestrictionsTest,
singleVPCWithRestrictionsTest, dnsNtpMtuTest, dhcpRenewalTest, and roceBasicTest
in pkg/hhfab/rt_single_vpc_suite.go, and staticExternalPeeringTest in
pkg/hhfab/rt_static_external.go. Preserve the existing function behavior and
calls to DoVLABTestConnectivity.

In `@pkg/hhfab/testing.go`:
- Around line 1861-1888: The populateConnectivityMatrix function repeatedly
calls IsServerReachable for endpoint pairs sharing the same source and
destination server names. Add a local cache keyed by the two server names, reuse
cached Reachability results before querying, and preserve the existing
unsupported-error handling and connectivity expectations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d19e5876-703f-4ab4-b6cd-6934478c0b6a

📥 Commits

Reviewing files that changed from the base of the PR and between 3e67dac and fffd135.

📒 Files selected for processing (19)
  • cmd/hhfab/main.go
  • pkg/hhfab/cmdvlab.go
  • pkg/hhfab/endpoints.go
  • pkg/hhfab/endpoints_test.go
  • pkg/hhfab/expose_not_test.go
  • pkg/hhfab/matrix.go
  • pkg/hhfab/rt_base.go
  • pkg/hhfab/rt_eslag_fallback.go
  • pkg/hhfab/rt_multi_vpc_multi_subnet_suite.go
  • pkg/hhfab/rt_multi_vpc_single_subnet_suite.go
  • pkg/hhfab/rt_nat_external_tests.go
  • pkg/hhfab/rt_nat_tests.go
  • pkg/hhfab/rt_no_vpc_suite.go
  • pkg/hhfab/rt_on_ready_suite.go
  • pkg/hhfab/rt_single_vpc_suite.go
  • pkg/hhfab/rt_static_external.go
  • pkg/hhfab/rt_utils.go
  • pkg/hhfab/testing.go
  • pkg/hhfab/vlabrunner.go

Comment thread pkg/hhfab/rt_base.go
Comment thread pkg/hhfab/rt_utils.go
Comment thread pkg/hhfab/rt_utils.go
@pau-hedgehog
pau-hedgehog force-pushed the pau/rt-gw-peering-expose-not branch from fffd135 to 8a9d082 Compare July 30, 2026 14:41
Copilot AI review requested due to automatic review settings July 30, 2026 14:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@pau-hedgehog
pau-hedgehog marked this pull request as ready for review July 30, 2026 15:13
@pau-hedgehog
pau-hedgehog requested review from a team as code owners July 30, 2026 15:13
@pau-hedgehog

Copy link
Copy Markdown
Contributor Author

The earlier reviews ran while this branch still carried pre-merge copies of #1761's commits, so the diff showed 19 files and most findings landed on code that is now merged in master. Rebased onto master, the diff is 4 files and one finding was in scope.

Fixed: pickUnusedHostAddress computed its loop bound as uint32(1) << (32 - bits), which wraps to 0 for a /0 and underflows the bound to 4294967295. The bound is now computed in uint64 while the loop counter stays uint32, so gosec's G115 stays quiet. Added TestPickUnusedHostAddress covering /0, /31, IPv6, and the used-address skip.

Out of scope, all in code merged with #1761 rather than added here: the noSetup endpoint-less matrix in rt_base.go, findAllServersInSubnet native-VLAN handling in rt_utils.go, the unused matrix parameters in rt_single_vpc_suite.go, toolboxMutexes in matrix.go, and memoizing IsServerReachable. The noSetup and native-VLAN ones look worth their own issues.

One correction: the suggestion that %s on expose.As renders as %!s(...) is wrong. PeeringEntryAs has only string fields, so %s on the slice prints [{10.0.0.0/24 } { 10.0.0.5/32}]. That line is also from #1761, not this PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/hhfab/rt_utils.go (1)

842-849: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider normalizing used lookups. Candidates come from netip.AddrFrom4, so any 4-in-6 mapped address in used (e.g. from a source that produced ::ffff:a.b.c.d) will not match and could be handed out as "unused". Applying Unmap() when building or probing the set removes that dependency on caller hygiene.

♻️ Optional hardening
 		candidate := netip.AddrFrom4(b)
-		if !used[candidate] {
+		if !used[candidate] && !used[netip.AddrFrom16(candidate.As16())] {
 			return candidate, nil
 		}

Alternatively, normalize with Unmap() at the call sites that populate used.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/hhfab/rt_utils.go` around lines 842 - 849, Normalize IPv4-mapped
addresses before checking occupancy in the candidate allocation loop around
netip.AddrFrom4 and the used lookup. Apply Unmap() when constructing or probing
the used set so mapped forms such as ::ffff:a.b.c.d are treated as equivalent to
their IPv4 candidates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/hhfab/rt_multi_vpc_single_subnet_suite.go`:
- Around line 446-473: Update the subnet selection before pickUnusedHostAddress
in the VPC2 flow to choose the first parseable IPv4 prefix by checking
prefix.Addr().Is4(), rather than always using subnetNames[0]. If VPC2 has no
IPv4 subnet, return the existing skip-style result with a clear reason; only
construct the /32 exclusion from the selected IPv4 prefix and address.

---

Nitpick comments:
In `@pkg/hhfab/rt_utils.go`:
- Around line 842-849: Normalize IPv4-mapped addresses before checking occupancy
in the candidate allocation loop around netip.AddrFrom4 and the used lookup.
Apply Unmap() when constructing or probing the used set so mapped forms such as
::ffff:a.b.c.d are treated as equivalent to their IPv4 candidates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d62e3d74-92ce-400c-93a0-ff11cbbedc88

📥 Commits

Reviewing files that changed from the base of the PR and between fffd135 and 8a9d082.

📒 Files selected for processing (4)
  • pkg/hhfab/expose_not_test.go
  • pkg/hhfab/rt_multi_vpc_single_subnet_suite.go
  • pkg/hhfab/rt_utils.go
  • pkg/hhfab/testing.go

Comment thread pkg/hhfab/rt_multi_vpc_single_subnet_suite.go
Copilot AI review requested due to automatic review settings July 30, 2026 16:06

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
pkg/hhfab/rt_multi_vpc_single_subnet_suite.go (1)

461-472: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Select an IPv4 subnet before generating the /32 exclusion.

Unmap() correctly normalizes IPv4-mapped endpoint addresses, but prefix is still derived from the lexicographically first subnet. If that subnet is IPv6, unused.String() + "/32" creates an IPv6 /32 rather than a single-host exclusion. Select a valid IPv4 prefix first, or return the existing skip-style result when none exists. This is the same unresolved issue previously reported for this test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/hhfab/rt_multi_vpc_single_subnet_suite.go` around lines 461 - 472, Update
the logic around usedIPs and the subsequent unused-host exclusion to select an
IPv4 prefix before constructing the /32 exclusion. Do not derive prefix from the
lexicographically first subnet when it may be IPv6; choose a valid IPv4 subnet,
or return the existing skip-style result if none exists, while preserving Unmap
normalization and IPv4 filtering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@pkg/hhfab/rt_multi_vpc_single_subnet_suite.go`:
- Around line 461-472: Update the logic around usedIPs and the subsequent
unused-host exclusion to select an IPv4 prefix before constructing the /32
exclusion. Do not derive prefix from the lexicographically first subnet when it
may be IPv6; choose a valid IPv4 subnet, or return the existing skip-style
result if none exists, while preserving Unmap normalization and IPv4 filtering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 18b974de-35ed-4f9e-a305-ef45b357da14

📥 Commits

Reviewing files that changed from the base of the PR and between 8a9d082 and b62f5a6.

📒 Files selected for processing (1)
  • pkg/hhfab/rt_multi_vpc_single_subnet_suite.go

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@pau-hedgehog

Copy link
Copy Markdown
Contributor Author

On the IPv4 subnet selection: pickUnusedHostAddress returns an error for any prefix where prefix.Addr().Is4() is false (rt_utils.go:828), so it cannot return an IPv6 address and no IPv6 /32 exclusion can be constructed. An IPv6 first-sorted subnet would surface as a clear error from that call, not as a broad exclusion. Leaving the selection as is.

On normalizing used: taking this one, though the mismatch was upstream of the lookup. The !ep.Server.IP.Is4() filter returns false for 4-in-6 mapped addresses, so such an endpoint was dropped before it ever entered the set, and probing both forms inside the loop would not have helped. Now unmapping when building the set, which also matches isEndpointExcluded, which already unmaps before its prefix check.

@edipascale edipascale 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.

I think it's great to add support for Not statements, but we need to make sure that the reach check stays honest, e.g. in case it is used from the kubectl plugin or with legacy test connectivity checks.

As a side note, it's easier for me to look at the PR if you answer the agent's comments directly (and ideally resolve them if it's the case) rather than as separate comments. I also think we should start doing a pass after an agent-aided PR and trim all unnecessary comments - I know I have been guilty of not doing this. Maybe there's a way to tune claude's verbosity with instructions in the CLAUDE.md and we should consider adding one, but this is a separate topic of course.

Comment thread pkg/hhfab/testing.go Outdated
Comment thread pkg/hhfab/testing.go
@pau-hedgehog
pau-hedgehog force-pushed the pau/rt-gw-peering-expose-not branch from b62f5a6 to 969d1d2 Compare July 31, 2026 08:47
Copilot AI review requested due to automatic review settings August 4, 2026 12:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Suppressed comments (1)

pkg/hhfab/expose_not_test.go:169

  • TestPickUnusedHostAddress uses t.Run(tc.prefix, ...), but there are two cases with the same prefix ("10.0.1.0/24"), so subtest names collide and failures become harder to triage (and -run filtering is ambiguous). Use a unique subtest name per case (e.g. include want/wantErr).
		t.Run(tc.prefix, func(t *testing.T) {

Copilot AI review requested due to automatic review settings August 4, 2026 15:37
@pau-hedgehog
pau-hedgehog force-pushed the pau/rt-gw-peering-expose-not branch from 270ca06 to ad951b1 Compare August 4, 2026 15:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@pau-hedgehog
pau-hedgehog force-pushed the pau/rt-gw-peering-expose-not branch 2 times, most recently from 4f19065 to f21bd75 Compare August 11, 2026 14:10
@pau-hedgehog
pau-hedgehog force-pushed the pau/rt-gw-peering-expose-not branch from f21bd75 to 9785d62 Compare August 15, 2026 10:01
@pau-hedgehog pau-hedgehog added ci:+hlab Enable hybrid VLAB tests ci:+release Enable VLAB release tests labels Sep 1, 2026
@pau-hedgehog pau-hedgehog self-assigned this Sep 1, 2026
@pau-hedgehog
pau-hedgehog force-pushed the pau/rt-gw-peering-expose-not branch from 9785d62 to a42316a Compare September 1, 2026 13:02
@pau-hedgehog
pau-hedgehog requested review from edipascale and a lite review from Copilot September 1, 2026 13:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread pkg/hhfab/rt_multi_vpc_single_subnet_suite.go Outdated
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Release Tests

  9 files   45 suites   4h 30m 9s ⏱️
 60 tests  58 ✅   2 💤 0 ❌
540 runs  264 ✅ 276 💤 0 ❌

Results for commit db228b8.

♻️ This comment has been updated with latest results.

pau-hedgehog and others added 7 commits September 1, 2026 23:46
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Pau Capdevila <pau@githedgehog.com>
Isolates whether the excluded-server test's real drops (sent 5, rcvd 0
both directions) come from host-scoped exclusion or a prefix-collapse
dataplane bug: subtracting a /32 from vpc-04's /24 fragments the
covering route, and if FRR's Ge(len) prefix-list matching can't match
the /24 against those fragments, the whole subnet could go dark
regardless of which address was excluded.

This clone excludes a /32 that is not assigned to any discovered
endpoint, so the matrix expectations stay all-Allow. PASS confirms
host-scoped exclusion and closes the open question in
ISSUE-expose-not-order-dependent.md with no dataplane bug; a repeat
server-3<->server-4 failure means prefix collapse is real and gets
filed as its own bug.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Pau Capdevila <pau@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Pau Capdevila <pau@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Pau Capdevila <pau@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Pau Capdevila <pau@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Pau Capdevila <pau@githedgehog.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Pau Capdevila <pau@githedgehog.com>
@pau-hedgehog
pau-hedgehog force-pushed the pau/rt-gw-peering-expose-not branch from 0281c3c to b4aec91 Compare September 1, 2026 21:46
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Pau Capdevila <pau@githedgehog.com>
@pau-hedgehog

Copy link
Copy Markdown
Contributor Author

I also think we should start doing a pass after an agent-aided PR and trim all unnecessary comments - I know I have been guilty of not doing this. Maybe there's a way to tune claude's verbosity with instructions in the CLAUDE.md and we should consider adding one, but this is a separate topic of course.

I have addressed this. I will take into account all your (as usual) good points from now on

@edipascale edipascale 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.

Thanks @pau-hedgehog, LGTM. I asked Claude to have a look too and it found something, but it's a nit rather than a blocker

Comment thread pkg/hhfab/rt_utils.go

// pickUnusedHostAddress returns the first host address inside prefix that is not present
// in used, skipping the network and broadcast addresses. IPv4 only.
func pickUnusedHostAddress(prefix netip.Prefix, used map[netip.Addr]bool) (netip.Addr, error) {

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.

pickUnusedHostAddress returns the subnet gateway. It returns the first host address not claimed by a discovered endpoint, and used is seeded only from matrix.AllEndpoints. VPC subnet gateways default to the first IP in the subnet (vpc_types.go:93), and servers get .2, so it always returns .1. From the CI log:

23:19:11 INF Excluding an unused address from gateway peering expose vpc=vpc-08 subnet=10.0.8.0/24 not=10.0.8.1/32

10.0.8.1 is vpc-08's anycast gateway on the leaf, not an unused address. The test still passes and still exercises the prefix-fragmentation question it was written for, so this isn't a correctness bug — but the test name, the log line and the doc comment all claim "an address nothing uses", and that's not what it picks. It also means the test's signal is entangled with however the dataplane treats the SVI address, which is a bad thing to have quietly baked into a regression test.

Fix is one line in gatewayPeeringExposeNotUnusedTest: seed usedIPs with vpc2.Spec.Subnets[subnetNames[0]].Gateway (falling back to the subnet's first address when the field is empty) before calling pickUnusedHostAddress.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@pau-hedgehog plz take care of it in a separate PR

@Frostman
Frostman merged commit 49b6a21 into master Sep 3, 2026
28 checks passed
@Frostman
Frostman deleted the pau/rt-gw-peering-expose-not branch September 3, 2026 02:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:+hlab Enable hybrid VLAB tests ci:+release Enable VLAB release tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Release-test expectation model ignores gateway-expose Not exclusions (order-dependent)

4 participants