mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-02 04:51:29 +02:00
## Describe your changes #6799 turned `AllowedIPsRefCounter` from an alias of the generic `Counter` into a dedicated peer-aware type. A change merged in parallel — `DefaultManager.ReconcilePeerAllowedIPs` (lazy-connection idle→wake reconvergence) — calls `allowedIPsRefCounter.ReapplyMatching`, which only existed on the generic `Counter`. Each PR built alone; the merged `main` did not: ``` client/internal/routemanager/manager.go: m.allowedIPsRefCounter.ReapplyMatching undefined ``` Add `ReapplyMatching(pred, apply)` to the dedicated type, matching the generic contract (keyed on the active/`Out` peer): it re-applies every prefix whose currently installed peer satisfies `pred`, skipping prefixes with no active peer (reconciled by the next Increment/Decrement). Also update `reconcile_test.go` to construct the counter via `refcounter.NewAllowedIPs` — the generic `refcounter.New` return value is no longer assignable to the dedicated type. Verified with `cd client && CGO_ENABLED=1 go build .` (the failing CI step) and the `TestReconcilePeerAllowedIPs` / refcounter / routemanager tests. ## Issue ticket number and link No public issue — fixes a `main` build break from a semantic merge conflict between #6799 and the `ReconcilePeerAllowedIPs` change. Failing run: https://github.com/netbirdio/netbird/actions/runs/30330882389/job/90185591366 ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) Internal build fix restoring a method on the AllowedIPs refcounter. No public API, CLI, config, or behavior change. ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: N/A <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/netbirdio/codesmith/netbird/pr/6935"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787814246&installation_model_id=427504&pr_number=6935&repository=netbirdio%2Fnetbird&return_to=https%3A%2F%2Fgithub.com%2Fnetbirdio%2Fnetbird%2Fpull%2F6935&signature=6a0f08ffe5ed39096a77d94e71a5947f68bad6f2b7abee5f1a4823fd44f35106"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved route reconciliation so all applicable allowed IP prefixes are reliably re-applied for the correct peer. * Prevented reconciliation from affecting routes assigned to other peers. * Improved handling of errors encountered while restoring multiple routes, providing more consistent results. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
91 lines
3.3 KiB
Go
91 lines
3.3 KiB
Go
//go:build !windows
|
|
|
|
package routemanager
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
|
|
|
"github.com/netbirdio/netbird/client/iface/device"
|
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
|
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter"
|
|
)
|
|
|
|
// reconcileWGMock is a minimal iface.WGIface that only records AddAllowedIP calls; every other
|
|
// method is an inert stub because ReconcilePeerAllowedIPs exercises none of them.
|
|
type reconcileWGMock struct {
|
|
mu sync.Mutex
|
|
adds map[string][]netip.Prefix
|
|
}
|
|
|
|
func (m *reconcileWGMock) AddAllowedIP(peerKey string, allowedIP netip.Prefix) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if m.adds == nil {
|
|
m.adds = map[string][]netip.Prefix{}
|
|
}
|
|
m.adds[peerKey] = append(m.adds[peerKey], allowedIP)
|
|
return nil
|
|
}
|
|
|
|
func (m *reconcileWGMock) added(peerKey string) []netip.Prefix {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
return m.adds[peerKey]
|
|
}
|
|
|
|
func (m *reconcileWGMock) RemoveAllowedIP(string, netip.Prefix) error { return nil }
|
|
func (m *reconcileWGMock) Name() string { return "utun-test" }
|
|
func (m *reconcileWGMock) Address() wgaddr.Address { return wgaddr.Address{} }
|
|
func (m *reconcileWGMock) ToInterface() *net.Interface { return nil }
|
|
func (m *reconcileWGMock) IsUserspaceBind() bool { return false }
|
|
func (m *reconcileWGMock) GetFilter() device.PacketFilter { return nil }
|
|
func (m *reconcileWGMock) GetDevice() *device.FilteredDevice { return nil }
|
|
func (m *reconcileWGMock) GetNet() *netstack.Net { return nil }
|
|
|
|
// TestReconcilePeerAllowedIPs verifies the declarative reconcile re-applies every routed prefix
|
|
// tracked for the peer (self-heal, independent of refcount level) and stays scoped to that peer.
|
|
func TestReconcilePeerAllowedIPs(t *testing.T) {
|
|
wg := &reconcileWGMock{}
|
|
m := &DefaultManager{wgInterface: wg}
|
|
m.allowedIPsRefCounter = refcounter.NewAllowedIPs(
|
|
func(_ netip.Prefix, peerKey string) (string, error) { return peerKey, nil },
|
|
func(netip.Prefix, string) error { return nil },
|
|
)
|
|
|
|
peerA1 := netip.MustParsePrefix("10.0.0.0/24")
|
|
peerA2 := netip.MustParsePrefix("10.1.0.0/24")
|
|
peerB1 := netip.MustParsePrefix("10.2.0.0/24")
|
|
|
|
for prefix, peer := range map[netip.Prefix]string{peerA1: "peerA", peerA2: "peerA", peerB1: "peerB"} {
|
|
_, err := m.allowedIPsRefCounter.Increment(prefix, peer)
|
|
require.NoError(t, err)
|
|
}
|
|
// Extra reference: reconcile must still re-apply the prefix even though its refcount never
|
|
// hit 0 again (the exact case the plain incremental path skips).
|
|
_, err := m.allowedIPsRefCounter.Increment(peerA1, "peerA")
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, m.ReconcilePeerAllowedIPs("peerA"))
|
|
|
|
assert.ElementsMatch(t, []netip.Prefix{peerA1, peerA2}, wg.added("peerA"),
|
|
"reconcile must re-apply all routed prefixes of the peer")
|
|
assert.Empty(t, wg.added("peerB"), "reconcile must not touch another peer's prefixes")
|
|
}
|
|
|
|
// TestReconcilePeerAllowedIPsNoCounter verifies reconcile is a safe no-op before the refcounter is
|
|
// set up.
|
|
func TestReconcilePeerAllowedIPsNoCounter(t *testing.T) {
|
|
wg := &reconcileWGMock{}
|
|
m := &DefaultManager{wgInterface: wg}
|
|
|
|
require.NoError(t, m.ReconcilePeerAllowedIPs("peerA"))
|
|
assert.Empty(t, wg.added("peerA"))
|
|
}
|