From 23a5c0de4b0bc3e503fbfa07d1410e7bc7bd3fb6 Mon Sep 17 00:00:00 2001 From: Dmitri Dolguikh Date: Wed, 29 Jul 2026 17:37:00 +0200 Subject: [PATCH] added support for routes Signed-off-by: Dmitri Dolguikh --- .../internals/network_map_db/db_store.go | 94 ++++++++++++ .../network_map_db/pgsql/another_test.go | 141 +++++++++++++++++ .../internals/network_map_db/pgsql/group.go | 53 ++++--- .../network_map_db/pgsql/group_test.go | 71 ++++++++- .../internals/network_map_db/pgsql/peer.go | 144 ++++++++---------- .../internals/network_map_db/pgsql/policy.go | 141 ++++++++--------- .../internals/network_map_db/pgsql/route.go | 67 ++++++++ 7 files changed, 536 insertions(+), 175 deletions(-) create mode 100644 management/internals/network_map_db/pgsql/another_test.go create mode 100644 management/internals/network_map_db/pgsql/route.go diff --git a/management/internals/network_map_db/db_store.go b/management/internals/network_map_db/db_store.go index 1cfafa8d4..7f1a26b8c 100644 --- a/management/internals/network_map_db/db_store.go +++ b/management/internals/network_map_db/db_store.go @@ -2,10 +2,21 @@ package networkmapdb import ( "context" + "database/sql" + "encoding/json" + "errors" + "reflect" + "strings" "github.com/netbirdio/netbird/shared/management/networkmap/nmdata" ) +const ( + NMAP_STRUCT_TAG = "nmap" + NMAP_SKIP = "skip" + NMAP_MAP_TO = "map_to" +) + type NetworkMapDBStore interface { GetGroups(ctx context.Context, accountId string) ([]nmdata.Group, error) // TODO: join/populate peers GetPeers(ctx context.Context, accountId string) ([]nmdata.Peer, error) @@ -15,3 +26,86 @@ type NetworkMapDBStore interface { type NetworkMapDBStoreImpl struct { store NetworkMapDBStore } + +func FromSqlTypesToSharedTypes(src reflect.Value, dst reflect.Value) error { + typ := src.Type() + + for i := 0; i < typ.NumField(); i++ { + f := typ.Field(i) + + fieldTags := make(map[string]string) + if v := f.Tag.Get(NMAP_STRUCT_TAG); v != "" { + for _, t := range strings.Split(v, ",") { + kv := tagFromString(t) + fieldTags[kv.Key] = kv.Value + } + } + if _, ok := fieldTags[NMAP_SKIP]; ok { + continue + } + if f.PkgPath != "" { // skip unexported fields + continue + } + dstFieldName := f.Name + if override, ok := fieldTags[NMAP_MAP_TO]; ok { + dstFieldName = override + } + + dstField := dst.FieldByName(dstFieldName) + if !dstField.IsValid() { + return errors.New("invalid field in destination type: " + dstFieldName) + } + + srcField := src.Field(i) + + srcFieldType := srcField.Type().String() + switch srcFieldType { + case "string": + s := srcField.Interface().(string) + dstField.SetString(s) + case "sql.NullString": + s := srcField.Interface().(sql.NullString) + if s.Valid { + dstField.SetString(s.String) + } + case "sql.NullTime": + s := srcField.Interface().(sql.NullTime) + if s.Valid { + if dstField.Kind() == reflect.Ptr { + t := reflect.ValueOf(&s.Time).Elem() + dstField.Set(t.Addr()) + } else { + dstField.Set(reflect.ValueOf(s.Time)) + } + } + case "sql.NullBool": + s := srcField.Interface().(sql.NullBool) + if s.Valid { + dstField.SetBool(s.Bool) + } + case "sql.NullInt64": + s := srcField.Interface().(sql.NullInt64) + if s.Valid { + dstField.SetInt(s.Int64) + } + case "json.RawMessage": + s := srcField.Interface().(json.RawMessage) + json.Unmarshal(s, dstField.Addr().Interface()) + } + } + + return nil +} + +type fieldTag struct { + Key string + Value string +} + +func tagFromString(t string) fieldTag { + kv := strings.Split(t, ":") + if len(kv) == 1 { + return fieldTag{Key: strings.TrimSpace(kv[0])} + } + return fieldTag{Key: strings.TrimSpace(kv[0]), Value: strings.TrimSpace(kv[1])} +} diff --git a/management/internals/network_map_db/pgsql/another_test.go b/management/internals/network_map_db/pgsql/another_test.go new file mode 100644 index 000000000..1bfd3296e --- /dev/null +++ b/management/internals/network_map_db/pgsql/another_test.go @@ -0,0 +1,141 @@ +package networkmap_pgsql + +import ( + "database/sql" + "encoding/json" + "reflect" + "testing" + "time" + + networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db" + "github.com/stretchr/testify/assert" +) + +type g1 struct { + Name sql.NullString +} + +type g2 struct { + Name string +} + +type g11 struct { + Name sql.NullString + PublicId sql.NullString +} + +type g22 struct { + Name string + PublicId string +} + +type g111 struct { + Name sql.NullString + TrueOrFalse sql.NullBool +} + +type g222 struct { + Name string + TrueOrFalse bool +} + +type g1111 struct { + Time sql.NullTime +} + +type g2222 struct { + Time time.Time +} + +type g11111 struct { + Blob json.RawMessage +} + +type embeddedS struct { + Name string + SomeField int +} +type g22222 struct { + Blob embeddedS +} + +type t1 struct { + Field string `nmap:"skip"` +} + +type dt1 struct { + Field string +} + +type o1 struct { + Field string `nmap:"map_to:AnotherField"` +} + +type do1 struct { + AnotherField string +} + +type ptr1 struct { + Field sql.NullString +} + +type ptro1 struct { + Field *string +} + +type i1 struct { + Field sql.NullInt64 +} + +type io1 struct { + Field int +} + +func TestOne(t *testing.T) { + + src := g1{Name: sql.NullString{String: "string", Valid: true}} + dst := g2{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src).Elem(), reflect.ValueOf(&dst).Elem())) + assert.Equal(t, g2{Name: "string"}, dst) + + src = g1{Name: sql.NullString{Valid: false}} + dst = g2{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src).Elem(), reflect.ValueOf(&dst).Elem())) + assert.Equal(t, g2{Name: ""}, dst) + + src1 := g11{Name: sql.NullString{String: "aaa", Valid: true}, PublicId: sql.NullString{String: "id", Valid: true}} + dst1 := g22{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src1).Elem(), reflect.ValueOf(&dst1).Elem())) + assert.Equal(t, g22{Name: "aaa", PublicId: "id"}, dst1) + + src2 := g111{Name: sql.NullString{String: "aaa", Valid: true}, TrueOrFalse: sql.NullBool{Bool: true, Valid: true}} + dst2 := g222{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src2).Elem(), reflect.ValueOf(&dst2).Elem())) + assert.Equal(t, g222{Name: "aaa", TrueOrFalse: true}, dst2) + + jb, _ := json.Marshal(embeddedS{Name: "blob-name", SomeField: 1}) + src3 := g11111{Blob: json.RawMessage(jb)} + dst3 := g22222{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src3).Elem(), reflect.ValueOf(&dst3).Elem())) + assert.Equal(t, g22222{Blob: embeddedS{Name: "blob-name", SomeField: 1}}, dst3) + + src4 := g11111{} + dst4 := g22222{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src4).Elem(), reflect.ValueOf(&dst4).Elem())) + assert.Equal(t, g22222{}, dst4) + + src5 := t1{Field: "shouldskip"} + dst5 := dt1{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src5).Elem(), reflect.ValueOf(&dst5).Elem())) + assert.Equal(t, dt1{}, dst5) + + src6 := o1{Field: "fieldvalue"} + dst6 := do1{} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src6).Elem(), reflect.ValueOf(&dst6).Elem())) + assert.Equal(t, do1{AnotherField: "fieldvalue"}, dst6) + + src7 := i1{Field: sql.NullInt64{Int64: int64(1), Valid: true}} + dst7 := io1{Field: 1} + assert.NoError(t, networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&src7).Elem(), reflect.ValueOf(&dst7).Elem())) + assert.Equal(t, io1{Field: 1}, dst7) +} diff --git a/management/internals/network_map_db/pgsql/group.go b/management/internals/network_map_db/pgsql/group.go index 9e2ce5a34..2fc70d641 100644 --- a/management/internals/network_map_db/pgsql/group.go +++ b/management/internals/network_map_db/pgsql/group.go @@ -4,14 +4,22 @@ import ( "context" "database/sql" "encoding/json" + "reflect" "github.com/jackc/pgx/v5" + networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db" "github.com/netbirdio/netbird/shared/management/networkmap/nmdata" ) const ( GetGroupsQuery = ` - select name, public_id, resources from groups where account_id=$1 + select name, public_id, resources, + ( + select array_agg(group_peers.peer_id) + from group_peers + where group_peers.group_id = groups.id + ) as peers + from groups where account_id=$1 ` ) @@ -21,33 +29,24 @@ func (pg *PgStore) GetGroups(ctx context.Context, accountId string) ([]nmdata.Gr return nil, err } - var ( - group nmdata.Group - name, publicId, resources sql.NullString - ) - - groups, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (nmdata.Group, error) { - err := row.Scan(&name, &publicId, &resources) + groups, err := pgx.CollectRows(rows, pgx.RowToStructByName[group]) + toret := make([]nmdata.Group, 0, len(groups)) + for _, g := range groups { + dg := nmdata.Group{} + err := networkmapdb.FromSqlTypesToSharedTypes( + reflect.ValueOf(&g).Elem(), reflect.ValueOf(&dg).Elem()) if err != nil { - return group, err + return nil, err } + toret = append(toret, dg) + } - if name.Valid { - group.Name = name.String - } - if publicId.Valid { - group.PublicID = publicId.String - } - if resources.Valid { - groupResources := make([]nmdata.Resource, 0) - err := json.Unmarshal([]byte(resources.String), &groupResources) - if err != nil { - return group, err - } - group.Resources = groupResources - } - return group, nil - }) - - return groups, err + return toret, err +} + +type group struct { + Name sql.NullString + PublicID sql.NullString + Resources json.RawMessage + Peers []string } diff --git a/management/internals/network_map_db/pgsql/group_test.go b/management/internals/network_map_db/pgsql/group_test.go index 5c1c6ae1e..58a73600c 100644 --- a/management/internals/network_map_db/pgsql/group_test.go +++ b/management/internals/network_map_db/pgsql/group_test.go @@ -2,6 +2,7 @@ package networkmap_pgsql import ( "context" + "fmt" "strings" "testing" @@ -15,7 +16,7 @@ import ( //go:embed test_db.sql var initDb string -func TestXxx(t *testing.T) { +func TestGetGroups(t *testing.T) { ctx := context.TODO() s, err := NewPostgresqlStore(ctx, "postgresql://root:netbird@localhost:5432/netbird") @@ -26,7 +27,7 @@ func TestXxx(t *testing.T) { _, err = s.pool.Query(ctx, "insert into groups (id, account_id, name, resources, public_id) VALUES('test-group-id-1','ck7bnf2t2r9s739pkug0','test-group-1', '[{\"ID\":\"cui7q2jl0ubs73d8qpi0\",\"Type\":\"host\"}]','public-id-1')") assert.NoError(t, err) - groups, err := s.GetGroups(ctx, "ck7bnf2t2r9s739pkug0") + groups, err := s.GetGroups(ctx, "ck7bnf2t2r9s739pkug0") //"ckd7ee2fic3c73dtendg") assert.NoError(t, err) assert.Contains(t, groups, @@ -38,6 +39,72 @@ func TestXxx(t *testing.T) { ) } +func TestGetPeers(t *testing.T) { + ctx := context.TODO() + + s, err := NewPostgresqlStore(ctx, "postgresql://root:netbird@localhost:5432/netbird") + assert.NoError(t, err) + // err = loadSQL(ctx, s.pool, initDb) + //assert.NoError(t, err) + + peers, err := s.GetPeers(ctx, "ck7bnf2t2r9s739pkug0") //"ckd7ee2fic3c73dtendg") + assert.NoError(t, err) + + fmt.Print(peers) + // assert.Contains(t, + // groups, + // nmdata.Group{Name: "test-group-1", PublicID: "public-id-1", Resources: []nmdata.Resource{{ID: "cui7q2jl0ubs73d8qpi0", Type: "host"}}}, + // ) + // assert.Contains(t, + // groups, + // nmdata.Group{Name: "All", PublicID: "d9aejspvcsu517nkh4a0", Resources: []nmdata.Resource{{ID: "cui7olrl0ubs73d8qpe0", Type: "subnet"}, {ID: "cui7q2jl0ubs73d8qpi0", Type: "host"}}}, + // ) +} + +func TestGetPolocies(t *testing.T) { + ctx := context.TODO() + + s, err := NewPostgresqlStore(ctx, "postgresql://root:netbird@localhost:5432/netbird") + assert.NoError(t, err) + // err = loadSQL(ctx, s.pool, initDb) + //assert.NoError(t, err) + + peers, err := s.GetPolicies(ctx, "ck7bnf2t2r9s739pkug0") //"ckd7ee2fic3c73dtendg") + assert.NoError(t, err) + + fmt.Print(peers) + // assert.Contains(t, + // groups, + // nmdata.Group{Name: "test-group-1", PublicID: "public-id-1", Resources: []nmdata.Resource{{ID: "cui7q2jl0ubs73d8qpi0", Type: "host"}}}, + // ) + // assert.Contains(t, + // groups, + // nmdata.Group{Name: "All", PublicID: "d9aejspvcsu517nkh4a0", Resources: []nmdata.Resource{{ID: "cui7olrl0ubs73d8qpe0", Type: "subnet"}, {ID: "cui7q2jl0ubs73d8qpi0", Type: "host"}}}, + // ) +} + +func TestGetRoutes(t *testing.T) { + ctx := context.TODO() + + s, err := NewPostgresqlStore(ctx, "postgresql://root:netbird@localhost:5432/netbird") + assert.NoError(t, err) + // err = loadSQL(ctx, s.pool, initDb) + //assert.NoError(t, err) + + peers, err := s.GetRoutes(ctx, "csg5iabl0ubs7398nf1g") //"ckd7ee2fic3c73dtendg") + assert.NoError(t, err) + + fmt.Print(peers) + // assert.Contains(t, + // groups, + // nmdata.Group{Name: "test-group-1", PublicID: "public-id-1", Resources: []nmdata.Resource{{ID: "cui7q2jl0ubs73d8qpi0", Type: "host"}}}, + // ) + // assert.Contains(t, + // groups, + // nmdata.Group{Name: "All", PublicID: "d9aejspvcsu517nkh4a0", Resources: []nmdata.Resource{{ID: "cui7olrl0ubs73d8qpe0", Type: "subnet"}, {ID: "cui7q2jl0ubs73d8qpi0", Type: "host"}}}, + // ) +} + func loadSQL(ctx context.Context, pool *pgxpool.Pool, initdb string) error { queries := strings.Split(string(initdb), ";") diff --git a/management/internals/network_map_db/pgsql/peer.go b/management/internals/network_map_db/pgsql/peer.go index 13d74b5e2..748a618b6 100644 --- a/management/internals/network_map_db/pgsql/peer.go +++ b/management/internals/network_map_db/pgsql/peer.go @@ -4,8 +4,10 @@ import ( "context" "database/sql" "encoding/json" + "reflect" "github.com/jackc/pgx/v5" + networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db" "github.com/netbirdio/netbird/shared/management/networkmap/nmdata" ) @@ -25,108 +27,94 @@ func (pg *PgStore) GetPeers(ctx context.Context, accountId string) ([]nmdata.Pee return nil, err } - var ( - key, sshKey, dnsLabel, userId sql.NullString - lastLogin sql.NullTime - sshEnabled, loginExpirationEnabled sql.NullBool - ip, ipv6, locationConnectionIp []byte - metaFiles, metaCapabilities, metaFlags, metaNetworkAddresses []byte - metaWtVersion, metaGoOS, metaOSVersion, metaKernelVersion sql.NullString - locationCountryCode, locationCityName sql.NullString - ) + peers, err := pgx.CollectRows(rows, pgx.RowToStructByName[peer]) + if err != nil { + return nil, err + } - peers, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (nmdata.Peer, error) { - var peer nmdata.Peer - err := row.Scan(&peer.ID, &key, &sshKey, &dnsLabel, &userId, &sshEnabled, &loginExpirationEnabled, &lastLogin, &ip, &ipv6, - &metaWtVersion, &metaGoOS, &metaOSVersion, &metaKernelVersion, &metaNetworkAddresses, &metaFiles, &metaCapabilities, &metaFlags, - &locationCountryCode, &locationCityName, &locationConnectionIp) + toret := make([]nmdata.Peer, 0, len(peers)) + for _, p := range peers { + dp := nmdata.Peer{} + err := networkmapdb.FromSqlTypesToSharedTypes( + reflect.ValueOf(&p).Elem(), reflect.ValueOf(&dp).Elem()) if err != nil { - return peer, err + return nil, err } - if key.Valid { - peer.Key = key.String + if p.MetaWtVersion.Valid { + dp.Meta.WtVersion = p.MetaWtVersion.String } - if sshKey.Valid { - peer.SSHKey = sshKey.String + if p.MetaGoOS.Valid { + dp.Meta.GoOS = p.MetaGoOS.String } - if dnsLabel.Valid { - peer.DNSLabel = dnsLabel.String + if p.MetaOSVersion.Valid { + dp.Meta.OSVersion = p.MetaOSVersion.String } - if userId.Valid { - peer.UserID = userId.String + if p.MetaKernelVersion.Valid { + dp.Meta.KernelVersion = p.MetaKernelVersion.String } - if lastLogin.Valid { - peer.LastLogin = &lastLogin.Time + if p.LocationCountryCode.Valid { + dp.Location.CountryCode = p.LocationCountryCode.String } - if sshEnabled.Valid { - peer.SSHEnabled = sshEnabled.Bool + if p.LocationCityName.Valid { + dp.Location.CityName = p.LocationCityName.String } - if loginExpirationEnabled.Valid { - peer.LoginExpirationEnabled = loginExpirationEnabled.Bool - } - if metaWtVersion.Valid { - peer.Meta.WtVersion = metaWtVersion.String - } - if metaGoOS.Valid { - peer.Meta.GoOS = metaGoOS.String - } - if metaOSVersion.Valid { - peer.Meta.OSVersion = metaOSVersion.String - } - if metaKernelVersion.Valid { - peer.Meta.KernelVersion = metaKernelVersion.String - } - if locationCountryCode.Valid { - peer.Location.CountryCode = locationCountryCode.String - } - if locationCityName.Valid { - peer.Location.CityName = locationCityName.String - } - if ip != nil { - err := json.Unmarshal(ip, &peer.IP) + if p.LocationConnectionIp != nil { + err := json.Unmarshal(p.LocationConnectionIp, &dp.Location.ConnectionIP) if err != nil { - return peer, err + return toret, err } } - if ipv6 != nil { - err := json.Unmarshal(ipv6, &peer.IPv6) + if p.MetaFiles != nil { + err := json.Unmarshal(p.MetaFiles, &dp.Meta.Files) if err != nil { - return peer, err + return toret, err } } - if locationConnectionIp != nil { - err := json.Unmarshal(locationConnectionIp, &peer.Location.ConnectionIP) + if p.MetaCapabilities != nil { + err := json.Unmarshal(p.MetaCapabilities, &dp.Meta.Capabilities) if err != nil { - return peer, err + return toret, err } } - if metaFiles != nil { - err := json.Unmarshal(metaFiles, &peer.Meta.Files) + if p.MetaFlags != nil { + err := json.Unmarshal(p.MetaFlags, &dp.Meta.Flags) if err != nil { - return peer, err + return toret, err } } - if metaCapabilities != nil { - err := json.Unmarshal(metaCapabilities, &peer.Meta.Capabilities) + if p.MetaNetworkAddresses != nil { + err := json.Unmarshal(p.MetaNetworkAddresses, &dp.Meta.NetworkAddresses) if err != nil { - return peer, err + return toret, err } } - if metaFlags != nil { - err := json.Unmarshal(metaFlags, &peer.Meta.Flags) - if err != nil { - return peer, err - } - } - if metaNetworkAddresses != nil { - err := json.Unmarshal(metaNetworkAddresses, &peer.Meta.NetworkAddresses) - if err != nil { - return peer, err - } - } - return peer, nil - }) + } - return peers, err + return toret, nil +} + +// TODO add support for creating struct fields from denormalized fields +type peer struct { + ID string + Key sql.NullString + SSHKey sql.NullString + DNSLabel sql.NullString + UserID sql.NullString + LastLogin sql.NullTime + SSHEnabled sql.NullBool + LoginExpirationEnabled sql.NullBool + IP json.RawMessage + IPv6 json.RawMessage + LocationConnectionIp json.RawMessage `nmap:"skip"` + MetaFiles json.RawMessage `nmap:"skip"` + MetaCapabilities json.RawMessage `nmap:"skip"` + MetaFlags json.RawMessage `nmap:"skip"` + MetaNetworkAddresses json.RawMessage `nmap:"skip"` + MetaWtVersion sql.NullString `nmap:"skip"` + MetaGoOS sql.NullString `nmap:"skip"` + MetaOSVersion sql.NullString `nmap:"skip"` + MetaKernelVersion sql.NullString `nmap:"skip"` + LocationCountryCode sql.NullString `nmap:"skip"` + LocationCityName sql.NullString `nmap:"skip"` } diff --git a/management/internals/network_map_db/pgsql/policy.go b/management/internals/network_map_db/pgsql/policy.go index 61be07e22..c54ae27e2 100644 --- a/management/internals/network_map_db/pgsql/policy.go +++ b/management/internals/network_map_db/pgsql/policy.go @@ -4,14 +4,16 @@ import ( "context" "database/sql" "encoding/json" + "reflect" "github.com/jackc/pgx/v5" + networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db" "github.com/netbirdio/netbird/shared/management/networkmap/nmdata" ) const ( GetPoliciesQuery = ` - select p.id, p.public_id, p.enabled, p.source_posture_checks, pr.enabled, pr.action, pr.protocol, pr.bidirectional, + select p.id, p.public_id, p.enabled, p.source_posture_checks, pr.enabled as rule_enabled, pr.action, pr.protocol, pr.bidirectional, pr.sources, pr.destinations, pr.source_resource, pr.destination_resource, pr.ports, pr.port_ranges, pr.authorized_groups, pr.authorized_user from policies as p @@ -26,18 +28,21 @@ func (pg *PgStore) GetPolicies(ctx context.Context, accountId string) ([]nmdata. return nil, err } - var ( - publicId, sourcePostureChecks sql.NullString - enabled, ruleEnabled, bidirectional sql.NullBool - action, protocol, sources, destinations sql.NullString - sourceResource, destinationResource, ports, portRanges sql.NullString - authorizedGroups, authorizedUser sql.NullString - ) + policies, err := pgx.CollectRows(rows, pgx.RowToStructByName[policy]) + if err != nil { + return nil, err + } + + toret := make([]nmdata.Policy, 0, len(policies)) + for _, p := range policies { + policy := nmdata.Policy{} + err := networkmapdb.FromSqlTypesToSharedTypes( + reflect.ValueOf(&p).Elem(), reflect.ValueOf(&policy).Elem()) + if err != nil { + return nil, err + } - policies, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (nmdata.Policy, error) { - var policy nmdata.Policy var policyRule *nmdata.PolicyRule - pr := func() *nmdata.PolicyRule { if policyRule != nil { return policyRule @@ -47,91 +52,91 @@ func (pg *PgStore) GetPolicies(ctx context.Context, accountId string) ([]nmdata. return policyRule } - err := row.Scan(&policy.ID, &publicId, &enabled, &sourcePostureChecks, &ruleEnabled, &action, &protocol, &bidirectional, - &sources, &destinations, &sourceResource, &destinationResource, &ports, &portRanges, - &authorizedGroups, &authorizedUser) - if err != nil { - return policy, err + if p.RuleEnabled.Valid { + pr().Enabled = p.RuleEnabled.Bool } - - if publicId.Valid { - policy.PublicID = publicId.String + if p.Action.Valid { + pr().Action = p.Action.String } - if sourcePostureChecks.Valid { - err := json.Unmarshal([]byte(sourcePostureChecks.String), &policy.SourcePostureChecks) + if p.Protocol.Valid { + pr().Protocol = p.Protocol.String + } + if p.Bidirectional.Valid { + pr().Bidirectional = p.Bidirectional.Bool + } + if len(p.Sources) > 0 { + err := json.Unmarshal([]byte(p.Sources), &pr().Sources) if err != nil { - return policy, err + return toret, err } } - if enabled.Valid { - policy.Enabled = enabled.Bool - } - if ruleEnabled.Valid { - pr().Enabled = ruleEnabled.Bool - } - if action.Valid { - pr().Action = action.String - } - if protocol.Valid { - pr().Protocol = protocol.String - } - if bidirectional.Valid { - pr().Bidirectional = bidirectional.Bool - } - if sources.Valid { - err := json.Unmarshal([]byte(sources.String), &pr().Sources) + if len(p.Destinations) > 0 { + err := json.Unmarshal([]byte(p.Destinations), &pr().Destinations) if err != nil { - return policy, err + return toret, err } } - if destinations.Valid { - err := json.Unmarshal([]byte(destinations.String), &pr().Destinations) + if len(p.SourceResource) > 0 { + err := json.Unmarshal([]byte(p.SourceResource), &pr().SourceResource) if err != nil { - return policy, err + return toret, err } } - if sourceResource.Valid { - err := json.Unmarshal([]byte(sourceResource.String), &pr().SourceResource) + if len(p.DestinationResource) > 0 { + err := json.Unmarshal([]byte(p.DestinationResource), &pr().DestinationResource) if err != nil { - return policy, err + return toret, err } } - if destinationResource.Valid { - err := json.Unmarshal([]byte(destinationResource.String), &pr().DestinationResource) + if len(p.Ports) > 0 { + err := json.Unmarshal([]byte(p.Ports), &pr().Ports) if err != nil { - return policy, err + return toret, err } } - if ports.Valid { - err := json.Unmarshal([]byte(ports.String), &pr().Ports) + if len(p.PortRanges) > 0 { + err := json.Unmarshal([]byte(p.PortRanges), &pr().PortRanges) if err != nil { - return policy, err + return toret, err } } - if portRanges.Valid { - err := json.Unmarshal([]byte(portRanges.String), &pr().PortRanges) + if len(p.AuthorizedGroups) > 0 { + err := json.Unmarshal([]byte(p.AuthorizedGroups), &pr().AuthorizedGroups) if err != nil { - return policy, err + return toret, err } } - if authorizedGroups.Valid { - err := json.Unmarshal([]byte(authorizedGroups.String), &pr().AuthorizedGroups) - if err != nil { - return policy, err - } - } - if authorizedUser.Valid { - pr().AuthorizedUser = authorizedUser.String + if p.AuthorizedUser.Valid { + pr().AuthorizedUser = p.AuthorizedUser.String } if policyRule != nil { - policyRule.ID = policy.ID - policyRule.PolicyID = policy.ID + policyRule.ID = p.ID + policyRule.PolicyID = p.ID policy.Rules = []*nmdata.PolicyRule{policyRule} } - return policy, nil - }) + toret = append(toret, policy) + } - return policies, err + return toret, err +} + +type policy struct { + ID string + PublicID sql.NullString + SourcePostureChecks json.RawMessage + Enabled sql.NullBool + RuleEnabled sql.NullBool `nmap:"skip"` + Bidirectional sql.NullBool `nmap:"skip"` + Action sql.NullString `nmap:"skip"` + Protocol sql.NullString `nmap:"skip"` + Sources json.RawMessage `nmap:"skip"` + Destinations json.RawMessage `nmap:"skip"` + SourceResource json.RawMessage `nmap:"skip"` + DestinationResource json.RawMessage `nmap:"skip"` + Ports json.RawMessage `nmap:"skip"` + PortRanges json.RawMessage `nmap:"skip"` + AuthorizedGroups json.RawMessage `nmap:"skip"` + AuthorizedUser sql.NullString `nmap:"skip"` } diff --git a/management/internals/network_map_db/pgsql/route.go b/management/internals/network_map_db/pgsql/route.go new file mode 100644 index 000000000..c712bbe17 --- /dev/null +++ b/management/internals/network_map_db/pgsql/route.go @@ -0,0 +1,67 @@ +package networkmap_pgsql + +import ( + "context" + "database/sql" + "encoding/json" + "reflect" + + "github.com/jackc/pgx/v5" + networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db" + "github.com/netbirdio/netbird/shared/management/networkmap/nmdata" +) + +const ( + GetRoutesQuery = ` + select id, account_id, public_id, network, domains, keep_route, net_id, description, + peer, peer as peer_id, peer_groups, network_type, masquerade, metric, enabled, + groups, access_control_groups, skip_auto_apply + from routes + where account_id=$1 + ` +) + +func (pg *PgStore) GetRoutes(ctx context.Context, accountId string) ([]nmdata.Route, error) { + rows, err := pg.pool.Query(ctx, GetRoutesQuery, accountId) + if err != nil { + return nil, err + } + + routes, err := pgx.CollectRows(rows, pgx.RowToStructByName[route]) + if err != nil { + return nil, err + } + + toret := make([]nmdata.Route, 0, len(routes)) + for _, r := range routes { + route := nmdata.Route{} + err := networkmapdb.FromSqlTypesToSharedTypes( + reflect.ValueOf(&r).Elem(), reflect.ValueOf(&route).Elem()) + if err != nil { + return nil, err + } + toret = append(toret, route) + } + return toret, nil +} + +type route struct { + ID string + AccountID sql.NullString + PublicID sql.NullString + Network json.RawMessage + Domains json.RawMessage + KeepRoute sql.NullBool + NetID sql.NullString + Description sql.NullString + Peer sql.NullString + PeerID sql.NullString + PeerGroups json.RawMessage + NetworkType sql.NullInt64 + Masquerade sql.NullBool + Metric sql.NullInt64 + Enabled sql.NullBool + Groups json.RawMessage + AccessControlGroups json.RawMessage + SkipAutoApply sql.NullBool +}