mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-26 17:41:30 +02:00
219 lines
5.3 KiB
Go
219 lines
5.3 KiB
Go
package nmaptest
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
|
|
"github.com/netbirdio/netbird/shared/management/networkmap"
|
|
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
|
)
|
|
|
|
// LoadNetworkMapData reads a fixture holding the NetworkMapData the store
|
|
// would return for one account. Unknown fields are rejected so fixture typos
|
|
// fail loudly instead of silently testing a default.
|
|
func LoadNetworkMapData(path string) (*networkmap.NetworkMapData, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open fixture: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
dec := json.NewDecoder(f)
|
|
dec.DisallowUnknownFields()
|
|
var nmData networkmap.NetworkMapData
|
|
if err := dec.Decode(&nmData); err != nil {
|
|
return nil, fmt.Errorf("decode fixture %s: %w", path, err)
|
|
}
|
|
return &nmData, nil
|
|
}
|
|
|
|
var defaultNetworkNet = func() net.IPNet {
|
|
_, ipnet, err := net.ParseCIDR("100.64.0.0/10")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return *ipnet
|
|
}()
|
|
|
|
// applyFixtureDefaults fills the boilerplate a fixture may omit. Map-keyed
|
|
// objects inherit their key as ID, peers get a deterministic WG-shaped key
|
|
// and their ID as DNS label, PublicIDs default to the internal ID (the
|
|
// envelope encoder puts public IDs on the wire and silently degrades on
|
|
// empty ones), and a nil ValidatedPeers validates every peer — production
|
|
// fills it through the integrated validator, not the store.
|
|
func applyFixtureDefaults(nmData *networkmap.NetworkMapData) {
|
|
if nmData.Network == nil {
|
|
nmData.Network = &nmdata.Network{}
|
|
}
|
|
if nmData.Network.Identifier == "" {
|
|
nmData.Network.Identifier = "network"
|
|
}
|
|
if nmData.Network.Net.IP == nil {
|
|
nmData.Network.Net = defaultNetworkNet
|
|
}
|
|
if nmData.AccountSettings == nil {
|
|
nmData.AccountSettings = &nmdata.AccountSettingsInfo{}
|
|
}
|
|
if nmData.DNSSettings == nil {
|
|
nmData.DNSSettings = &nmdata.DNSSettings{}
|
|
}
|
|
|
|
for id, p := range nmData.Peers {
|
|
if p == nil {
|
|
continue
|
|
}
|
|
if p.ID == "" {
|
|
p.ID = id
|
|
}
|
|
if p.Key == "" {
|
|
p.Key = derivedWgKey(p.ID)
|
|
}
|
|
if p.DNSLabel == "" {
|
|
p.DNSLabel = p.ID
|
|
}
|
|
}
|
|
|
|
for id, g := range nmData.Groups {
|
|
if g == nil {
|
|
continue
|
|
}
|
|
if g.ID == "" {
|
|
g.ID = id
|
|
}
|
|
if g.Name == "" {
|
|
g.Name = g.ID
|
|
}
|
|
if g.PublicID == "" {
|
|
g.PublicID = g.ID
|
|
}
|
|
}
|
|
|
|
for _, policy := range nmData.Policies {
|
|
defaultPolicyIDs(policy)
|
|
}
|
|
resolveResourcePolicyRefs(nmData)
|
|
|
|
for _, r := range nmData.Routes {
|
|
if r != nil && r.PublicID == "" {
|
|
r.PublicID = r.ID
|
|
}
|
|
}
|
|
for _, nsg := range nmData.NameServerGroups {
|
|
if nsg != nil && nsg.PublicID == "" {
|
|
nsg.PublicID = nsg.ID
|
|
}
|
|
}
|
|
for _, res := range nmData.NetworkResources {
|
|
if res == nil {
|
|
continue
|
|
}
|
|
if res.PublicID == "" {
|
|
res.PublicID = res.ID
|
|
}
|
|
defaultXIDMapping(&nmData.NetworkXIDToPublicID, res.NetworkID)
|
|
}
|
|
for networkID, routers := range nmData.Routers {
|
|
defaultXIDMapping(&nmData.NetworkXIDToPublicID, networkID)
|
|
for _, router := range routers {
|
|
if router != nil && router.PublicID == "" {
|
|
router.PublicID = networkID
|
|
}
|
|
}
|
|
}
|
|
|
|
for id, pc := range nmData.PostureChecks {
|
|
if pc == nil {
|
|
continue
|
|
}
|
|
if pc.ID == "" {
|
|
pc.ID = id
|
|
}
|
|
defaultXIDMapping(&nmData.PostureCheckXIDToPublicID, pc.ID)
|
|
}
|
|
|
|
if nmData.ValidatedPeers == nil {
|
|
nmData.ValidatedPeers = make(map[string]struct{}, len(nmData.Peers))
|
|
for id := range nmData.Peers {
|
|
nmData.ValidatedPeers[id] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
// resolveResourcePolicyRefs lets a fixture name an account policy by ID in
|
|
// ResourcePolicies — {"ID": "pol-x"} with no rules — instead of repeating it.
|
|
// The real store puts the same policy pointer in both places, which is what
|
|
// resolving the reference reproduces.
|
|
func resolveResourcePolicyRefs(nmData *networkmap.NetworkMapData) {
|
|
byID := make(map[string]*nmdata.Policy, len(nmData.Policies))
|
|
for _, policy := range nmData.Policies {
|
|
if policy != nil && policy.ID != "" {
|
|
byID[policy.ID] = policy
|
|
}
|
|
}
|
|
|
|
for _, policies := range nmData.ResourcePolicies {
|
|
for i, policy := range policies {
|
|
if policy == nil {
|
|
continue
|
|
}
|
|
if len(policy.Rules) == 0 {
|
|
if full, ok := byID[policy.ID]; ok {
|
|
policies[i] = full
|
|
continue
|
|
}
|
|
}
|
|
defaultPolicyIDs(policy)
|
|
}
|
|
}
|
|
}
|
|
|
|
func defaultPolicyIDs(policy *nmdata.Policy) {
|
|
if policy == nil {
|
|
return
|
|
}
|
|
if policy.PublicID == "" {
|
|
policy.PublicID = policy.ID
|
|
}
|
|
for i, rule := range policy.Rules {
|
|
if rule == nil {
|
|
continue
|
|
}
|
|
if rule.PolicyID == "" {
|
|
rule.PolicyID = policy.ID
|
|
}
|
|
if rule.ID == "" {
|
|
// Production gives a rule its policy's id (management/server/policy.go:205,
|
|
// "when policy can contain multiple rules, need refactor"), so a
|
|
// single-rule policy — the only shape the product can create today —
|
|
// must be modelled that way or the wire ids come out unrealistic.
|
|
rule.ID = policy.ID
|
|
if len(policy.Rules) > 1 {
|
|
rule.ID = fmt.Sprintf("%s-rule-%d", policy.ID, i)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func defaultXIDMapping(m *map[string]string, id string) {
|
|
if id == "" {
|
|
return
|
|
}
|
|
if *m == nil {
|
|
*m = make(map[string]string)
|
|
}
|
|
if _, ok := (*m)[id]; !ok {
|
|
(*m)[id] = id
|
|
}
|
|
}
|
|
|
|
// derivedWgKey returns a deterministic base64 key of 32 bytes, valid for the
|
|
// envelope decoder's WG-key identity.
|
|
func derivedWgKey(peerID string) string {
|
|
sum := sha256.Sum256([]byte(peerID))
|
|
return base64.StdEncoding.EncodeToString(sum[:])
|
|
}
|