fix proto compatibility

This commit is contained in:
pascal
2026-08-24 17:42:23 +02:00
parent b731c593ae
commit 6faedeb3af
7 changed files with 511 additions and 582 deletions

View File

@@ -381,30 +381,29 @@ func (e *componentEncoder) groupPublicXid(groupID string) (string, bool) {
// today (Calculate's resource-typed rule path consults SourceResource only
// for "peer" — other types fall through to group-based lookup).
func (e *componentEncoder) resourceToProto(r nmdata.Resource) *proto.ResourceCompact {
t, ok := proto.ResourceCompactType_value[string(r.Type)]
if !ok || t == 0 || r.ID == "" {
if !types.ResourceType(r.Type).Valid() || r.ID == "" {
return nil
}
if t == int32(proto.ResourceCompactType_peer) {
out := &proto.ResourceCompact{Type: r.Type}
if r.Type == string(types.ResourceTypePeer) {
idx, ok := e.peerOrder[r.ID]
if !ok {
return nil
}
return &proto.ResourceCompact{
Type: proto.ResourceCompactType_peer,
ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: idx},
}
out.PeerIndexSet = true
out.PeerIndex = idx
return out
}
publicID, ok := e.networkIdToPublicId[r.ID]
if !ok {
return nil
}
out.Id = publicID
return &proto.ResourceCompact{
Type: proto.ResourceCompactType(t),
ResourceId: &proto.ResourceCompact_Id{Id: publicID},
}
return out
}
// postureCheckSeqs translates a slice of posture-check xids to their

View File

@@ -308,19 +308,19 @@ func TestEncodePolicy(t *testing.T) {
encoder := componentEncoder{peerOrder: map[string]uint32{"peerId": uint32(1234)}, networkIdToPublicId: map[string]string{"domain": "publicDomain", "host": "publicHost", "subnet": "publicSubnet"}}
assert.Equal(t,
encoder.resourceToProto(nmdata.Resource{Type: "peer", ID: "peerId"}),
&proto.ResourceCompact{Type: proto.ResourceCompactType_peer, ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: uint32(1234)}})
&proto.ResourceCompact{Type: "peer", PeerIndexSet: true, PeerIndex: uint32(1234)})
// verify invalid peer id results in nil
assert.Nil(t,
encoder.resourceToProto(nmdata.Resource{Type: "peer", ID: "boom"}))
assert.Equal(t,
encoder.resourceToProto(nmdata.Resource{Type: "domain", ID: "domain"}),
&proto.ResourceCompact{Type: proto.ResourceCompactType_domain, ResourceId: &proto.ResourceCompact_Id{Id: "publicDomain"}})
&proto.ResourceCompact{Type: "domain", Id: "publicDomain"})
assert.Equal(t,
encoder.resourceToProto(nmdata.Resource{Type: "host", ID: "host"}),
&proto.ResourceCompact{Type: proto.ResourceCompactType_host, ResourceId: &proto.ResourceCompact_Id{Id: "publicHost"}})
&proto.ResourceCompact{Type: "host", Id: "publicHost"})
assert.Equal(t,
encoder.resourceToProto(nmdata.Resource{Type: "subnet", ID: "subnet"}),
&proto.ResourceCompact{Type: proto.ResourceCompactType_subnet, ResourceId: &proto.ResourceCompact_Id{Id: "publicSubnet"}})
&proto.ResourceCompact{Type: "subnet", Id: "publicSubnet"})
// verify invalid resource type results in nil
assert.Nil(t,
encoder.resourceToProto(nmdata.Resource{Type: "boom", ID: "boom"}))

View File

@@ -106,7 +106,7 @@ func DecodeEnvelope(ctx context.Context, env *proto.NetworkMapEnvelope) (*types.
for _, r := range gc.Resources {
res := resourceFromProto(r, peerIDByIndex)
if res == (nmdata.Resource{}) {
log.WithContext(ctx).Warnf("skipping invalid resource in group compact: %s", r.ResourceId)
log.WithContext(ctx).Warnf("skipping invalid resource in group compact: %s", r.String())
continue
}
toret = append(toret, res)
@@ -404,30 +404,18 @@ func decodePolicyCompact(pc *proto.PolicyCompact, policyID string, peerIDByIndex
// peer reference is reconstructed from the envelope's peer index — wire
// format ships no xid for peers, so we use the synthesized peer id.
func resourceFromProto(r *proto.ResourceCompact, peerIDByIndex []string) nmdata.Resource {
if r == nil {
if r == nil || !types.ResourceType(r.Type).Valid() {
return nmdata.Resource{}
}
t, ok := proto.ResourceCompactType_name[int32(r.Type)]
if !ok || r.Type == proto.ResourceCompactType_unknown_type {
return nmdata.Resource{}
}
if r.Type == proto.ResourceCompactType_peer && int(r.GetPeerIndex()) >= len(peerIDByIndex) {
return nmdata.Resource{}
}
if r.Type == proto.ResourceCompactType_peer && int(r.GetPeerIndex()) < len(peerIDByIndex) {
return nmdata.Resource{
Type: "peer",
ID: peerIDByIndex[int(r.GetPeerIndex())],
if r.Type == string(types.ResourceTypePeer) {
if !r.PeerIndexSet || int(r.PeerIndex) >= len(peerIDByIndex) {
return nmdata.Resource{}
}
return nmdata.Resource{Type: r.Type, ID: peerIDByIndex[int(r.PeerIndex)]}
}
return nmdata.Resource{
Type: t,
ID: r.GetId(),
}
return nmdata.Resource{Type: r.Type, ID: r.Id}
}
// authorizedGroupsFromProto inverts encodeAuthorizedGroups: the wire form

View File

@@ -3,38 +3,59 @@ package networkmap
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
protobuf "google.golang.org/protobuf/proto"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
"github.com/netbirdio/netbird/shared/management/proto"
"github.com/stretchr/testify/assert"
)
func TestDecodePolicy(t *testing.T) {
assert.Equal(t,
nmdata.Resource{Type: "peer", ID: "valid-id"},
resourceFromProto(
&proto.ResourceCompact{Type: proto.ResourceCompactType_peer, ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: uint32(1)}},
&proto.ResourceCompact{Type: "peer", PeerIndexSet: true, PeerIndex: uint32(1)},
[]string{"invalid-id-0", "valid-id", "invalid-id-2"}))
// check invalid peer index returns an empty resource
assert.Equal(t,
nmdata.Resource{},
resourceFromProto(
&proto.ResourceCompact{Type: proto.ResourceCompactType_peer, ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: uint32(100)}},
&proto.ResourceCompact{Type: "peer", PeerIndexSet: true, PeerIndex: uint32(100)},
[]string{"invalid-id-0", "valid-id", "invalid-id-2"}))
assert.Equal(t,
nmdata.Resource{Type: "domain", ID: "domain"},
resourceFromProto(
&proto.ResourceCompact{Type: proto.ResourceCompactType_domain, ResourceId: &proto.ResourceCompact_Id{Id: "domain"}}, []string{}))
&proto.ResourceCompact{Type: "domain", Id: "domain"}, []string{}))
assert.Equal(t,
nmdata.Resource{Type: "host", ID: "host"},
resourceFromProto(
&proto.ResourceCompact{Type: proto.ResourceCompactType_host, ResourceId: &proto.ResourceCompact_Id{Id: "host"}}, []string{}))
&proto.ResourceCompact{Type: "host", Id: "host"}, []string{}))
assert.Equal(t,
nmdata.Resource{Type: "subnet", ID: "subnet"},
resourceFromProto(
&proto.ResourceCompact{Type: proto.ResourceCompactType_subnet, ResourceId: &proto.ResourceCompact_Id{Id: "subnet"}}, []string{}))
&proto.ResourceCompact{Type: "subnet", Id: "subnet"}, []string{}))
// an unknown resource type return an empty resource
assert.Equal(t,
nmdata.Resource{},
resourceFromProto(
&proto.ResourceCompact{Type: proto.ResourceCompactType_unknown_type, ResourceId: &proto.ResourceCompact_Id{Id: "boom"}}, []string{}))
&proto.ResourceCompact{Type: "boom", Id: "boom"}, []string{}))
}
// ResourceCompact fields 1-3 are the v0.77 wire contract. Retyping any of them
// makes peers on either side of the change silently drop policy resources, so
// the encoding is pinned here as raw bytes: field 1 "peer" (bytes), field 2
// true (varint), field 3 7 (varint).
func TestResourceCompactLegacyWireFormat(t *testing.T) {
legacy := []byte{0x0a, 0x04, 'p', 'e', 'e', 'r', 0x10, 0x01, 0x18, 0x07}
var decoded proto.ResourceCompact
require.NoError(t, protobuf.Unmarshal(legacy, &decoded))
assert.Equal(t, "peer", decoded.Type)
assert.True(t, decoded.PeerIndexSet)
assert.Equal(t, uint32(7), decoded.PeerIndex)
encoded, err := protobuf.Marshal(&proto.ResourceCompact{Type: "peer", PeerIndexSet: true, PeerIndex: 7})
require.NoError(t, err)
assert.Equal(t, legacy, encoded)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1070,23 +1070,15 @@ message PolicyCompact {
repeated string source_posture_check_ids = 13;
}
enum ResourceCompactType {
unknown_type = 0; //placeholder
peer = 1;
domain = 2;
host = 3;
subnet = 4;
}
// ResourceCompact mirrors types.Resource. Used by PolicyCompact to carry
// rule.SourceResource / rule.DestinationResource when the rule targets a
// specific resource (typically a peer) rather than groups.
message ResourceCompact {
ResourceCompactType type = 1;
oneof resource_id {
string id = 2; // for domain/host/subnet resources
uint32 peer_index = 3; // for peers
}
string type = 1;
bool peer_index_set = 2;
uint32 peer_index = 3;
reserved 4;
string id = 5; // public id for domain/host/subnet resources
}
// UserNameList is a list of local-user names — used as the value type in

View File

@@ -8,3 +8,12 @@ const (
ResourceTypeHost ResourceType = "host"
ResourceTypeSubnet ResourceType = "subnet"
)
func (t ResourceType) Valid() bool {
switch t {
case ResourceTypePeer, ResourceTypeDomain, ResourceTypeHost, ResourceTypeSubnet:
return true
default:
return false
}
}