mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-18 12:39:54 +00:00
144 lines
5.5 KiB
Go
144 lines
5.5 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
|
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
|
"github.com/netbirdio/netbird/management/server/store"
|
|
"github.com/netbirdio/netbird/management/server/types"
|
|
)
|
|
|
|
// An update spans an old and a new state. The affected set must be the UNION of
|
|
// peers reachable before and after the change; resolving only against the final
|
|
// state drops peers that were reachable but no longer are. These tests pin the
|
|
// two paths where the old state is reachable only by the changed object's
|
|
// previous references: detaching a resource group, and re-pointing a router peer.
|
|
|
|
// TestAffectedPeers_E2E_UpdateResource_DetachGroup_RefreshesOldGroupSources:
|
|
// a resource is reachable by a source group via two destination resource groups;
|
|
// detaching one of them must still refresh that group's policy source peers, even
|
|
// though the post-update resource no longer maps to it.
|
|
func TestAffectedPeers_E2E_UpdateResource_DetachGroup_RefreshesOldGroupSources(t *testing.T) {
|
|
s := setupRouterScenario(t, true)
|
|
ctx := context.Background()
|
|
|
|
// A second resource group + a second source group/peer that reaches the
|
|
// resource only through that second group.
|
|
const detachGroupID = "rs-detach-grp"
|
|
require.NoError(t, s.manager.CreateGroup(ctx, s.accountID, userID, &types.Group{ID: detachGroupID, Name: "rs-detach"}))
|
|
|
|
const secondSourceGroupID = "rs-source-grp-2"
|
|
setupKey, err := s.manager.CreateSetupKey(ctx, s.accountID, "rs-detach-key", types.SetupKeyReusable, time.Hour, nil, 999, userID, false, false)
|
|
require.NoError(t, err)
|
|
secondSourcePeer := addPeerToAccount(t, s.manager, s.accountID, setupKey.Key)
|
|
require.NoError(t, s.manager.CreateGroup(ctx, s.accountID, userID, &types.Group{
|
|
ID: secondSourceGroupID, Name: "rs-source-2", Peers: []string{secondSourcePeer.ID},
|
|
}))
|
|
|
|
resourcesManager, _, _ := s.managers()
|
|
|
|
// Attach the resource to the detach group as well: now in [resourceGroup, detachGroup].
|
|
_, err = resourcesManager.UpdateResource(ctx, userID, &resourceTypes.NetworkResource{
|
|
ID: s.resourceID,
|
|
AccountID: s.accountID,
|
|
NetworkID: s.networkID,
|
|
Name: "rs-resource-host",
|
|
Address: "10.20.30.0/24",
|
|
GroupIDs: []string{s.resourceGroupID, detachGroupID},
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Policy granting the second source group access via the detach group.
|
|
_, err = s.manager.SavePolicy(ctx, s.accountID, userID, peerToResourcePolicyByGroup(secondSourceGroupID, detachGroupID), true)
|
|
require.NoError(t, err)
|
|
|
|
secondSrcCh := s.updateManager.CreateChannel(ctx, secondSourcePeer.ID)
|
|
t.Cleanup(func() { s.updateManager.CloseChannel(ctx, secondSourcePeer.ID) })
|
|
settleAffectedUpdates(secondSrcCh)
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
// Detaching the resource from detachGroup removes the second source's
|
|
// access; that source peer must be refreshed even though the post-update
|
|
// resource no longer maps to detachGroup.
|
|
peerShouldReceiveUpdate(t, secondSrcCh)
|
|
close(done)
|
|
}()
|
|
|
|
_, err = resourcesManager.UpdateResource(ctx, userID, &resourceTypes.NetworkResource{
|
|
ID: s.resourceID,
|
|
AccountID: s.accountID,
|
|
NetworkID: s.networkID,
|
|
Name: "rs-resource-host",
|
|
Address: "10.20.30.0/24",
|
|
GroupIDs: []string{s.resourceGroupID}, // detached detachGroup
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(peerUpdateTimeout):
|
|
t.Error("timeout: detaching a resource group did not refresh the old group's policy source peer")
|
|
}
|
|
}
|
|
|
|
// TestAffectedPeers_E2E_UpdateRouter_RepointPeer_RefreshesOldRoutingPeer:
|
|
// changing router.Peer within the same network must still refresh the OLD routing
|
|
// peer, which loses its routing role.
|
|
func TestAffectedPeers_E2E_UpdateRouter_RepointPeer_RefreshesOldRoutingPeer(t *testing.T) {
|
|
s := setupRouterScenario(t, true)
|
|
ctx := context.Background()
|
|
|
|
_, err := s.manager.SavePolicy(ctx, s.accountID, userID, peerToResourcePolicyByGroup(s.sourceGroupID, s.resourceGroupID), true)
|
|
require.NoError(t, err)
|
|
|
|
_, routersManager, _ := s.managers()
|
|
|
|
routers, err := s.manager.Store.GetNetworkRoutersByNetID(ctx, store.LockingStrengthNone, s.accountID, s.networkID)
|
|
require.NoError(t, err)
|
|
require.Len(t, routers, 1)
|
|
router := routers[0]
|
|
oldRoutingPeer := router.Peer
|
|
require.NotEmpty(t, oldRoutingPeer)
|
|
|
|
// A new peer to become the routing peer in place of the old one.
|
|
setupKey, err := s.manager.CreateSetupKey(ctx, s.accountID, "rs-newrouter-key", types.SetupKeyReusable, time.Hour, nil, 999, userID, false, false)
|
|
require.NoError(t, err)
|
|
newRoutingPeer := addPeerToAccount(t, s.manager, s.accountID, setupKey.Key)
|
|
|
|
oldCh := s.updateManager.CreateChannel(ctx, oldRoutingPeer)
|
|
t.Cleanup(func() { s.updateManager.CloseChannel(ctx, oldRoutingPeer) })
|
|
settleAffectedUpdates(oldCh)
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
// The old routing peer stops serving the resource and must be refreshed.
|
|
peerShouldReceiveUpdate(t, oldCh)
|
|
close(done)
|
|
}()
|
|
|
|
_, err = routersManager.UpdateRouter(ctx, userID, &routerTypes.NetworkRouter{
|
|
ID: router.ID,
|
|
NetworkID: s.networkID,
|
|
AccountID: s.accountID,
|
|
Peer: newRoutingPeer.ID, // repoint within the same network
|
|
Masquerade: true,
|
|
Metric: 9999,
|
|
Enabled: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(peerUpdateTimeout):
|
|
t.Error("timeout: re-pointing the router peer did not refresh the old routing peer")
|
|
}
|
|
}
|