From eb5798e927539016d07bfb92a091792ae0d1bc0e Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Mon, 9 Dec 2024 19:42:38 +0100 Subject: [PATCH 1/9] Migrate networks, network routers and network resource tables Signed-off-by: bcmmbaga --- management/server/sql_store.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/management/server/sql_store.go b/management/server/sql_store.go index 1fd8ae2aabe..265b6d6e613 100644 --- a/management/server/sql_store.go +++ b/management/server/sql_store.go @@ -15,6 +15,7 @@ import ( "sync" "time" + "github.com/netbirdio/netbird/management/server/networks" log "github.com/sirupsen/logrus" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" @@ -89,6 +90,7 @@ func NewSqlStore(ctx context.Context, db *gorm.DB, storeEngine StoreEngine, metr &SetupKey{}, &nbpeer.Peer{}, &User{}, &PersonalAccessToken{}, &nbgroup.Group{}, &Account{}, &Policy{}, &PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{}, &installation{}, &account.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{}, + &networks.Network{}, &networks.NetworkRouter{}, &networks.NetworkResource{}, ) if err != nil { return nil, fmt.Errorf("auto migrate: %w", err) From 6a1eda1caa61c28f5eebbb06dac7bd32c4f18e23 Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Tue, 10 Dec 2024 11:20:48 +0100 Subject: [PATCH 2/9] Add account networks Signed-off-by: bcmmbaga --- management/server/account.go | 9 +++++++++ management/server/account_test.go | 6 ++++++ management/server/networks/network.go | 10 ++++++++++ 3 files changed, 25 insertions(+) diff --git a/management/server/account.go b/management/server/account.go index fbe6fcc1a4b..e89609e574f 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -21,6 +21,7 @@ import ( cacheStore "github.com/eko/gocache/v3/store" "github.com/hashicorp/go-multierror" "github.com/miekg/dns" + "github.com/netbirdio/netbird/management/server/networks" gocache "github.com/patrickmn/go-cache" "github.com/rs/xid" log "github.com/sirupsen/logrus" @@ -276,6 +277,8 @@ type Account struct { PostureChecks []*posture.Checks `gorm:"foreignKey:AccountID;references:id"` // Settings is a dictionary of Account settings Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"` + + Networks []*networks.Network `gorm:"foreignKey:AccountID;references:id"` } // Subclass used in gorm to only load settings and not whole account @@ -879,6 +882,11 @@ func (a *Account) Copy() *Account { postureChecks = append(postureChecks, postureCheck.Copy()) } + nets := []*networks.Network{} + for _, network := range a.Networks { + nets = append(nets, network.Copy()) + } + return &Account{ Id: a.Id, CreatedBy: a.CreatedBy, @@ -897,6 +905,7 @@ func (a *Account) Copy() *Account { DNSSettings: dnsSettings, PostureChecks: postureChecks, Settings: settings, + Networks: nets, } } diff --git a/management/server/account_test.go b/management/server/account_test.go index d952e118acf..675ad65cb2b 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -16,6 +16,7 @@ import ( "time" "github.com/golang-jwt/jwt" + "github.com/netbirdio/netbird/management/server/networks" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1778,6 +1779,11 @@ func TestAccount_Copy(t *testing.T) { }, }, Settings: &Settings{}, + Networks: []*networks.Network{ + { + ID: "network1", + }, + }, } err := hasNilField(account) if err != nil { diff --git a/management/server/networks/network.go b/management/server/networks/network.go index 8920294586a..310262d1132 100644 --- a/management/server/networks/network.go +++ b/management/server/networks/network.go @@ -17,3 +17,13 @@ func NewNetwork(accountId, name, description string) *Network { Description: description, } } + +// Copy returns a copy of a posture checks. +func (n *Network) Copy() *Network { + return &Network{ + ID: n.ID, + AccountID: n.AccountID, + Name: n.Name, + Description: n.Description, + } +} From f9be8f829e091ebe2faabcd24aac94eff2dd8457 Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Tue, 10 Dec 2024 11:21:14 +0100 Subject: [PATCH 3/9] Add networks store implementation and tests Signed-off-by: bcmmbaga --- management/server/sql_store.go | 92 +++++++++++++++++++++ management/server/sql_store_test.go | 118 +++++++++++++++++++++++++++ management/server/status/error.go | 5 ++ management/server/store.go | 16 ++++ management/server/testdata/store.sql | 4 + 5 files changed, 235 insertions(+) diff --git a/management/server/sql_store.go b/management/server/sql_store.go index 265b6d6e613..a1be5bbb44a 100644 --- a/management/server/sql_store.go +++ b/management/server/sql_store.go @@ -1599,3 +1599,95 @@ func (s *SqlStore) SaveDNSSettings(ctx context.Context, lockStrength LockingStre return nil } + +func (s *SqlStore) GetAccountNetworks(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*networks.Network, error) { + var networks []*networks.Network + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Find(&networks, accountIDCondition, accountID) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to get networks from the store: %s", result.Error) + return nil, status.Errorf(status.Internal, "failed to get networks from store") + } + + return networks, nil +} + +func (s *SqlStore) GetNetworkByID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networks.Network, error) { + var network *networks.Network + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}). + First(&network, accountAndIDQueryCondition, accountID, networkID) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, status.NewNetworkNotFoundError(networkID) + } + + log.WithContext(ctx).Errorf("failed to get network from store: %v", result.Error) + return nil, status.Errorf(status.Internal, "failed to get network from store") + } + + return network, nil +} + +func (s *SqlStore) SaveNetwork(ctx context.Context, lockStrength LockingStrength, network *networks.Network) error { + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Save(network) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to save network to store: %v", result.Error) + return status.Errorf(status.Internal, "failed to save network to store") + } + + return nil +} + +func (s *SqlStore) DeleteNetwork(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) error { + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}). + Delete(&networks.Network{}, accountAndIDQueryCondition, accountID, networkID) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to delete network from store: %v", result.Error) + return status.Errorf(status.Internal, "failed to delete network from store") + } + + if result.RowsAffected == 0 { + return status.NewNetworkNotFoundError(networkID) + } + + return nil +} + +func (s *SqlStore) GetNetworkRoutersByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) ([]*networks.NetworkRouter, error) { + //TODO implement me + panic("implement me") +} + +func (s *SqlStore) GetNetworkRouterByID(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) (*networks.NetworkRouter, error) { + //TODO implement me + panic("implement me") +} + +func (s *SqlStore) SaveNetworkRouter(ctx context.Context, lockStrength LockingStrength, router *networks.NetworkRouter) error { + //TODO implement me + panic("implement me") +} + +func (s *SqlStore) DeleteNetworkRouter(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) error { + //TODO implement me + panic("implement me") +} + +func (s *SqlStore) GetAccountNetworkResourceByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networks.NetworkResource, error) { + //TODO implement me + panic("implement me") +} + +func (s *SqlStore) GetAccountNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*networks.NetworkResource, error) { + //TODO implement me + panic("implement me") +} + +func (s *SqlStore) SaveNetworkResource(ctx context.Context, lockStrength LockingStrength, resource *networks.NetworkResource) error { + //TODO implement me + panic("implement me") +} + +func (s *SqlStore) DeleteNetworkResource(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) error { + //TODO implement me + panic("implement me") +} diff --git a/management/server/sql_store_test.go b/management/server/sql_store_test.go index 6064b019f29..4e5940cfdda 100644 --- a/management/server/sql_store_test.go +++ b/management/server/sql_store_test.go @@ -16,6 +16,7 @@ import ( "github.com/google/uuid" nbdns "github.com/netbirdio/netbird/dns" nbgroup "github.com/netbirdio/netbird/management/server/group" + "github.com/netbirdio/netbird/management/server/networks" "github.com/netbirdio/netbird/management/server/posture" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -2051,3 +2052,120 @@ func TestSqlStore_DeleteNameServerGroup(t *testing.T) { require.Error(t, err) require.Nil(t, nsGroup) } + +func TestSqlStore_GetAccountNetworks(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + tests := []struct { + name string + accountID string + expectedCount int + }{ + { + name: "retrieve networks by existing account ID", + accountID: "bf1c8084-ba50-4ce7-9439-34653001fc3b", + expectedCount: 1, + }, + + { + name: "retrieve networks by non-existing account ID", + accountID: "non-existent", + expectedCount: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + networks, err := store.GetAccountNetworks(context.Background(), LockingStrengthShare, tt.accountID) + require.NoError(t, err) + require.Len(t, networks, tt.expectedCount) + }) + } +} + +func TestSqlStore_GetNetworkByID(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + tests := []struct { + name string + networkID string + expectError bool + }{ + { + name: "retrieve existing network ID", + networkID: "ct286bi7qv930dsrrug0", + expectError: false, + }, + { + name: "retrieve non-existing network ID", + networkID: "non-existing", + expectError: true, + }, + { + name: "retrieve network with empty ID", + networkID: "", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + network, err := store.GetNetworkByID(context.Background(), LockingStrengthShare, accountID, tt.networkID) + if tt.expectError { + require.Error(t, err) + sErr, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, sErr.Type(), status.NotFound) + require.Nil(t, network) + } else { + require.NoError(t, err) + require.NotNil(t, network) + require.Equal(t, tt.networkID, network.ID) + } + }) + } +} + +func TestSqlStore_SaveNetwork(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + network := &networks.Network{ + ID: "net-id", + AccountID: accountID, + Name: "net", + } + + err = store.SaveNetwork(context.Background(), LockingStrengthUpdate, network) + require.NoError(t, err) + + savedNet, err := store.GetNetworkByID(context.Background(), LockingStrengthShare, accountID, network.ID) + require.NoError(t, err) + require.Equal(t, network, savedNet) +} + +func TestSqlStore_DeleteNetwork(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + networkID := "ct286bi7qv930dsrrug0" + + err = store.DeleteNetwork(context.Background(), LockingStrengthUpdate, accountID, networkID) + require.NoError(t, err) + + network, err := store.GetNetworkByID(context.Background(), LockingStrengthShare, accountID, networkID) + require.Error(t, err) + sErr, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, status.NotFound, sErr.Type()) + require.Nil(t, network) +} diff --git a/management/server/status/error.go b/management/server/status/error.go index 59f436f5b19..33df4ed3333 100644 --- a/management/server/status/error.go +++ b/management/server/status/error.go @@ -154,3 +154,8 @@ func NewPolicyNotFoundError(policyID string) error { func NewNameServerGroupNotFoundError(nsGroupID string) error { return Errorf(NotFound, "nameserver group: %s not found", nsGroupID) } + +// NewNetworkNotFoundError creates a new Error with NotFound type for a missing network. +func NewNetworkNotFoundError(networkID string) error { + return Errorf(NotFound, "network: %s not found", networkID) +} diff --git a/management/server/store.go b/management/server/store.go index b16ad8a1aa4..1a8168de7e4 100644 --- a/management/server/store.go +++ b/management/server/store.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "github.com/netbirdio/netbird/management/server/networks" log "github.com/sirupsen/logrus" "gorm.io/driver/sqlite" "gorm.io/gorm" @@ -140,6 +141,21 @@ type Store interface { // This is also a method of metrics.DataSource interface. GetStoreEngine() StoreEngine ExecuteInTransaction(ctx context.Context, f func(store Store) error) error + + GetAccountNetworks(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*networks.Network, error) + GetNetworkByID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networks.Network, error) + SaveNetwork(ctx context.Context, lockStrength LockingStrength, network *networks.Network) error + DeleteNetwork(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) error + + GetNetworkRoutersByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) ([]*networks.NetworkRouter, error) + GetNetworkRouterByID(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) (*networks.NetworkRouter, error) + SaveNetworkRouter(ctx context.Context, lockStrength LockingStrength, router *networks.NetworkRouter) error + DeleteNetworkRouter(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) error + + GetAccountNetworkResourceByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networks.NetworkResource, error) + GetAccountNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*networks.NetworkResource, error) + SaveNetworkResource(ctx context.Context, lockStrength LockingStrength, resource *networks.NetworkResource) error + DeleteNetworkResource(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) error } type StoreEngine string diff --git a/management/server/testdata/store.sql b/management/server/testdata/store.sql index 168973cad91..f1111da19a0 100644 --- a/management/server/testdata/store.sql +++ b/management/server/testdata/store.sql @@ -12,6 +12,7 @@ CREATE TABLE `installations` (`id` integer,`installation_id_value` text,PRIMARY CREATE TABLE `extra_settings` (`peer_approval_enabled` numeric,`integrated_validator_groups` text); CREATE TABLE `posture_checks` (`id` text,`name` text,`description` text,`account_id` text,`checks` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_posture_checks` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`)); CREATE TABLE `network_addresses` (`net_ip` text,`mac` text); +CREATE TABLE `networks` (`id` text,`account_id` text,`name` text,`description` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_networks` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`)); CREATE INDEX `idx_accounts_domain` ON `accounts`(`domain`); CREATE INDEX `idx_setup_keys_account_id` ON `setup_keys`(`account_id`); CREATE INDEX `idx_peers_key` ON `peers`(`key`); @@ -24,6 +25,8 @@ CREATE INDEX `idx_policy_rules_policy_id` ON `policy_rules`(`policy_id`); CREATE INDEX `idx_routes_account_id` ON `routes`(`account_id`); CREATE INDEX `idx_name_server_groups_account_id` ON `name_server_groups`(`account_id`); CREATE INDEX `idx_posture_checks_account_id` ON `posture_checks`(`account_id`); +CREATE INDEX `idx_networks_id` ON `networks`(`id`); +CREATE INDEX `idx_networks_account_id` ON `networks`(`account_id`); INSERT INTO accounts VALUES('bf1c8084-ba50-4ce7-9439-34653001fc3b','','2024-10-02 16:03:06.778746+02:00','test.com','private',1,'af1c8024-ha40-4ce2-9418-34653101fc3c','{"IP":"100.64.0.0","Mask":"//8AAA=="}','',0,'[]',0,86400000000000,0,0,0,'',NULL,NULL,NULL); INSERT INTO "groups" VALUES('cs1tnh0hhcjnqoiuebeg','bf1c8084-ba50-4ce7-9439-34653001fc3b','All','api','[]',0,''); @@ -34,3 +37,4 @@ INSERT INTO personal_access_tokens VALUES('9dj38s35-63fb-11ec-90d6-0242ac120003' INSERT INTO installations VALUES(1,''); INSERT INTO policies VALUES('cs1tnh0hhcjnqoiuebf0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Default','This is a default rule that allows connections between all the resources',1,'[]'); INSERT INTO policy_rules VALUES('cs387mkv2d4bgq41b6n0','cs1tnh0hhcjnqoiuebf0','Default','This is a default rule that allows connections between all the resources',1,'accept','["cs1tnh0hhcjnqoiuebeg"]','["cs1tnh0hhcjnqoiuebeg"]',1,'all',NULL,NULL); +INSERT INTO networks VALUES('ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Test Network','Test Network'); From 21586acc1647c79fb23b01a69ace05ce51e55cdc Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Tue, 10 Dec 2024 14:59:55 +0100 Subject: [PATCH 4/9] Add network routers in account object Signed-off-by: bcmmbaga --- management/server/account.go | 9 ++++++++- management/server/networks/network_router.go | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/management/server/account.go b/management/server/account.go index e89609e574f..bd0b2f049bb 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -278,7 +278,8 @@ type Account struct { // Settings is a dictionary of Account settings Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"` - Networks []*networks.Network `gorm:"foreignKey:AccountID;references:id"` + Networks []*networks.Network `gorm:"foreignKey:AccountID;references:id"` + NetworkRouters []*networks.NetworkRouter `gorm:"foreignKey:AccountID;references:id"` } // Subclass used in gorm to only load settings and not whole account @@ -887,6 +888,11 @@ func (a *Account) Copy() *Account { nets = append(nets, network.Copy()) } + networkRouters := []*networks.NetworkRouter{} + for _, router := range a.NetworkRouters { + networkRouters = append(networkRouters, router.Copy()) + } + return &Account{ Id: a.Id, CreatedBy: a.CreatedBy, @@ -906,6 +912,7 @@ func (a *Account) Copy() *Account { PostureChecks: postureChecks, Settings: settings, Networks: nets, + NetworkRouters: networkRouters, } } diff --git a/management/server/networks/network_router.go b/management/server/networks/network_router.go index cf7859fa8dc..cadabf3cc57 100644 --- a/management/server/networks/network_router.go +++ b/management/server/networks/network_router.go @@ -31,3 +31,15 @@ func NewNetworkRouter(accountID string, networkID string, peer string, peerGroup Metric: metric, }, nil } + +func (n *NetworkRouter) Copy() *NetworkRouter { + return &NetworkRouter{ + ID: n.ID, + NetworkID: n.NetworkID, + AccountID: n.AccountID, + Peer: n.Peer, + PeerGroups: n.PeerGroups, + Masquerade: n.Masquerade, + Metric: n.Metric, + } +} From 6dd6992415bb6d67b5d1d26ed90a945b12e05330 Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Tue, 10 Dec 2024 15:00:23 +0100 Subject: [PATCH 5/9] Add network routers store implementation Signed-off-by: bcmmbaga --- management/server/sql_store.go | 52 ++++++++-- management/server/sql_store_test.go | 140 +++++++++++++++++++++++++++ management/server/status/error.go | 5 + management/server/store.go | 6 +- management/server/testdata/store.sql | 5 + 5 files changed, 195 insertions(+), 13 deletions(-) diff --git a/management/server/sql_store.go b/management/server/sql_store.go index a1be5bbb44a..e341796f975 100644 --- a/management/server/sql_store.go +++ b/management/server/sql_store.go @@ -1652,24 +1652,56 @@ func (s *SqlStore) DeleteNetwork(ctx context.Context, lockStrength LockingStreng return nil } -func (s *SqlStore) GetNetworkRoutersByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) ([]*networks.NetworkRouter, error) { - //TODO implement me - panic("implement me") +func (s *SqlStore) GetNetworkRoutersByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) ([]*networks.NetworkRouter, error) { + var netRouters []*networks.NetworkRouter + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}). + Find(&netRouters, "account_id = ? AND network_id = ?", accountID, netID) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to get network routers from store: %v", result.Error) + return nil, status.Errorf(status.Internal, "failed to get network routers from store") + } + + return netRouters, nil } func (s *SqlStore) GetNetworkRouterByID(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) (*networks.NetworkRouter, error) { - //TODO implement me - panic("implement me") + var netRouter *networks.NetworkRouter + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}). + First(&netRouter, accountAndIDQueryCondition, accountID, routerID) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, status.NewNetworkRouterNotFoundError(routerID) + } + log.WithContext(ctx).Errorf("failed to get network router from store: %v", result.Error) + return nil, status.Errorf(status.Internal, "failed to get network router from store") + } + + return netRouter, nil } func (s *SqlStore) SaveNetworkRouter(ctx context.Context, lockStrength LockingStrength, router *networks.NetworkRouter) error { - //TODO implement me - panic("implement me") + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Save(router) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to save network router to store: %v", result.Error) + return status.Errorf(status.Internal, "failed to save network router to store") + } + + return nil } func (s *SqlStore) DeleteNetworkRouter(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) error { - //TODO implement me - panic("implement me") + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}). + Delete(&networks.NetworkRouter{}, accountAndIDQueryCondition, accountID, routerID) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to delete network router from store: %v", result.Error) + return status.Errorf(status.Internal, "failed to delete network router from store") + } + + if result.RowsAffected == 0 { + return status.NewNetworkRouterNotFoundError(routerID) + } + + return nil } func (s *SqlStore) GetAccountNetworkResourceByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networks.NetworkResource, error) { @@ -1677,7 +1709,7 @@ func (s *SqlStore) GetAccountNetworkResourceByNetID(ctx context.Context, lockStr panic("implement me") } -func (s *SqlStore) GetAccountNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*networks.NetworkResource, error) { +func (s *SqlStore) GetNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*networks.NetworkResource, error) { //TODO implement me panic("implement me") } diff --git a/management/server/sql_store_test.go b/management/server/sql_store_test.go index 4e5940cfdda..bb6f5c84d36 100644 --- a/management/server/sql_store_test.go +++ b/management/server/sql_store_test.go @@ -307,6 +307,24 @@ func TestSqlite_DeleteAccount(t *testing.T) { Status: &nbpeer.PeerStatus{Connected: true, LastSeen: time.Now().UTC()}, } account.Users[testUserID] = user + account.Networks = []*networks.Network{ + { + ID: "network_id", + AccountID: account.Id, + Name: "network name", + Description: "network description", + }, + } + account.NetworkRouters = []*networks.NetworkRouter{ + { + ID: "router_id", + NetworkID: account.Networks[0].ID, + AccountID: account.Id, + PeerGroups: []string{"group_id"}, + Masquerade: true, + Metric: 1, + }, + } err = store.SaveAccount(context.Background(), account) require.NoError(t, err) @@ -353,6 +371,11 @@ func TestSqlite_DeleteAccount(t *testing.T) { } + for _, network := range account.Networks { + routers, err := store.GetNetworkRoutersByNetID(context.Background(), LockingStrengthShare, account.Id, network.ID) + require.NoError(t, err, "expecting no error after removing DeleteAccount when searching for network routers") + require.Len(t, routers, 0, "expecting no network routers to be found after DeleteAccount") + } } func TestSqlite_GetAccount(t *testing.T) { @@ -2169,3 +2192,120 @@ func TestSqlStore_DeleteNetwork(t *testing.T) { require.Equal(t, status.NotFound, sErr.Type()) require.Nil(t, network) } + +func TestSqlStore_GetNetworkRoutersByNetID(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + + tests := []struct { + name string + networkID string + expectedCount int + }{ + { + name: "retrieve routers by existing network ID", + networkID: "ct286bi7qv930dsrrug0", + expectedCount: 1, + }, + { + name: "retrieve routers by non-existing network ID", + networkID: "non-existent", + expectedCount: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + routers, err := store.GetNetworkRoutersByNetID(context.Background(), LockingStrengthShare, accountID, tt.networkID) + require.NoError(t, err) + require.Len(t, routers, tt.expectedCount) + }) + } +} + +func TestSqlStore_GetNetworkRouterByID(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + tests := []struct { + name string + networkRouterID string + expectError bool + }{ + { + name: "retrieve existing network router ID", + networkRouterID: "ctc20ji7qv9ck2sebc80", + expectError: false, + }, + { + name: "retrieve non-existing network router ID", + networkRouterID: "non-existing", + expectError: true, + }, + { + name: "retrieve network with empty router ID", + networkRouterID: "", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + networkRouter, err := store.GetNetworkRouterByID(context.Background(), LockingStrengthShare, accountID, tt.networkRouterID) + if tt.expectError { + require.Error(t, err) + sErr, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, sErr.Type(), status.NotFound) + require.Nil(t, networkRouter) + } else { + require.NoError(t, err) + require.NotNil(t, networkRouter) + require.Equal(t, tt.networkRouterID, networkRouter.ID) + } + }) + } +} + +func TestSqlStore_SaveNetworkRouter(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + networkID := "ct286bi7qv930dsrrug0" + + netRouter, err := networks.NewNetworkRouter(accountID, networkID, "", []string{"net-router-grp"}, true, 0) + require.NoError(t, err) + + err = store.SaveNetworkRouter(context.Background(), LockingStrengthUpdate, netRouter) + require.NoError(t, err) + + savedNetRouter, err := store.GetNetworkRouterByID(context.Background(), LockingStrengthShare, accountID, netRouter.ID) + require.NoError(t, err) + require.Equal(t, netRouter, savedNetRouter) +} + +func TestSqlStore_DeleteNetworkRouter(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + netRouterID := "ctc20ji7qv9ck2sebc80" + + err = store.DeleteNetworkRouter(context.Background(), LockingStrengthUpdate, accountID, netRouterID) + require.NoError(t, err) + + netRouter, err := store.GetNetworkByID(context.Background(), LockingStrengthShare, accountID, netRouterID) + require.Error(t, err) + sErr, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, status.NotFound, sErr.Type()) + require.Nil(t, netRouter) +} diff --git a/management/server/status/error.go b/management/server/status/error.go index 33df4ed3333..3ab02adf676 100644 --- a/management/server/status/error.go +++ b/management/server/status/error.go @@ -159,3 +159,8 @@ func NewNameServerGroupNotFoundError(nsGroupID string) error { func NewNetworkNotFoundError(networkID string) error { return Errorf(NotFound, "network: %s not found", networkID) } + +// NewNetworkRouterNotFoundError creates a new Error with NotFound type for a missing network router. +func NewNetworkRouterNotFoundError(routerID string) error { + return Errorf(NotFound, "network router: %s not found", routerID) +} diff --git a/management/server/store.go b/management/server/store.go index 1a8168de7e4..df0d9fd31fb 100644 --- a/management/server/store.go +++ b/management/server/store.go @@ -147,13 +147,13 @@ type Store interface { SaveNetwork(ctx context.Context, lockStrength LockingStrength, network *networks.Network) error DeleteNetwork(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) error - GetNetworkRoutersByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) ([]*networks.NetworkRouter, error) + GetNetworkRoutersByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) ([]*networks.NetworkRouter, error) GetNetworkRouterByID(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) (*networks.NetworkRouter, error) SaveNetworkRouter(ctx context.Context, lockStrength LockingStrength, router *networks.NetworkRouter) error DeleteNetworkRouter(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) error - GetAccountNetworkResourceByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networks.NetworkResource, error) - GetAccountNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*networks.NetworkResource, error) + GetAccountNetworkResourceByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) (*networks.NetworkResource, error) + GetNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*networks.NetworkResource, error) SaveNetworkResource(ctx context.Context, lockStrength LockingStrength, resource *networks.NetworkResource) error DeleteNetworkResource(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) error } diff --git a/management/server/testdata/store.sql b/management/server/testdata/store.sql index f1111da19a0..28f9280f610 100644 --- a/management/server/testdata/store.sql +++ b/management/server/testdata/store.sql @@ -12,6 +12,7 @@ CREATE TABLE `installations` (`id` integer,`installation_id_value` text,PRIMARY CREATE TABLE `extra_settings` (`peer_approval_enabled` numeric,`integrated_validator_groups` text); CREATE TABLE `posture_checks` (`id` text,`name` text,`description` text,`account_id` text,`checks` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_posture_checks` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`)); CREATE TABLE `network_addresses` (`net_ip` text,`mac` text); +CREATE TABLE `network_routers` (`id` text,`network_id` text,`account_id` text,`peer` text,`peer_groups` text,`masquerade` numeric,`metric` integer,PRIMARY KEY (`id`)); CREATE TABLE `networks` (`id` text,`account_id` text,`name` text,`description` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_networks` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`)); CREATE INDEX `idx_accounts_domain` ON `accounts`(`domain`); CREATE INDEX `idx_setup_keys_account_id` ON `setup_keys`(`account_id`); @@ -25,6 +26,9 @@ CREATE INDEX `idx_policy_rules_policy_id` ON `policy_rules`(`policy_id`); CREATE INDEX `idx_routes_account_id` ON `routes`(`account_id`); CREATE INDEX `idx_name_server_groups_account_id` ON `name_server_groups`(`account_id`); CREATE INDEX `idx_posture_checks_account_id` ON `posture_checks`(`account_id`); +CREATE INDEX `idx_network_routers_id` ON `network_routers`(`id`); +CREATE INDEX `idx_network_routers_account_id` ON `network_routers`(`account_id`); +CREATE INDEX `idx_network_routers_network_id` ON `network_routers`(`network_id`); CREATE INDEX `idx_networks_id` ON `networks`(`id`); CREATE INDEX `idx_networks_account_id` ON `networks`(`account_id`); @@ -37,4 +41,5 @@ INSERT INTO personal_access_tokens VALUES('9dj38s35-63fb-11ec-90d6-0242ac120003' INSERT INTO installations VALUES(1,''); INSERT INTO policies VALUES('cs1tnh0hhcjnqoiuebf0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Default','This is a default rule that allows connections between all the resources',1,'[]'); INSERT INTO policy_rules VALUES('cs387mkv2d4bgq41b6n0','cs1tnh0hhcjnqoiuebf0','Default','This is a default rule that allows connections between all the resources',1,'accept','["cs1tnh0hhcjnqoiuebeg"]','["cs1tnh0hhcjnqoiuebeg"]',1,'all',NULL,NULL); +INSERT INTO network_routers VALUES('ctc20ji7qv9ck2sebc80','ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','cs1tnh0hhcjnqoiuebeg',NULL,0,0); INSERT INTO networks VALUES('ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Test Network','Test Network'); From 382dba4a8526d66d34f7d2096fe09c47ae21f849 Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Tue, 10 Dec 2024 16:42:11 +0100 Subject: [PATCH 6/9] Add network resources store implementation Signed-off-by: bcmmbaga --- management/server/account.go | 5 +- management/server/sql_store.go | 50 ++++++++-- management/server/sql_store_test.go | 132 +++++++++++++++++++++++++++ management/server/status/error.go | 5 + management/server/store.go | 2 +- management/server/testdata/store.sql | 5 + 6 files changed, 187 insertions(+), 12 deletions(-) diff --git a/management/server/account.go b/management/server/account.go index bd0b2f049bb..8ee9db44aff 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -278,8 +278,9 @@ type Account struct { // Settings is a dictionary of Account settings Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"` - Networks []*networks.Network `gorm:"foreignKey:AccountID;references:id"` - NetworkRouters []*networks.NetworkRouter `gorm:"foreignKey:AccountID;references:id"` + Networks []*networks.Network `gorm:"foreignKey:AccountID;references:id"` + NetworkRouters []*networks.NetworkRouter `gorm:"foreignKey:AccountID;references:id"` + NetworkResources []*networks.NetworkResource `gorm:"foreignKey:AccountID;references:id"` } // Subclass used in gorm to only load settings and not whole account diff --git a/management/server/sql_store.go b/management/server/sql_store.go index e341796f975..1067550a745 100644 --- a/management/server/sql_store.go +++ b/management/server/sql_store.go @@ -1704,22 +1704,54 @@ func (s *SqlStore) DeleteNetworkRouter(ctx context.Context, lockStrength Locking return nil } -func (s *SqlStore) GetAccountNetworkResourceByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) (*networks.NetworkResource, error) { - //TODO implement me - panic("implement me") +func (s *SqlStore) GetNetworkResourcesByNetID(ctx context.Context, lockStrength LockingStrength, accountID, networkID string) ([]*networks.NetworkResource, error) { + var netResources []*networks.NetworkResource + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}). + Find(&netResources, "account_id = ? AND network_id = ?", accountID, networkID) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to get network resources from store: %v", result.Error) + return nil, status.Errorf(status.Internal, "failed to get network resources from store") + } + + return netResources, nil } func (s *SqlStore) GetNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*networks.NetworkResource, error) { - //TODO implement me - panic("implement me") + var netResources *networks.NetworkResource + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}). + First(&netResources, accountAndIDQueryCondition, accountID, resourceID) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, status.NewNetworkResourceNotFoundError(resourceID) + } + log.WithContext(ctx).Errorf("failed to get network resource from store: %v", result.Error) + return nil, status.Errorf(status.Internal, "failed to get network resource from store") + } + + return netResources, nil } func (s *SqlStore) SaveNetworkResource(ctx context.Context, lockStrength LockingStrength, resource *networks.NetworkResource) error { - //TODO implement me - panic("implement me") + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).Save(resource) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to save network resource to store: %v", result.Error) + return status.Errorf(status.Internal, "failed to save network resource to store") + } + + return nil } func (s *SqlStore) DeleteNetworkResource(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) error { - //TODO implement me - panic("implement me") + result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}). + Delete(&networks.NetworkResource{}, accountAndIDQueryCondition, accountID, resourceID) + if result.Error != nil { + log.WithContext(ctx).Errorf("failed to delete network resource from store: %v", result.Error) + return status.Errorf(status.Internal, "failed to delete network resource from store") + } + + if result.RowsAffected == 0 { + return status.NewNetworkResourceNotFoundError(resourceID) + } + + return nil } diff --git a/management/server/sql_store_test.go b/management/server/sql_store_test.go index bb6f5c84d36..d8f78242547 100644 --- a/management/server/sql_store_test.go +++ b/management/server/sql_store_test.go @@ -325,6 +325,17 @@ func TestSqlite_DeleteAccount(t *testing.T) { Metric: 1, }, } + account.NetworkResources = []*networks.NetworkResource{ + { + ID: "resource_id", + NetworkID: account.Networks[0].ID, + AccountID: account.Id, + Name: "Name", + Description: "Description", + Type: "Domain", + Address: "example.com", + }, + } err = store.SaveAccount(context.Background(), account) require.NoError(t, err) @@ -375,6 +386,10 @@ func TestSqlite_DeleteAccount(t *testing.T) { routers, err := store.GetNetworkRoutersByNetID(context.Background(), LockingStrengthShare, account.Id, network.ID) require.NoError(t, err, "expecting no error after removing DeleteAccount when searching for network routers") require.Len(t, routers, 0, "expecting no network routers to be found after DeleteAccount") + + resources, err := store.GetNetworkResourcesByNetID(context.Background(), LockingStrengthShare, account.Id, network.ID) + require.NoError(t, err, "expecting no error after removing DeleteAccount when searching for network resources") + require.Len(t, resources, 0, "expecting no network resources to be found after DeleteAccount") } } @@ -2309,3 +2324,120 @@ func TestSqlStore_DeleteNetworkRouter(t *testing.T) { require.Equal(t, status.NotFound, sErr.Type()) require.Nil(t, netRouter) } + +func TestSqlStore_GetNetworkResourcesByNetID(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + + tests := []struct { + name string + networkID string + expectedCount int + }{ + { + name: "retrieve resources by existing network ID", + networkID: "ct286bi7qv930dsrrug0", + expectedCount: 1, + }, + { + name: "retrieve resources by non-existing network ID", + networkID: "non-existent", + expectedCount: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resources, err := store.GetNetworkResourcesByNetID(context.Background(), LockingStrengthShare, accountID, tt.networkID) + require.NoError(t, err) + require.Len(t, resources, tt.expectedCount) + }) + } +} + +func TestSqlStore_GetNetworkResourceByID(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + tests := []struct { + name string + networkResourceID string + expectError bool + }{ + { + name: "retrieve existing network resource ID", + networkResourceID: "ctc4nci7qv9061u6ilfg", + expectError: false, + }, + { + name: "retrieve non-existing network resource ID", + networkResourceID: "non-existing", + expectError: true, + }, + { + name: "retrieve network with empty resource ID", + networkResourceID: "", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + networkResource, err := store.GetNetworkResourceByID(context.Background(), LockingStrengthShare, accountID, tt.networkResourceID) + if tt.expectError { + require.Error(t, err) + sErr, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, sErr.Type(), status.NotFound) + require.Nil(t, networkResource) + } else { + require.NoError(t, err) + require.NotNil(t, networkResource) + require.Equal(t, tt.networkResourceID, networkResource.ID) + } + }) + } +} + +func TestSqlStore_SaveNetworkResource(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + networkID := "ct286bi7qv930dsrrug0" + + netResource, err := networks.NewNetworkResource(accountID, networkID, "resource-name", "resource-description", "example.com") + require.NoError(t, err) + + err = store.SaveNetworkResource(context.Background(), LockingStrengthUpdate, netResource) + require.NoError(t, err) + + savedNetResource, err := store.GetNetworkResourceByID(context.Background(), LockingStrengthShare, accountID, netResource.ID) + require.NoError(t, err) + require.Equal(t, netResource, savedNetResource) +} + +func TestSqlStore_DeleteNetworkResource(t *testing.T) { + store, cleanup, err := NewTestStoreFromSQL(context.Background(), "testdata/store.sql", t.TempDir()) + t.Cleanup(cleanup) + require.NoError(t, err) + + accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" + netResourceID := "ctc4nci7qv9061u6ilfg" + + err = store.DeleteNetworkResource(context.Background(), LockingStrengthUpdate, accountID, netResourceID) + require.NoError(t, err) + + netRouter, err := store.GetNetworkByID(context.Background(), LockingStrengthShare, accountID, netResourceID) + require.Error(t, err) + sErr, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, status.NotFound, sErr.Type()) + require.Nil(t, netRouter) +} diff --git a/management/server/status/error.go b/management/server/status/error.go index 3ab02adf676..70209b7c84f 100644 --- a/management/server/status/error.go +++ b/management/server/status/error.go @@ -164,3 +164,8 @@ func NewNetworkNotFoundError(networkID string) error { func NewNetworkRouterNotFoundError(routerID string) error { return Errorf(NotFound, "network router: %s not found", routerID) } + +// NewNetworkResourceNotFoundError creates a new Error with NotFound type for a missing network resource. +func NewNetworkResourceNotFoundError(resourceID string) error { + return Errorf(NotFound, "network resource: %s not found", resourceID) +} diff --git a/management/server/store.go b/management/server/store.go index df0d9fd31fb..5b7253d71f4 100644 --- a/management/server/store.go +++ b/management/server/store.go @@ -152,7 +152,7 @@ type Store interface { SaveNetworkRouter(ctx context.Context, lockStrength LockingStrength, router *networks.NetworkRouter) error DeleteNetworkRouter(ctx context.Context, lockStrength LockingStrength, accountID, routerID string) error - GetAccountNetworkResourceByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) (*networks.NetworkResource, error) + GetNetworkResourcesByNetID(ctx context.Context, lockStrength LockingStrength, accountID, netID string) ([]*networks.NetworkResource, error) GetNetworkResourceByID(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) (*networks.NetworkResource, error) SaveNetworkResource(ctx context.Context, lockStrength LockingStrength, resource *networks.NetworkResource) error DeleteNetworkResource(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) error diff --git a/management/server/testdata/store.sql b/management/server/testdata/store.sql index 28f9280f610..7f0c7b5a4fd 100644 --- a/management/server/testdata/store.sql +++ b/management/server/testdata/store.sql @@ -13,6 +13,7 @@ CREATE TABLE `extra_settings` (`peer_approval_enabled` numeric,`integrated_valid CREATE TABLE `posture_checks` (`id` text,`name` text,`description` text,`account_id` text,`checks` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_posture_checks` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`)); CREATE TABLE `network_addresses` (`net_ip` text,`mac` text); CREATE TABLE `network_routers` (`id` text,`network_id` text,`account_id` text,`peer` text,`peer_groups` text,`masquerade` numeric,`metric` integer,PRIMARY KEY (`id`)); +CREATE TABLE `network_resources` (`id` text,`network_id` text,`account_id` text,`type` text,`address` text,PRIMARY KEY (`id`)); CREATE TABLE `networks` (`id` text,`account_id` text,`name` text,`description` text,PRIMARY KEY (`id`),CONSTRAINT `fk_accounts_networks` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`)); CREATE INDEX `idx_accounts_domain` ON `accounts`(`domain`); CREATE INDEX `idx_setup_keys_account_id` ON `setup_keys`(`account_id`); @@ -29,6 +30,9 @@ CREATE INDEX `idx_posture_checks_account_id` ON `posture_checks`(`account_id`); CREATE INDEX `idx_network_routers_id` ON `network_routers`(`id`); CREATE INDEX `idx_network_routers_account_id` ON `network_routers`(`account_id`); CREATE INDEX `idx_network_routers_network_id` ON `network_routers`(`network_id`); +CREATE INDEX `idx_network_resources_account_id` ON `network_resources`(`account_id`); +CREATE INDEX `idx_network_resources_network_id` ON `network_resources`(`network_id`); +CREATE INDEX `idx_network_resources_id` ON `network_resources`(`id`); CREATE INDEX `idx_networks_id` ON `networks`(`id`); CREATE INDEX `idx_networks_account_id` ON `networks`(`account_id`); @@ -42,4 +46,5 @@ INSERT INTO installations VALUES(1,''); INSERT INTO policies VALUES('cs1tnh0hhcjnqoiuebf0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Default','This is a default rule that allows connections between all the resources',1,'[]'); INSERT INTO policy_rules VALUES('cs387mkv2d4bgq41b6n0','cs1tnh0hhcjnqoiuebf0','Default','This is a default rule that allows connections between all the resources',1,'accept','["cs1tnh0hhcjnqoiuebeg"]','["cs1tnh0hhcjnqoiuebeg"]',1,'all',NULL,NULL); INSERT INTO network_routers VALUES('ctc20ji7qv9ck2sebc80','ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','cs1tnh0hhcjnqoiuebeg',NULL,0,0); +INSERT INTO network_resources VALUES ('ctc4nci7qv9061u6ilfg','ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Host','192.168.1.1'); INSERT INTO networks VALUES('ct286bi7qv930dsrrug0','bf1c8084-ba50-4ce7-9439-34653001fc3b','Test Network','Test Network'); From a52ef1a066099bb8df23dcbf6e9258bd283a5ead Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Tue, 10 Dec 2024 16:49:32 +0100 Subject: [PATCH 7/9] Add missing network resource in account copy Signed-off-by: bcmmbaga --- management/server/account.go | 6 ++++++ management/server/networks/network_resource.go | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/management/server/account.go b/management/server/account.go index 8ee9db44aff..3729972a7a7 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -894,6 +894,11 @@ func (a *Account) Copy() *Account { networkRouters = append(networkRouters, router.Copy()) } + networkResources := []*networks.NetworkResource{} + for _, resource := range a.NetworkResources { + networkResources = append(networkResources, resource.Copy()) + } + return &Account{ Id: a.Id, CreatedBy: a.CreatedBy, @@ -914,6 +919,7 @@ func (a *Account) Copy() *Account { Settings: settings, Networks: nets, NetworkRouters: networkRouters, + NetworkResources: networkResources, } } diff --git a/management/server/networks/network_resource.go b/management/server/networks/network_resource.go index 2a86cb0d7f4..79e7bee417f 100644 --- a/management/server/networks/network_resource.go +++ b/management/server/networks/network_resource.go @@ -49,6 +49,18 @@ func NewNetworkResource(accountID, networkID, name, description, address string) }, nil } +func (n *NetworkResource) Copy() *NetworkResource { + return &NetworkResource{ + ID: n.ID, + AccountID: n.AccountID, + NetworkID: n.NetworkID, + Name: n.Name, + Description: n.Description, + Type: n.Type, + Address: n.Address, + } +} + // getResourceType returns the type of the resource based on the address func getResourceType(address string) (NetworkResourceType, error) { if ip, cidr, err := net.ParseCIDR(address); err == nil { From 7146fd03d1734ae9b95d61b592150834307ee7bd Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Tue, 10 Dec 2024 17:04:16 +0100 Subject: [PATCH 8/9] Refactor Signed-off-by: bcmmbaga --- management/server/sql_store_test.go | 42 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/management/server/sql_store_test.go b/management/server/sql_store_test.go index d8f78242547..db084b8f2e3 100644 --- a/management/server/sql_store_test.go +++ b/management/server/sql_store_test.go @@ -2351,9 +2351,9 @@ func TestSqlStore_GetNetworkResourcesByNetID(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - resources, err := store.GetNetworkResourcesByNetID(context.Background(), LockingStrengthShare, accountID, tt.networkID) + netResources, err := store.GetNetworkResourcesByNetID(context.Background(), LockingStrengthShare, accountID, tt.networkID) require.NoError(t, err) - require.Len(t, resources, tt.expectedCount) + require.Len(t, netResources, tt.expectedCount) }) } } @@ -2365,40 +2365,40 @@ func TestSqlStore_GetNetworkResourceByID(t *testing.T) { accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" tests := []struct { - name string - networkResourceID string - expectError bool + name string + netResourceID string + expectError bool }{ { - name: "retrieve existing network resource ID", - networkResourceID: "ctc4nci7qv9061u6ilfg", - expectError: false, + name: "retrieve existing network resource ID", + netResourceID: "ctc4nci7qv9061u6ilfg", + expectError: false, }, { - name: "retrieve non-existing network resource ID", - networkResourceID: "non-existing", - expectError: true, + name: "retrieve non-existing network resource ID", + netResourceID: "non-existing", + expectError: true, }, { - name: "retrieve network with empty resource ID", - networkResourceID: "", - expectError: true, + name: "retrieve network with empty resource ID", + netResourceID: "", + expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - networkResource, err := store.GetNetworkResourceByID(context.Background(), LockingStrengthShare, accountID, tt.networkResourceID) + netResource, err := store.GetNetworkResourceByID(context.Background(), LockingStrengthShare, accountID, tt.netResourceID) if tt.expectError { require.Error(t, err) sErr, ok := status.FromError(err) require.True(t, ok) require.Equal(t, sErr.Type(), status.NotFound) - require.Nil(t, networkResource) + require.Nil(t, netResource) } else { require.NoError(t, err) - require.NotNil(t, networkResource) - require.Equal(t, tt.networkResourceID, networkResource.ID) + require.NotNil(t, netResource) + require.Equal(t, tt.netResourceID, netResource.ID) } }) } @@ -2412,7 +2412,7 @@ func TestSqlStore_SaveNetworkResource(t *testing.T) { accountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b" networkID := "ct286bi7qv930dsrrug0" - netResource, err := networks.NewNetworkResource(accountID, networkID, "resource-name", "resource-description", "example.com") + netResource, err := networks.NewNetworkResource(accountID, networkID, "resource-name", "", "example.com") require.NoError(t, err) err = store.SaveNetworkResource(context.Background(), LockingStrengthUpdate, netResource) @@ -2434,10 +2434,10 @@ func TestSqlStore_DeleteNetworkResource(t *testing.T) { err = store.DeleteNetworkResource(context.Background(), LockingStrengthUpdate, accountID, netResourceID) require.NoError(t, err) - netRouter, err := store.GetNetworkByID(context.Background(), LockingStrengthShare, accountID, netResourceID) + netResource, err := store.GetNetworkByID(context.Background(), LockingStrengthShare, accountID, netResourceID) require.Error(t, err) sErr, ok := status.FromError(err) require.True(t, ok) require.Equal(t, status.NotFound, sErr.Type()) - require.Nil(t, netRouter) + require.Nil(t, netResource) } From c7f79085df50c898a72850fe18f9df698f44eeb9 Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Tue, 10 Dec 2024 17:26:23 +0100 Subject: [PATCH 9/9] Fix tests Signed-off-by: bcmmbaga --- management/server/account_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/management/server/account_test.go b/management/server/account_test.go index 675ad65cb2b..fa163c1ca85 100644 --- a/management/server/account_test.go +++ b/management/server/account_test.go @@ -1784,6 +1784,24 @@ func TestAccount_Copy(t *testing.T) { ID: "network1", }, }, + NetworkRouters: []*networks.NetworkRouter{ + { + ID: "router1", + NetworkID: "network1", + PeerGroups: []string{"group1"}, + Masquerade: false, + Metric: 0, + }, + }, + NetworkResources: []*networks.NetworkResource{ + { + ID: "resource1", + NetworkID: "network1", + Name: "resource", + Type: "Subnet", + Address: "172.12.6.1/24", + }, + }, } err := hasNilField(account) if err != nil {