mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-25 00:51:28 +02:00
added tests to cover GetNetworkMapData() call
Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
This commit is contained in:
@@ -40,6 +40,7 @@ func TestMain(m *testing.M) {
|
||||
case string(types.PostgresStoreEngine):
|
||||
engine = string(types.PostgresStoreEngine)
|
||||
pgstore, cleanup = createPGTestStore(baseData, pgData)
|
||||
pgstore.UsingTimeZone(time.UTC)
|
||||
case "", string(types.SqliteStoreEngine):
|
||||
engine = string(types.SqliteStoreEngine)
|
||||
sqlitestore, cleanup = createSqliteTestStore(baseData, sqliteData)
|
||||
@@ -67,6 +68,18 @@ func conn(t *testing.T, ctx context.Context) networkmapdb.NetworkMapDBStoreConn
|
||||
return nil
|
||||
}
|
||||
|
||||
func store(t *testing.T) networkmapdb.NetworkMapDBStore {
|
||||
t.Helper()
|
||||
switch engine {
|
||||
case string(types.PostgresStoreEngine):
|
||||
return pgstore
|
||||
case string(types.SqliteStoreEngine):
|
||||
return sqlitestore
|
||||
}
|
||||
log.Fatalf("unknown db engine kind %s", engine)
|
||||
return nil
|
||||
}
|
||||
|
||||
func execQuery(t *testing.T, ctx context.Context, q string) {
|
||||
t.Helper()
|
||||
switch engine {
|
||||
@@ -80,20 +93,13 @@ func execQuery(t *testing.T, ctx context.Context, q string) {
|
||||
}
|
||||
|
||||
// use to parse time in time.RFC3339Nano format
|
||||
// returns the time in the local time zone, as that's what being returned from sql queries
|
||||
// pgx returns time in the "local" timezone
|
||||
// sql with sqlite driver returns time in UTC timezone
|
||||
// returns the time in the UTC time zone
|
||||
func mustParseTime(t string) *time.Time {
|
||||
tt, err := time.Parse(time.RFC3339Nano, t)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if engine == string(types.SqliteStoreEngine) {
|
||||
utc := tt.UTC()
|
||||
return &utc
|
||||
}
|
||||
|
||||
local := tt.Local()
|
||||
return &local
|
||||
utc := tt.UTC()
|
||||
return &utc
|
||||
}
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
{
|
||||
"Peers": {
|
||||
"peer-id-1": {
|
||||
"ID": "peer-id-1",
|
||||
"Key": "key-1",
|
||||
"SSHKey": "ssh-key-1",
|
||||
"DNSLabel": "peer-1",
|
||||
"UserID": "user-id-1",
|
||||
"SSHEnabled": true,
|
||||
"LoginExpirationEnabled": true,
|
||||
"LastLogin": "2026-08-06T13:25:59.12999Z",
|
||||
"IP": "10.10.10.1",
|
||||
"IPv6": "fdf4:ba80:6aa5:89f1:44d7:8701:8699:4940",
|
||||
"RequiresApproval": false,
|
||||
"ExtraDNSLabels": [
|
||||
"extra-peer-1"
|
||||
],
|
||||
"Meta": {
|
||||
"WtVersion": "0.76.0",
|
||||
"GoOS": "linux",
|
||||
"OSVersion": "26.4.1",
|
||||
"KernelVersion": "6.8.0-134-generic",
|
||||
"NetworkAddresses": [
|
||||
{
|
||||
"NetIP": "fe80::8b4c:973f:a76b:3771/64"
|
||||
},
|
||||
{
|
||||
"NetIP": "192.168.16.1/20"
|
||||
}
|
||||
],
|
||||
"Files": [
|
||||
{
|
||||
"Path": "/usr/bin/netbird",
|
||||
"ProcessIsRunning": false
|
||||
}
|
||||
],
|
||||
"Capabilities": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"Flags": {
|
||||
"ServerSSHAllowed": true,
|
||||
"DisableIPv6": false
|
||||
},
|
||||
"SyncMessageVersion": 1
|
||||
},
|
||||
"ProxyMeta": {
|
||||
"Embedded": true
|
||||
},
|
||||
"Location": {
|
||||
"CountryCode": "DE",
|
||||
"CityName": "Berlin",
|
||||
"ConnectionIP": "46.201.148.187"
|
||||
}
|
||||
},
|
||||
"peer-id-2": {
|
||||
"ID": "peer-id-2",
|
||||
"Key": "key-2",
|
||||
"SSHKey": "ssh-key-2",
|
||||
"DNSLabel": "peer-2",
|
||||
"UserID": "user-id-2",
|
||||
"SSHEnabled": true,
|
||||
"LoginExpirationEnabled": true,
|
||||
"LastLogin": "2026-08-06T14:25:59.12999Z",
|
||||
"IP": "10.10.100.1",
|
||||
"IPv6": "fdf5:ba80:6aa5:89f1:44d7:8701:8699:4940",
|
||||
"RequiresApproval": false,
|
||||
"ExtraDNSLabels": [
|
||||
"extra-peer-2"
|
||||
],
|
||||
"Meta": {
|
||||
"WtVersion": "0.76.1",
|
||||
"GoOS": "linux",
|
||||
"OSVersion": "26.4.2",
|
||||
"KernelVersion": "6.8.0-135-generic",
|
||||
"NetworkAddresses": [
|
||||
{
|
||||
"NetIP": "fe81::8b4c:973f:a76b:3771/64"
|
||||
},
|
||||
{
|
||||
"NetIP": "192.168.17.1/20"
|
||||
}
|
||||
],
|
||||
"Files": [
|
||||
{
|
||||
"Path": "/usr/bin/netbird",
|
||||
"ProcessIsRunning": false
|
||||
}
|
||||
],
|
||||
"Capabilities": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"Flags": {
|
||||
"ServerSSHAllowed": true,
|
||||
"DisableIPv6": false
|
||||
},
|
||||
"SyncMessageVersion": 0
|
||||
},
|
||||
"ProxyMeta": {
|
||||
"Embedded": true
|
||||
},
|
||||
"Location": {
|
||||
"CountryCode": "DE",
|
||||
"CityName": "Berlin",
|
||||
"ConnectionIP": "46.201.149.187"
|
||||
}
|
||||
},
|
||||
"peer-id-3": {
|
||||
"ID": "peer-id-3",
|
||||
"Key": "key-3",
|
||||
"SSHKey": "ssh-key-3",
|
||||
"DNSLabel": "peer-3",
|
||||
"UserID": "user-id-3",
|
||||
"SSHEnabled": true,
|
||||
"LoginExpirationEnabled": true,
|
||||
"LastLogin": "2026-08-06T12:25:59.12999Z",
|
||||
"IP": "10.10.200.1",
|
||||
"IPv6": "fdf6:ba80:6aa5:89f1:44d7:8701:8699:4940",
|
||||
"RequiresApproval": false,
|
||||
"ExtraDNSLabels": [
|
||||
"extra-peer-3"
|
||||
],
|
||||
"Meta": {
|
||||
"WtVersion": "0.76.2",
|
||||
"GoOS": "linux",
|
||||
"OSVersion": "26.4.3",
|
||||
"KernelVersion": "6.8.0-136-generic",
|
||||
"NetworkAddresses": [
|
||||
{
|
||||
"NetIP": "fe82::8b4c:973f:a76b:3771/64"
|
||||
},
|
||||
{
|
||||
"NetIP": "192.168.18.1/20"
|
||||
}
|
||||
],
|
||||
"Files": [
|
||||
{
|
||||
"Path": "/usr/bin/netbird",
|
||||
"ProcessIsRunning": false
|
||||
}
|
||||
],
|
||||
"Capabilities": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"Flags": {
|
||||
"ServerSSHAllowed": true,
|
||||
"DisableIPv6": false
|
||||
},
|
||||
"SyncMessageVersion": 1
|
||||
},
|
||||
"ProxyMeta": {
|
||||
"Embedded": true
|
||||
},
|
||||
"Location": {
|
||||
"CountryCode": "DE",
|
||||
"CityName": "Berlin",
|
||||
"ConnectionIP": "46.201.150.187"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Groups": {
|
||||
"group-no-resources-id": {
|
||||
"ID": "group-no-resources-id",
|
||||
"Name": "group-3-name",
|
||||
"PublicID": "group-no-resources-id-public",
|
||||
"Peers": null,
|
||||
"Resources": null
|
||||
},
|
||||
"group-one-resource-id": {
|
||||
"ID": "group-one-resource-id",
|
||||
"Name": "group-1-name",
|
||||
"PublicID": "group-one-resource-id-public",
|
||||
"Peers": [
|
||||
"peer-id-1"
|
||||
],
|
||||
"Resources": [
|
||||
{
|
||||
"ID": "host-id-1",
|
||||
"Type": "host"
|
||||
}
|
||||
]
|
||||
},
|
||||
"group-two-resources-id": {
|
||||
"ID": "group-two-resources-id",
|
||||
"Name": "group-2-name",
|
||||
"PublicID": "group-two-resources-id-public",
|
||||
"Peers": [
|
||||
"peer-id-2",
|
||||
"peer-id-3"
|
||||
],
|
||||
"Resources": [
|
||||
{
|
||||
"ID": "subnet-id-1",
|
||||
"Type": "subnet"
|
||||
},
|
||||
{
|
||||
"ID": "host-id-2",
|
||||
"Type": "host"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Policies": [],
|
||||
"Routes": [],
|
||||
"NameServerGroups": [
|
||||
{
|
||||
"ID": "nsgroup-1",
|
||||
"PublicID": "nsgroup-1-public",
|
||||
"Name": "nsgroup-1",
|
||||
"Description": "nsgroup-1",
|
||||
"NameServers": [
|
||||
{
|
||||
"IP": "192.168.31.2",
|
||||
"NSType": 1,
|
||||
"Port": 53
|
||||
}
|
||||
],
|
||||
"Groups": [
|
||||
"group-one-resource-id"
|
||||
],
|
||||
"Primary": true,
|
||||
"Domains": [
|
||||
"test-1.com"
|
||||
],
|
||||
"Enabled": true,
|
||||
"SearchDomainsEnabled": false
|
||||
},
|
||||
{
|
||||
"ID": "nsgroup-2",
|
||||
"PublicID": "nsgroup-2-public",
|
||||
"Name": "nsgroup-2",
|
||||
"Description": "nsgroup-2",
|
||||
"NameServers": [
|
||||
{
|
||||
"IP": "192.168.32.3",
|
||||
"NSType": 1,
|
||||
"Port": 53
|
||||
}
|
||||
],
|
||||
"Groups": [
|
||||
"group-one-resource-id",
|
||||
"group-no-resources-id"
|
||||
],
|
||||
"Primary": true,
|
||||
"Domains": [
|
||||
"test-1.com",
|
||||
"test-2.com"
|
||||
],
|
||||
"Enabled": true,
|
||||
"SearchDomainsEnabled": false
|
||||
},
|
||||
{
|
||||
"ID": "nsgroup-3",
|
||||
"PublicID": "nsgroup-3-public",
|
||||
"Name": "",
|
||||
"Description": "",
|
||||
"NameServers": null,
|
||||
"Groups": null,
|
||||
"Primary": false,
|
||||
"Domains": null,
|
||||
"Enabled": true,
|
||||
"SearchDomainsEnabled": false
|
||||
}
|
||||
],
|
||||
"NetworkResources": [],
|
||||
"Network": {
|
||||
"Identifier": "network-1",
|
||||
"Net": {
|
||||
"IP": "100.103.0.0",
|
||||
"Mask": "//8AAA=="
|
||||
},
|
||||
"NetV6": {
|
||||
"IP": "fdde:e995:fd38:a465::",
|
||||
"Mask": "//////////8AAAAAAAAAAA=="
|
||||
},
|
||||
"Dns": "",
|
||||
"Serial": 1
|
||||
},
|
||||
"DNSSettings": {
|
||||
"DisabledManagementGroups": [
|
||||
"disabled-group-1",
|
||||
"disabled-group-2"
|
||||
]
|
||||
},
|
||||
"AccountSettings": {
|
||||
"PeerLoginExpirationEnabled": true,
|
||||
"PeerLoginExpiration": 86400000000000,
|
||||
"PeerInactivityExpirationEnabled": false,
|
||||
"PeerInactivityExpiration": 86400000000000,
|
||||
"DNSDomain": "",
|
||||
"IPv6EnabledGroups": [
|
||||
"group-one-resource-id"
|
||||
],
|
||||
"RoutingPeerDNSResolutionEnabled": false,
|
||||
"LazyConnectionEnabled": false,
|
||||
"AutoUpdateVersion": "disabled",
|
||||
"AutoUpdateAlways": false,
|
||||
"MetricsPushEnabled": false
|
||||
},
|
||||
"PostureChecks": {},
|
||||
"PostureValidation": null,
|
||||
"AllowedUserIDs": {},
|
||||
"NetworkXIDToPublicID": {},
|
||||
"PostureCheckXIDToPublicID": {},
|
||||
"ValidatedPeers": {
|
||||
"peer-id-1": {},
|
||||
"peer-id-2": {},
|
||||
"peer-id-3": {}
|
||||
},
|
||||
"ResourcePolicies": {},
|
||||
"Routers": {},
|
||||
"GroupIDToUserIDs": {},
|
||||
"DNSDomain": "",
|
||||
"ProxyTargetedDomainResourceIDs": {},
|
||||
"AppliedZoneCandidates": [
|
||||
{
|
||||
"DistributionGroups": [
|
||||
"group-one-resource-id"
|
||||
],
|
||||
"Zone": {
|
||||
"Domain": "test-1.com",
|
||||
"Records": [
|
||||
{
|
||||
"Name": "test.test-1.com",
|
||||
"Type": 1,
|
||||
"Class": "IN",
|
||||
"TTL": 1800,
|
||||
"RData": "1.1.1.1"
|
||||
},
|
||||
{
|
||||
"Name": "test2.test-1.com",
|
||||
"Type": 1,
|
||||
"Class": "IN",
|
||||
"TTL": 1800,
|
||||
"RData": "1.1.1.2"
|
||||
},
|
||||
{
|
||||
"Name": "test3.test-1.com",
|
||||
"Type": 5,
|
||||
"Class": "IN",
|
||||
"TTL": 1800,
|
||||
"RData": "test4.test-1.com."
|
||||
}
|
||||
],
|
||||
"SearchDomainDisabled": false,
|
||||
"NonAuthoritative": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"DistributionGroups": [
|
||||
"group-two-resources-id"
|
||||
],
|
||||
"Zone": {
|
||||
"Domain": "test-2.com",
|
||||
"Records": [
|
||||
{
|
||||
"Name": "test2.test-2.com",
|
||||
"Type": 5,
|
||||
"Class": "IN",
|
||||
"TTL": 1800,
|
||||
"RData": "test3.test-2.com."
|
||||
}
|
||||
],
|
||||
"SearchDomainDisabled": true,
|
||||
"NonAuthoritative": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"PrivateServiceCandidates": null
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/management/server/integrations/extra_settings"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
//go:embed network_map_data_golden.json
|
||||
var goldenNMap string
|
||||
|
||||
const EnvUpdateGoldenData = "NMAP_UPDATE_GOLDEN_DATA"
|
||||
|
||||
func TestGetNetworkMapData(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
storeImpl := networkmapdb.NetworkMapDBStoreImpl{
|
||||
Store: store(t),
|
||||
ExtraSettingsManager: &extraSettingsManagerForTesting{},
|
||||
IntegratedPeerValidator: &peerValidatorForTesting{},
|
||||
}
|
||||
|
||||
nmap, err := storeImpl.GetNetworkMapData(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
serializedNMap, err := json.MarshalIndent(nmap, "", " ")
|
||||
assert.NoError(t, err)
|
||||
|
||||
if _, ok := os.LookupEnv(EnvUpdateGoldenData); ok {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
tosavepath := filepath.Join(filepath.Dir(filename), "network_map_data_golden.json")
|
||||
err = os.WriteFile(tosavepath, serializedNMap, 0644)
|
||||
assert.NoError(t, err)
|
||||
goldenNMap = string(serializedNMap)
|
||||
}
|
||||
assert.Equal(t, goldenNMap, string(serializedNMap))
|
||||
}
|
||||
|
||||
// need these calls for the test
|
||||
func (*extraSettingsManagerForTesting) GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error) {
|
||||
return &types.ExtraSettings{}, nil
|
||||
}
|
||||
func (*peerValidatorForTesting) GetValidatedPeers(ctx context.Context, accountID string, groups []*nmdata.Group, peers []*nmdata.Peer, extraSettings *types.ExtraSettings) (map[string]struct{}, error) {
|
||||
return map[string]struct{}{
|
||||
"peer-id-1": {},
|
||||
"peer-id-2": {},
|
||||
"peer-id-3": {},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type extraSettingsManagerForTesting struct{}
|
||||
|
||||
func (*extraSettingsManagerForTesting) GetExtraSettingsManager() extra_settings.Manager { return nil }
|
||||
func (*extraSettingsManagerForTesting) GetSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (*extraSettingsManagerForTesting) UpdateExtraSettings(ctx context.Context, accountID, userID string, extraSettings *types.ExtraSettings) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (*extraSettingsManagerForTesting) GetEffectiveNetworkRanges(ctx context.Context, accountID string) (v4, v6 netip.Prefix, err error) {
|
||||
return netip.Prefix{}, netip.Prefix{}, nil
|
||||
}
|
||||
|
||||
type peerValidatorForTesting struct{}
|
||||
|
||||
func (*peerValidatorForTesting) ValidateExtraSettings(ctx context.Context, newExtraSettings *types.ExtraSettings, oldExtraSettings *types.ExtraSettings, userID string, accountID string) error {
|
||||
return nil
|
||||
}
|
||||
func (*peerValidatorForTesting) ValidatePeer(ctx context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *types.ExtraSettings) (*nbpeer.Peer, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
func (*peerValidatorForTesting) PreparePeer(ctx context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *types.ExtraSettings, temporary bool) *nbpeer.Peer {
|
||||
return nil
|
||||
}
|
||||
func (*peerValidatorForTesting) IsNotValidPeer(ctx context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *types.ExtraSettings) (bool, bool, error) {
|
||||
return false, false, nil
|
||||
}
|
||||
func (*peerValidatorForTesting) GetInvalidPeers(ctx context.Context, accountID string, extraSettings *types.ExtraSettings) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (*peerValidatorForTesting) PeerDeleted(ctx context.Context, accountID, peerID string, extraSettings *types.ExtraSettings) error {
|
||||
return nil
|
||||
}
|
||||
func (*peerValidatorForTesting) SetPeerInvalidationListener(fn func(accountID string, peerIDs []string)) {
|
||||
}
|
||||
func (*peerValidatorForTesting) Stop(ctx context.Context) {}
|
||||
func (*peerValidatorForTesting) ValidateFlowResponse(ctx context.Context, peerKey string, flowResponse *proto.PKCEAuthorizationFlow) *proto.PKCEAuthorizationFlow {
|
||||
return nil
|
||||
}
|
||||
@@ -55,6 +55,15 @@ func (Integrationtest) NmapDbSqlite(gotestflags *string) error {
|
||||
return sh.RunWithV(map[string]string{"NETBIRD_STORE_ENGINE": "sqlite"}, "go", cli...)
|
||||
}
|
||||
|
||||
func (Integrationtest) RegenerateNmapGoldenData(gotestflags *string) error {
|
||||
cli := defaultcli
|
||||
if gotestflags != nil {
|
||||
cli = append(cli, strings.Split(*gotestflags, " ")...)
|
||||
}
|
||||
cli = append(cli, "./integration_tests/management/network_map_db/...")
|
||||
return sh.RunWithV(map[string]string{"NMAP_UPDATE_GOLDEN_DATA": "true", "NETBIRD_STORE_ENGINE": "sqlite"}, "go", cli...)
|
||||
}
|
||||
|
||||
func (Integrationtest) Api(gotestflags *string) error {
|
||||
cli := defaultcli
|
||||
if gotestflags != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package networkmapdb
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
@@ -88,35 +89,13 @@ func (s *NetworkMapDBStoreImpl) GetNetworkMapData(ctx context.Context, accountId
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get proxy targeted domain resources: %w", err))
|
||||
}
|
||||
|
||||
resourcePolicies := make(map[string][]*nmdata.Policy)
|
||||
for _, resource := range networkResources {
|
||||
if !resource.Enabled {
|
||||
continue
|
||||
}
|
||||
networkResourceGroups := resourceToGroupIdx[resource.ID]
|
||||
for _, policy := range policies {
|
||||
if !policy.Enabled {
|
||||
continue
|
||||
}
|
||||
if _, ok := policyToDestinationResourceIdx[policy.ID][resource.ID]; ok {
|
||||
resourcePolicies[resource.ID] = append(resourcePolicies[resource.ID], &policy) // TODO (dmitri) maybe use public id?
|
||||
continue
|
||||
}
|
||||
if groupIds, ok := policyToDestinationGroupIdx[policy.ID]; ok {
|
||||
for networkResourceGroup := range networkResourceGroups {
|
||||
if _, ok := groupIds[networkResourceGroup]; ok {
|
||||
resourcePolicies[resource.ID] = append(resourcePolicies[resource.ID], &policy)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = tx.CommitTx(ctx); err != nil {
|
||||
log.WithContext(ctx).Warnf("failed to commit network map read transaction: %v", err)
|
||||
}
|
||||
|
||||
resourcePolicies := buildResourcePolicies(
|
||||
networkResources, policies, resourceToGroupIdx, policyToDestinationResourceIdx, policyToDestinationGroupIdx)
|
||||
|
||||
toret := networkmap.NetworkMapData{
|
||||
AccountSettings: &acctSettings,
|
||||
DNSSettings: &dnsSettings,
|
||||
@@ -220,18 +199,16 @@ func buildPrivateServiceCandidates(svcs []Service, domains []Domain, proxyPeersB
|
||||
continue
|
||||
}
|
||||
|
||||
// this is implied when domainZone != "", but for maintainability's sake the check is explicit
|
||||
// TODO (dmitri) make this an invariant
|
||||
if svc.Domain.String == "" {
|
||||
continue
|
||||
}
|
||||
var records []nmdata.SimpleRecord
|
||||
for _, proxyPeer := range proxyPeersByCluster[svc.ProxyCluster.String] {
|
||||
if !proxyPeer.IP.IsValid() {
|
||||
continue
|
||||
if record, ok := recordForProxyPeer(svc.Domain.String, proxyPeer.IP); ok {
|
||||
records = append(records, record)
|
||||
}
|
||||
records = append(records, nmdata.SimpleRecord{
|
||||
Name: dns.Fqdn(svc.Domain.String),
|
||||
Type: int(dns.TypeA),
|
||||
Class: "IN",
|
||||
TTL: 5,
|
||||
RData: proxyPeer.IP.String(),
|
||||
})
|
||||
}
|
||||
if len(records) == 0 {
|
||||
continue
|
||||
@@ -250,3 +227,51 @@ func buildPrivateServiceCandidates(svcs []Service, domains []Domain, proxyPeersB
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func recordForProxyPeer(fqdn string, ip netip.Addr) (nmdata.SimpleRecord, bool) {
|
||||
if !ip.IsValid() {
|
||||
return nmdata.SimpleRecord{}, false
|
||||
}
|
||||
|
||||
return nmdata.SimpleRecord{
|
||||
Name: dns.Fqdn(fqdn),
|
||||
Type: int(dns.TypeA),
|
||||
Class: "IN",
|
||||
TTL: 5,
|
||||
RData: ip.String(),
|
||||
}, true
|
||||
}
|
||||
|
||||
func buildResourcePolicies(networkResources []nmdata.NetworkResource,
|
||||
policies []nmdata.Policy,
|
||||
resourceToGroupIdx map[string]map[string]any,
|
||||
policyToDestinationResourceIdx map[string]map[string]any,
|
||||
policyToDestinationGroupIdx map[string]map[string]any) map[string][]*nmdata.Policy {
|
||||
|
||||
resourcePolicies := make(map[string][]*nmdata.Policy)
|
||||
for _, resource := range networkResources {
|
||||
if !resource.Enabled {
|
||||
continue
|
||||
}
|
||||
networkResourceGroups := resourceToGroupIdx[resource.ID]
|
||||
for _, policy := range policies {
|
||||
if !policy.Enabled {
|
||||
continue
|
||||
}
|
||||
if _, ok := policyToDestinationResourceIdx[policy.ID][resource.ID]; ok {
|
||||
resourcePolicies[resource.ID] = append(resourcePolicies[resource.ID], &policy) // TODO (dmitri) maybe use public id?
|
||||
continue
|
||||
}
|
||||
if groupIds, ok := policyToDestinationGroupIdx[policy.ID]; ok {
|
||||
for networkResourceGroup := range networkResourceGroups {
|
||||
if _, ok := groupIds[networkResourceGroup]; ok {
|
||||
resourcePolicies[resource.ID] = append(resourcePolicies[resource.ID], &policy)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourcePolicies
|
||||
}
|
||||
|
||||
400
management/internals/network_map_db/network_map_data_test.go
Normal file
400
management/internals/network_map_db/network_map_data_test.go
Normal file
@@ -0,0 +1,400 @@
|
||||
package networkmapdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDomainFromSuffix(t *testing.T) {
|
||||
assert.False(t, domainFromSuffix("test", ""))
|
||||
assert.False(t, domainFromSuffix("test", "suffix")) // domain != suffix
|
||||
assert.True(t, domainFromSuffix("test", "test")) // domain == suffix
|
||||
assert.False(t, domainFromSuffix("test.anothersuffix", "suffix")) // domain doesn't contain suffix
|
||||
assert.True(t, domainFromSuffix("test.suffix", "suffix")) // domain contains suffix
|
||||
}
|
||||
|
||||
func TestServiceDomainZone(t *testing.T) {
|
||||
// shortcut -- service's domain is a subomain of proxy cluster
|
||||
assert.Equal(t, "cluster",
|
||||
serviceDomainZone(
|
||||
Service{
|
||||
Domain: sql.NullString{Valid: true, String: "test.cluster"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
[]Domain{}))
|
||||
assert.Equal(t, "a.b", serviceDomainZone(
|
||||
Service{
|
||||
Domain: sql.NullString{Valid: true, String: "test.a.b"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
[]Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "a-cluster"}},
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "b"}},
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}}, // should return this domain, as it's the longest match
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "b-cluster"}},
|
||||
}))
|
||||
// service and domain clusters don't match
|
||||
assert.Empty(t, serviceDomainZone(
|
||||
Service{
|
||||
Domain: sql.NullString{Valid: true, String: "test.a.b"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "c-cluster"}},
|
||||
[]Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}},
|
||||
}))
|
||||
// service domain is empty
|
||||
assert.Empty(t, serviceDomainZone(
|
||||
Service{
|
||||
Domain: sql.NullString{Valid: false, String: ""},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
[]Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}},
|
||||
}))
|
||||
}
|
||||
|
||||
func TestRecordForProxyPeer(t *testing.T) {
|
||||
record, ok := recordForProxyPeer("test.cluster", netip.MustParseAddr("127.0.0.1"))
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, nmdata.SimpleRecord{
|
||||
Name: "test.cluster.",
|
||||
Type: 1,
|
||||
Class: "IN",
|
||||
TTL: 5,
|
||||
RData: "127.0.0.1",
|
||||
}, record)
|
||||
|
||||
// invalid address
|
||||
var addr netip.Addr
|
||||
record, ok = recordForProxyPeer("test.cluster", addr)
|
||||
assert.False(t, ok)
|
||||
|
||||
}
|
||||
|
||||
var empty []networkmap.PrivateServiceCandidate
|
||||
|
||||
// empty proxyPeersByCluster results in empty []PrivateServiceCandidates
|
||||
func TestBuildPrivateServiceCandidates_EmptyProxyPeers(t *testing.T) {
|
||||
assert.Equal(t, empty, buildPrivateServiceCandidates([]Service{}, []Domain{}, nil))
|
||||
}
|
||||
|
||||
// disabled service returns an empty result
|
||||
func TestBuildPrivateServiceCandidates_DisabledService(t *testing.T) {
|
||||
assert.Equal(t, empty,
|
||||
buildPrivateServiceCandidates([]Service{
|
||||
{Enabled: sql.NullBool{Valid: true, Bool: false},
|
||||
Private: sql.NullBool{Valid: true, Bool: true},
|
||||
AccessGroups: []string{"group-1", "group-2"},
|
||||
Domain: sql.NullString{Valid: true, String: "test.a.b"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
}, []Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}},
|
||||
},
|
||||
map[string][]*nmdata.Peer{
|
||||
"cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.1")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.2")}},
|
||||
"a-cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.3")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.4")}},
|
||||
}))
|
||||
}
|
||||
|
||||
// non-private service results in empty []PrivateServiceCandidates
|
||||
func TestBuildPrivateServiceCandidates_PublicService(t *testing.T) {
|
||||
assert.Equal(t, empty,
|
||||
buildPrivateServiceCandidates([]Service{
|
||||
{Enabled: sql.NullBool{Valid: true, Bool: true},
|
||||
Private: sql.NullBool{Valid: true, Bool: false},
|
||||
AccessGroups: []string{"group-1", "group-2"},
|
||||
Domain: sql.NullString{Valid: true, String: "test.a.b"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
}, []Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}},
|
||||
},
|
||||
map[string][]*nmdata.Peer{
|
||||
"cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.1")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.2")}},
|
||||
"a-cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.3")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.4")}},
|
||||
}))
|
||||
}
|
||||
|
||||
// empty AccessList results in empty []PrivateServiceCandidates
|
||||
func TestBuildPrivateServiceCandidates_EmptyAccessList(t *testing.T) {
|
||||
assert.Equal(t, empty,
|
||||
buildPrivateServiceCandidates([]Service{
|
||||
{Enabled: sql.NullBool{Valid: true, Bool: true},
|
||||
Private: sql.NullBool{Valid: true, Bool: true},
|
||||
Domain: sql.NullString{Valid: true, String: "test.a.b"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
}, []Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}},
|
||||
},
|
||||
map[string][]*nmdata.Peer{
|
||||
"cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.1")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.2")}},
|
||||
"a-cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.3")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.4")}},
|
||||
}))
|
||||
}
|
||||
|
||||
// empty TragetCluster results in empty []PrivateServiceCandidates
|
||||
func TestBuildPrivateServiceCandidates_EmptyTargetCluster(t *testing.T) {
|
||||
assert.Equal(t, empty,
|
||||
buildPrivateServiceCandidates([]Service{
|
||||
{Enabled: sql.NullBool{Valid: true, Bool: true},
|
||||
Private: sql.NullBool{Valid: true, Bool: true},
|
||||
AccessGroups: []string{"group-1", "group-2"},
|
||||
Domain: sql.NullString{Valid: true, String: "test.a.b"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
}, []Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: ""},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}},
|
||||
},
|
||||
map[string][]*nmdata.Peer{
|
||||
"cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.1")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.2")}},
|
||||
"a-cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.3")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.4")}},
|
||||
}))
|
||||
}
|
||||
|
||||
func TestBuildPrivateServiceCandidates_EmptyServiceDomain(t *testing.T) {
|
||||
assert.Equal(t, empty,
|
||||
buildPrivateServiceCandidates([]Service{
|
||||
{Enabled: sql.NullBool{Valid: true, Bool: true},
|
||||
Private: sql.NullBool{Valid: true, Bool: true},
|
||||
Domain: sql.NullString{Valid: true, String: ""},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
}, []Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}},
|
||||
},
|
||||
map[string][]*nmdata.Peer{
|
||||
"cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.1")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.2")}},
|
||||
"a-cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.3")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.4")}},
|
||||
}))
|
||||
}
|
||||
|
||||
func TestBuildPrivateServiceCandidates_HappyPath(t *testing.T) {
|
||||
assert.Equal(t, []networkmap.PrivateServiceCandidate{
|
||||
{
|
||||
AccessGroups: []string{"group-1", "group-2"},
|
||||
Zone: nmdata.CustomZone{
|
||||
Domain: "a.b.",
|
||||
SearchDomainDisabled: true,
|
||||
NonAuthoritative: true,
|
||||
Records: []nmdata.SimpleRecord{
|
||||
{
|
||||
Name: "test.a.b.",
|
||||
Type: 1,
|
||||
Class: "IN",
|
||||
TTL: 5,
|
||||
RData: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
Name: "test.a.b.",
|
||||
Type: 1,
|
||||
Class: "IN",
|
||||
TTL: 5,
|
||||
RData: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
AccessGroups: []string{"group-1", "group-2"},
|
||||
Zone: nmdata.CustomZone{
|
||||
Domain: "c.d.",
|
||||
SearchDomainDisabled: true,
|
||||
NonAuthoritative: true,
|
||||
Records: []nmdata.SimpleRecord{
|
||||
{
|
||||
Name: "test.c.d.",
|
||||
Type: 1,
|
||||
Class: "IN",
|
||||
TTL: 5,
|
||||
RData: "127.0.0.3",
|
||||
},
|
||||
{
|
||||
Name: "test.c.d.",
|
||||
Type: 1,
|
||||
Class: "IN",
|
||||
TTL: 5,
|
||||
RData: "127.0.0.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
buildPrivateServiceCandidates([]Service{
|
||||
{Enabled: sql.NullBool{Valid: true, Bool: true},
|
||||
Private: sql.NullBool{Valid: true, Bool: true},
|
||||
AccessGroups: []string{"group-1", "group-2"},
|
||||
Domain: sql.NullString{Valid: true, String: "test.a.b"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "cluster"}},
|
||||
{Enabled: sql.NullBool{Valid: true, Bool: true},
|
||||
Private: sql.NullBool{Valid: true, Bool: true},
|
||||
AccessGroups: []string{"group-1", "group-2"},
|
||||
Domain: sql.NullString{Valid: true, String: "test.c.d"},
|
||||
ProxyCluster: sql.NullString{Valid: true, String: "a-cluster"}},
|
||||
}, []Domain{
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "a.b"}},
|
||||
{TargetCluster: sql.NullString{Valid: true, String: "a-cluster"},
|
||||
Domain: sql.NullString{Valid: true, String: "c.d"}},
|
||||
},
|
||||
map[string][]*nmdata.Peer{
|
||||
"cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.1")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.2")}},
|
||||
"a-cluster": {&nmdata.Peer{IP: netip.MustParseAddr("127.0.0.3")}, &nmdata.Peer{IP: netip.MustParseAddr("127.0.0.4")}},
|
||||
}))
|
||||
}
|
||||
|
||||
// disabled network resource shouldn't be in the resulting map
|
||||
func TestBuildResourcePolicies_DisabledNetworkResource(t *testing.T) {
|
||||
networkResources := []nmdata.NetworkResource{
|
||||
{ID: "net-res-1", Enabled: false},
|
||||
}
|
||||
policies := []nmdata.Policy{
|
||||
{ID: "policy-1", Enabled: true},
|
||||
}
|
||||
resourceToGroupIdx := map[string]map[string]any{}
|
||||
policyToDestinationResourceIdx := map[string]map[string]any{
|
||||
"policy-1": {
|
||||
"net-res-1": struct{}{},
|
||||
"net-res-3": struct{}{},
|
||||
},
|
||||
}
|
||||
policyToDestinationGroupIdx := map[string]map[string]any{}
|
||||
|
||||
assert.Empty(t, buildResourcePolicies(
|
||||
networkResources, policies, resourceToGroupIdx, policyToDestinationResourceIdx, policyToDestinationGroupIdx))
|
||||
}
|
||||
|
||||
// disabled policy shouldn't be in the resulting map
|
||||
func TestBuildResourcePolicies_DisabledPolicy(t *testing.T) {
|
||||
networkResources := []nmdata.NetworkResource{
|
||||
{ID: "net-res-1", Enabled: true},
|
||||
}
|
||||
policies := []nmdata.Policy{
|
||||
{ID: "policy-1", Enabled: false},
|
||||
}
|
||||
resourceToGroupIdx := map[string]map[string]any{}
|
||||
policyToDestinationResourceIdx := map[string]map[string]any{
|
||||
"policy-1": {
|
||||
"net-res-1": struct{}{},
|
||||
"net-res-3": struct{}{},
|
||||
},
|
||||
}
|
||||
policyToDestinationGroupIdx := map[string]map[string]any{}
|
||||
|
||||
assert.Empty(t, buildResourcePolicies(
|
||||
networkResources, policies, resourceToGroupIdx, policyToDestinationResourceIdx, policyToDestinationGroupIdx))
|
||||
}
|
||||
|
||||
// build ResourcePolicies via PolicyToDestinationResourceIdx only
|
||||
func TestBuildResourcePolicies_ViaPolicyToDestinationResourceIdx(t *testing.T) {
|
||||
networkResources := []nmdata.NetworkResource{
|
||||
{ID: "net-res-1", Enabled: true},
|
||||
{ID: "net-res-2", Enabled: true},
|
||||
{ID: "net-res-3", Enabled: true},
|
||||
}
|
||||
policies := []nmdata.Policy{
|
||||
{ID: "policy-1", Enabled: true},
|
||||
{ID: "policy-2", Enabled: true},
|
||||
{ID: "policy-3", Enabled: true},
|
||||
}
|
||||
resourceToGroupIdx := map[string]map[string]any{}
|
||||
policyToDestinationResourceIdx := map[string]map[string]any{
|
||||
"policy-1": {
|
||||
"net-res-1": struct{}{},
|
||||
"net-res-3": struct{}{},
|
||||
},
|
||||
"policy-2": {
|
||||
"net-res-2": struct{}{},
|
||||
},
|
||||
"policy-3": {
|
||||
"net-res-1": struct{}{},
|
||||
"net-res-2": struct{}{},
|
||||
},
|
||||
}
|
||||
policyToDestinationGroupIdx := map[string]map[string]any{}
|
||||
|
||||
resourceToPolicies := buildResourcePolicies(
|
||||
networkResources, policies, resourceToGroupIdx, policyToDestinationResourceIdx, policyToDestinationGroupIdx)
|
||||
|
||||
assert.Equal(t, map[string][]*nmdata.Policy{
|
||||
"net-res-1": {
|
||||
{ID: "policy-1", Enabled: true},
|
||||
{ID: "policy-3", Enabled: true},
|
||||
},
|
||||
"net-res-2": {
|
||||
{ID: "policy-2", Enabled: true},
|
||||
{ID: "policy-3", Enabled: true},
|
||||
},
|
||||
"net-res-3": {
|
||||
{ID: "policy-1", Enabled: true},
|
||||
},
|
||||
}, resourceToPolicies)
|
||||
}
|
||||
|
||||
// build ResourcePolicies via PolicyToDestinationGroupIdx only
|
||||
func TestBuildResourcePolicies_ViaPolicyToDestinationGroupIdx(t *testing.T) {
|
||||
networkResources := []nmdata.NetworkResource{
|
||||
{ID: "net-res-1", Enabled: true},
|
||||
{ID: "net-res-2", Enabled: true},
|
||||
{ID: "net-res-3", Enabled: true},
|
||||
}
|
||||
policies := []nmdata.Policy{
|
||||
{ID: "policy-1", Enabled: true},
|
||||
{ID: "policy-2", Enabled: true},
|
||||
{ID: "policy-3", Enabled: true},
|
||||
}
|
||||
resourceToGroupIdx := map[string]map[string]any{
|
||||
"net-res-1": {
|
||||
"group-1": struct{}{},
|
||||
"group-2": struct{}{},
|
||||
},
|
||||
"net-res-2": {
|
||||
"group-2": struct{}{},
|
||||
"group-3": struct{}{},
|
||||
},
|
||||
"net-res-3": {
|
||||
"group-3": struct{}{},
|
||||
"group-4": struct{}{},
|
||||
},
|
||||
}
|
||||
policyToDestinationResourceIdx := map[string]map[string]any{}
|
||||
policyToDestinationGroupIdx := map[string]map[string]any{
|
||||
"policy-1": {
|
||||
"group-1": struct{}{},
|
||||
"group-2": struct{}{},
|
||||
},
|
||||
"policy-2": {
|
||||
"group-1": struct{}{},
|
||||
"group-4": struct{}{},
|
||||
},
|
||||
"policy-3": {
|
||||
"group-1": struct{}{},
|
||||
"group-3": struct{}{},
|
||||
},
|
||||
}
|
||||
|
||||
resourceToPolicies := buildResourcePolicies(
|
||||
networkResources, policies, resourceToGroupIdx, policyToDestinationResourceIdx, policyToDestinationGroupIdx)
|
||||
|
||||
assert.Equal(t, map[string][]*nmdata.Policy{
|
||||
"net-res-1": {
|
||||
{ID: "policy-1", Enabled: true},
|
||||
{ID: "policy-2", Enabled: true},
|
||||
{ID: "policy-3", Enabled: true},
|
||||
},
|
||||
"net-res-2": {
|
||||
{ID: "policy-1", Enabled: true},
|
||||
{ID: "policy-3", Enabled: true},
|
||||
},
|
||||
"net-res-3": {
|
||||
{ID: "policy-2", Enabled: true},
|
||||
{ID: "policy-3", Enabled: true},
|
||||
},
|
||||
}, resourceToPolicies)
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
@@ -22,7 +23,8 @@ const (
|
||||
var _ networkmapdb.NetworkMapDBStore = &PgStore{}
|
||||
|
||||
type PgStore struct {
|
||||
Pool *pgxpool.Pool
|
||||
Pool *pgxpool.Pool
|
||||
Location *time.Location
|
||||
}
|
||||
|
||||
type PgStoreConn struct {
|
||||
@@ -45,7 +47,23 @@ func NewPostgresqlStore(ctx context.Context, dsn string) (*PgStore, error) {
|
||||
return &PgStore{Pool: pool}, nil
|
||||
}
|
||||
|
||||
// This is used to control the timezone timestamps returned in.
|
||||
// By default pgx returns timestamps in the local timezone,
|
||||
// which may not be desirable.
|
||||
// use .UsingTimeZone(time.UTC) to return timestamps in UTC TZ
|
||||
func (p *PgStore) UsingTimeZone(location *time.Location) {
|
||||
p.Location = location
|
||||
}
|
||||
|
||||
func (p *PgStore) UsingConnection(c *pgx.Conn) networkmapdb.NetworkMapDBStoreConn {
|
||||
if p.Location != nil {
|
||||
c.TypeMap().RegisterType(&pgtype.Type{
|
||||
Name: "timestamptz",
|
||||
OID: pgtype.TimestamptzOID,
|
||||
Codec: &pgtype.TimestamptzCodec{ScanLocation: time.UTC},
|
||||
})
|
||||
}
|
||||
|
||||
return &PgStoreConn{Conn: c}
|
||||
}
|
||||
|
||||
@@ -54,6 +72,13 @@ func (p *PgStore) BeginTx(ctx context.Context) (networkmapdb.NetworkMapDBStoreCo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.Location != nil {
|
||||
tx.Conn().TypeMap().RegisterType(&pgtype.Type{
|
||||
Name: "timestamptz",
|
||||
OID: pgtype.TimestamptzOID,
|
||||
Codec: &pgtype.TimestamptzCodec{ScanLocation: time.UTC},
|
||||
})
|
||||
}
|
||||
return &PgStoreConn{Conn: tx}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user