handle nil components consistently

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
This commit is contained in:
Dmitri Dolguikh
2026-07-10 17:12:51 +02:00
parent 8803a58f12
commit 4aa486fde8
5 changed files with 31 additions and 26 deletions

View File

@@ -601,7 +601,6 @@ func (c *Controller) GetValidatedPeerWithComponents(ctx context.Context, isRequi
routers := account.GetResourceRoutersMap()
groupIDToUserIDs := account.GetActiveGroupUsers()
components := account.GetPeerNetworkMapComponents(ctx, peer.ID, peersCustomZone, accountZones, approvedPeersMap, resourcePolicies, routers, groupIDToUserIDs)
dnsFwdPort := computeForwarderPort(maps.Values(account.Peers), network_map.DnsForwarderPortMinVersion)
return peer, components, proxyNetworkMaps[peer.ID], postureChecks, dnsFwdPort, nil

View File

@@ -25,6 +25,8 @@ import (
// computed by the client from the envelope's GroupIDToUserIDs / AllowedUserIDs
// inside Calculate(), so the SshConfig.SshEnabled bit may flip true on the
// client even though the server-side PeerConfig reports false.
//
// components parameter is expected to be !nil
func ToComponentSyncResponse(
ctx context.Context,
config *nbconfig.Config,
@@ -42,9 +44,8 @@ func ToComponentSyncResponse(
peerGroups []string,
dnsFwdPort int64,
) *proto.SyncResponse {
network := networkOrZero(components)
enableSSH := computeSSHEnabledForPeer(components, peer)
peerConfig := toPeerConfig(peer, network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH)
peerConfig := toPeerConfig(peer, components.Network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH)
includeIPv6 := peer.SupportsIPv6() && peer.IPv6.IsValid()
useSourcePrefixes := peer.SupportsSourcePrefixes()
@@ -85,15 +86,6 @@ func ToComponentSyncResponse(
return resp
}
// networkOrZero returns components.Network or a zero Network — toPeerConfig
// dereferences network.Net which would panic on nil.
func networkOrZero(c *types.NetworkMapComponents) *types.Network {
if c == nil || c.Network == nil {
return &types.Network{}
}
return c.Network
}
// toProxyPatch converts a proxy-injected *types.NetworkMap into the wire
// patch the components envelope ships alongside. Returns nil when there are
// no fragments to merge — proto3 omits a nil message field, so the receiver

View File

@@ -40,16 +40,6 @@ func (a *Account) GetPeerNetworkMapResult(
components := a.GetPeerNetworkMapComponents(
ctx, peerID, peersCustomZone, accountZones, validatedPeersMap, resourcePolicies, routers, groupIDToUserIDs,
)
// Mirror legacy graceful-degrade: GetPeerNetworkMapFromComponents
// returns &NetworkMap{Network: a.Network.Copy()} when components is
// nil. Match that floor so the receiving client always sees the
// account Network identifier, not a fully-empty envelope.
if components == nil {
components = &NetworkMapComponents{
PeerID: peerID,
Network: a.Network.Copy(),
}
}
return PeerNetworkMapResult{Components: components}
}
return PeerNetworkMapResult{
@@ -83,8 +73,8 @@ func (a *Account) GetPeerNetworkMapFromComponents(
groupIDToUserIDs,
)
if components == nil {
return &NetworkMap{Network: a.Network.Copy()}
if components.IsEmpty() {
return &NetworkMap{Network: components.Network}
}
nm := CalculateNetworkMapFromComponents(ctx, components)
@@ -117,11 +107,21 @@ func (a *Account) GetPeerNetworkMapComponents(
peer := a.Peers[peerID]
if peer == nil {
return nil
// Mirror legacy graceful-degrade: GetPeerNetworkMapFromComponents
// returns &NetworkMap{Network: a.Network.Copy()} when components is
// nil. Match that floor so the receiving client always sees the
// account Network identifier, not a fully-empty envelope.
return EmptyNetworkMapComponents(&NetworkMapComponents{
PeerID: peerID,
Network: a.Network.Copy(),
})
}
if _, ok := validatedPeersMap[peerID]; !ok {
return nil
return EmptyNetworkMapComponents(&NetworkMapComponents{
PeerID: peerID,
Network: a.Network.Copy(),
})
}
components := &NetworkMapComponents{

View File

@@ -41,6 +41,9 @@ type ResourceType = sharedtypes.ResourceType
type RouteFirewallRule = sharedtypes.RouteFirewallRule
type NetworkMapComponents = sharedtypes.NetworkMapComponents
var EmptyNetworkMapComponents = sharedtypes.EmptyNetworkMapComponents
type AccountSettingsInfo = sharedtypes.AccountSettingsInfo
type GroupCompact = sharedtypes.GroupCompact

View File

@@ -53,6 +53,8 @@ type NetworkMapComponents struct {
// Same role as NetworkXIDToSeq, used for PostureFailedPeers keys and
// policy SourcePostureChecks references.
PostureCheckXIDToPublicID map[string]string
// true when returning an empty-like map (returned instead of nil)
empty bool
}
type AccountSettingsInfo struct {
@@ -62,6 +64,11 @@ type AccountSettingsInfo struct {
PeerInactivityExpiration time.Duration
}
func EmptyNetworkMapComponents(nm *NetworkMapComponents) *NetworkMapComponents {
nm.empty = true
return nm
}
func (c *NetworkMapComponents) GetPeerInfo(peerID string) *nbpeer.Peer {
return c.Peers[peerID]
}
@@ -180,6 +187,10 @@ func (c *NetworkMapComponents) Calculate(ctx context.Context) *NetworkMap {
}
}
func (c *NetworkMapComponents) IsEmpty() bool {
return c.empty
}
func (c *NetworkMapComponents) getPeerConnectionResources(targetPeerID string) ([]*nbpeer.Peer, []*FirewallRule, map[string]map[string]struct{}, bool) {
targetPeer := c.GetPeerInfo(targetPeerID)
if targetPeer == nil {