include legacy path in golden test

This commit is contained in:
pascal
2026-08-19 16:28:35 +02:00
parent 93a0b914fa
commit 9324608d4e
18 changed files with 909 additions and 56 deletions
@@ -1,5 +1,3 @@
//go:build nmapequiv
package legacynmap
import (
@@ -1,16 +1,3 @@
//go:build nmapequiv
// Package legacynmap is a frozen copy of main's Account → NetworkMapComponents →
// NetworkMap → proto path, used only by the main-vs-branch equivalence test.
// It is build-tagged so it never compiles into production binaries, and it lives
// in its own package so it cannot reach this tree's unexported helpers — a
// divergence can therefore never be hidden by the two sides sharing code.
//
// Delete this package once the nmdata refactor is validated.
//
// Types below are aliased rather than copied because they are byte-identical
// between main and this branch. Anything that drifted is copied instead; see
// converters.go and copied_funcs.go.
package legacynmap
import (
@@ -1,5 +1,3 @@
//go:build nmapequiv
package legacynmap
import (
@@ -1,5 +1,3 @@
//go:build nmapequiv
package legacynmap
import (
@@ -1,5 +1,3 @@
//go:build nmapequiv
package legacynmap
import (
+13 -4
View File
@@ -1,7 +1,16 @@
// Package legacynmap holds a frozen copy of main's network-map computation,
// used only by the main-vs-branch proto equivalence test. All real content is
// behind the nmapequiv build tag; this file exists so the package is still valid
// for untagged builds and `go test ./...`.
// Package legacynmap is a frozen copy of main's Account → NetworkMapComponents
// → NetworkMap → proto path. It exists only to measure this tree against main:
// the proto-equivalence test runs it over a production database copy, and the
// nmaptest golden suite runs it as a third mode so every case pins all three
// shapes to one expectation.
//
// It lives in its own package so it cannot reach this tree's unexported
// helpers — a divergence can therefore never be hidden by the two sides
// sharing code. Nothing in production imports it.
//
// Types are aliased rather than copied where they are byte-identical between
// main and this branch. Anything that drifted is copied instead; see
// converters.go and copied_funcs.go.
//
// Delete this package once the nmdata refactor is validated.
package legacynmap
@@ -160,6 +160,17 @@ func checkAccount(ctx context.Context, t *testing.T, nmStore *networkmapdb.Netwo
routers := account.GetResourceRoutersMap()
groupUsers := account.GetActiveGroupUsers()
// The reverse-proxy ACLs are synthesised, never persisted. Both new paths
// derive them inside the twin; main derived them in the controller, onto
// the account, before the resource-policy map. The legacy side therefore
// runs on its own view of the policies — a shallow copy so the account the
// other two paths read stays untouched and cannot double-count them.
legacyAccount := *account
if synth := legacynmap.SynthesizeProxyPolicies(account); len(synth) > 0 {
legacyAccount.Policies = append(slices.Clone(account.Policies), synth...)
}
legacyResourcePolicies := legacyAccount.GetResourcePoliciesMap()
settings := account.Settings
if settings == nil {
settings = &types.Settings{}
@@ -200,7 +211,7 @@ func checkAccount(ctx context.Context, t *testing.T, nmStore *networkmapdb.Netwo
// LEGACY PATH — main's frozen copy.
legacyNM := legacynmap.GetPeerNetworkMapFromComponents(
account, ctx, peerID, nbdns.CustomZone{}, nil, validated, resourcePolicies, routers, nil, groupUsers,
&legacyAccount, ctx, peerID, nbdns.CustomZone{}, nil, validated, legacyResourcePolicies, routers, nil, groupUsers,
)
if legacyNM == nil {
t.Fatalf("after %d peers: account=%s peer=%s legacy NetworkMap nil, new non-nil", stats.peersChecked, account.Id, peerID)
@@ -1,5 +1,3 @@
//go:build nmapequiv
package legacynmap
import (
@@ -1,5 +1,3 @@
//go:build nmapequiv
package legacynmap
import (
@@ -1,5 +1,3 @@
//go:build nmapequiv
package legacynmap
import (
@@ -0,0 +1,150 @@
package legacynmap
import (
"fmt"
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
sharedtypes "github.com/netbirdio/netbird/shared/management/types"
)
// SynthesizeProxyPolicies is main's Account.InjectProxyPolicies, frozen. On
// main the network-map controller called it on the account before computing,
// so a comparison that starts from the account has to apply it too. It returns
// the policies instead of appending them, so the caller can measure the legacy
// path without mutating the account the other paths share.
func SynthesizeProxyPolicies(a *Account) []*Policy {
if len(a.Services) == 0 {
return nil
}
proxyPeersByCluster := a.GetProxyPeers()
if len(proxyPeersByCluster) == 0 {
return nil
}
var out []*Policy
for _, svc := range a.Services {
if svc == nil || !svc.Enabled {
continue
}
proxyPeers := proxyPeersByCluster[svc.ProxyCluster]
for _, target := range svc.Targets {
if target == nil || !target.Enabled {
continue
}
port, ok := legacyTargetPort(target)
if !ok {
continue
}
path := ""
if target.Path != nil {
path = *target.Path
}
for _, proxyPeer := range proxyPeers {
out = append(out, legacyProxyPolicy(svc, target, proxyPeer, port, path))
}
}
out = append(out, legacyPrivateServicePolicies(a, svc, proxyPeers)...)
}
return out
}
func legacyPrivateServicePolicies(a *Account, svc *service.Service, proxyPeers []*nbpeer.Peer) []*Policy {
if !svc.Private || len(svc.AccessGroups) == 0 || len(proxyPeers) == 0 {
return nil
}
sources := make([]string, 0, len(svc.AccessGroups))
for _, groupID := range svc.AccessGroups {
if _, ok := a.Groups[groupID]; ok {
sources = append(sources, groupID)
}
}
if len(sources) == 0 {
return nil
}
out := make([]*Policy, 0, len(proxyPeers))
for _, proxyPeer := range proxyPeers {
policyID := fmt.Sprintf("private-access-%s-%s", svc.ID, proxyPeer.ID)
out = append(out, &Policy{
ID: policyID,
Name: fmt.Sprintf("Private Access to %s", svc.Name),
Enabled: true,
Rules: []*PolicyRule{
{
ID: policyID,
PolicyID: policyID,
Name: fmt.Sprintf("Allow access groups to reach %s", svc.Name),
Enabled: true,
Sources: append([]string(nil), sources...),
DestinationResource: Resource{
ID: proxyPeer.ID,
Type: ResourceTypePeer,
},
Bidirectional: false,
Protocol: PolicyRuleProtocolTCP,
Action: PolicyTrafficActionAccept,
PortRanges: []RulePortRange{
{Start: 80, End: 80},
{Start: 443, End: 443},
},
},
},
})
}
return out
}
func legacyProxyPolicy(svc *service.Service, target *service.Target, proxyPeer *nbpeer.Peer, port uint16, path string) *Policy {
policyID := fmt.Sprintf("proxy-access-%s-%s-%s", svc.ID, proxyPeer.ID, path)
protocol := PolicyRuleProtocolTCP
if svc.Mode == service.ModeUDP {
protocol = sharedtypes.PolicyRuleProtocolUDP
}
return &Policy{
ID: policyID,
Name: fmt.Sprintf("Proxy Access to %s", svc.Name),
Enabled: true,
Rules: []*PolicyRule{
{
ID: policyID,
PolicyID: policyID,
Name: fmt.Sprintf("Allow access to %s", svc.Name),
Enabled: true,
SourceResource: Resource{
ID: proxyPeer.ID,
Type: ResourceTypePeer,
},
DestinationResource: Resource{
ID: target.TargetId,
Type: sharedtypes.ResourceType(target.TargetType),
},
Bidirectional: false,
Protocol: protocol,
Action: PolicyTrafficActionAccept,
PortRanges: []RulePortRange{{Start: port, End: port}},
},
},
}
}
func legacyTargetPort(target *service.Target) (uint16, bool) {
if target.Port != 0 {
return target.Port, true
}
switch target.Protocol {
case "https", "tls":
return 443, true
case "http":
return 80, true
default:
return 0, false
}
}