[management] add activity events for networks (#3069)

This commit is contained in:
Pascal Fischer 2024-12-18 11:39:17 +01:00 committed by GitHub
parent b53935065b
commit e40eb91e54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 373 additions and 59 deletions

View File

@ -276,10 +276,10 @@ var (
userManager := users.NewManager(store)
settingsManager := settings.NewManager(store)
permissionsManager := permissions.NewManager(userManager, settingsManager)
groupsManager := groups.NewManager(store, permissionsManager)
groupsManager := groups.NewManager(store, permissionsManager, accountManager)
resourcesManager := resources.NewManager(store, permissionsManager, groupsManager, accountManager)
routersManager := routers.NewManager(store, permissionsManager, accountManager)
networksManager := networks.NewManager(store, permissionsManager, resourcesManager)
networksManager := networks.NewManager(store, permissionsManager, resourcesManager, routersManager, accountManager)
httpAPIHandler, err := httpapi.APIHandler(ctx, accountManager, networksManager, resourcesManager, routersManager, groupsManager, geo, *jwtValidator, appMetrics, httpAPIAuthCfg, integratedPeerValidator)
if err != nil {

View File

@ -154,6 +154,21 @@ const (
AccountRoutingPeerDNSResolutionEnabled Activity = 71
AccountRoutingPeerDNSResolutionDisabled Activity = 72
NetworkCreated Activity = 73
NetworkUpdated Activity = 74
NetworkDeleted Activity = 75
NetworkResourceCreated Activity = 76
NetworkResourceUpdated Activity = 77
NetworkResourceDeleted Activity = 78
NetworkRouterCreated Activity = 79
NetworkRouterUpdated Activity = 80
NetworkRouterDeleted Activity = 81
ResourceAddedToGroup Activity = 82
ResourceRemovedFromGroup Activity = 83
)
var activityMap = map[Activity]Code{
@ -234,6 +249,21 @@ var activityMap = map[Activity]Code{
AccountRoutingPeerDNSResolutionEnabled: {"Account routing peer DNS resolution enabled", "account.setting.routing.peer.dns.resolution.enable"},
AccountRoutingPeerDNSResolutionDisabled: {"Account routing peer DNS resolution disabled", "account.setting.routing.peer.dns.resolution.disable"},
NetworkCreated: {"Network created", "network.create"},
NetworkUpdated: {"Network updated", "network.update"},
NetworkDeleted: {"Network deleted", "network.delete"},
NetworkResourceCreated: {"Network resource created", "network.resource.create"},
NetworkResourceUpdated: {"Network resource updated", "network.resource.update"},
NetworkResourceDeleted: {"Network resource deleted", "network.resource.delete"},
NetworkRouterCreated: {"Network router created", "network.router.create"},
NetworkRouterUpdated: {"Network router updated", "network.router.update"},
NetworkRouterDeleted: {"Network router deleted", "network.router.delete"},
ResourceAddedToGroup: {"Resource added to group", "resource.group.add"},
ResourceRemovedFromGroup: {"Resource removed from group", "resource.group.delete"},
}
// StringCode returns a string code of the activity

View File

@ -4,6 +4,8 @@ import (
"context"
"fmt"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/store"
@ -14,22 +16,24 @@ type Manager interface {
GetAllGroups(ctx context.Context, accountID, userID string) (map[string]*types.Group, error)
GetResourceGroupsInTransaction(ctx context.Context, transaction store.Store, lockingStrength store.LockingStrength, accountID, resourceID string) ([]*types.Group, error)
AddResourceToGroup(ctx context.Context, accountID, userID, groupID string, resourceID *types.Resource) error
AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID string, resourceID *types.Resource) error
RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID, resourceID string) error
AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID string, resourceID *types.Resource) (func(), error)
RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID, resourceID string) (func(), error)
}
type managerImpl struct {
store store.Store
permissionsManager permissions.Manager
accountManager s.AccountManager
}
type mockManager struct {
}
func NewManager(store store.Store, permissionsManager permissions.Manager) Manager {
func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager s.AccountManager) Manager {
return &managerImpl{
store: store,
permissionsManager: permissionsManager,
accountManager: accountManager,
}
}
@ -64,15 +68,40 @@ func (m *managerImpl) AddResourceToGroup(ctx context.Context, accountID, userID,
return err
}
return m.AddResourceToGroupInTransaction(ctx, m.store, accountID, groupID, resource)
event, err := m.AddResourceToGroupInTransaction(ctx, m.store, accountID, groupID, resource)
if err != nil {
return fmt.Errorf("error adding resource to group: %w", err)
}
func (m *managerImpl) AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID string, resource *types.Resource) error {
return transaction.AddResourceToGroup(ctx, accountID, groupID, resource)
event()
return nil
}
func (m *managerImpl) RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID, resourceID string) error {
return transaction.RemoveResourceFromGroup(ctx, accountID, groupID, resourceID)
func (m *managerImpl) AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID string, resource *types.Resource) (func(), error) {
err := transaction.AddResourceToGroup(ctx, accountID, groupID, resource)
if err != nil {
return nil, fmt.Errorf("error adding resource to group: %w", err)
}
event := func() {
m.accountManager.StoreEvent(ctx, accountID, groupID, accountID, activity.ResourceAddedToGroup, nil)
}
return event, nil
}
func (m *managerImpl) RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID, resourceID string) (func(), error) {
err := transaction.RemoveResourceFromGroup(ctx, accountID, groupID, resourceID)
if err != nil {
return nil, fmt.Errorf("error removing resource from group: %w", err)
}
event := func() {
m.accountManager.StoreEvent(ctx, accountID, groupID, accountID, activity.ResourceRemovedFromGroup, nil)
}
return event, nil
}
func (m *managerImpl) GetResourceGroupsInTransaction(ctx context.Context, transaction store.Store, lockingStrength store.LockingStrength, accountID, resourceID string) ([]*types.Group, error) {
@ -128,12 +157,16 @@ func (m *mockManager) AddResourceToGroup(ctx context.Context, accountID, userID,
return nil
}
func (m *mockManager) AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID string, resourceID *types.Resource) error {
return nil
func (m *mockManager) AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID string, resourceID *types.Resource) (func(), error) {
return func() {
// noop
}, nil
}
func (m *mockManager) RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID, resourceID string) error {
return nil
func (m *mockManager) RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, groupID, resourceID string) (func(), error) {
return func() {
// noop
}, nil
}
func NewManagerMock() Manager {

View File

@ -6,7 +6,10 @@ import (
"github.com/rs/xid"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/networks/resources"
"github.com/netbirdio/netbird/management/server/networks/routers"
"github.com/netbirdio/netbird/management/server/networks/types"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/status"
@ -23,15 +26,19 @@ type Manager interface {
type managerImpl struct {
store store.Store
accountManager s.AccountManager
permissionsManager permissions.Manager
resourcesManager resources.Manager
routersManager routers.Manager
}
func NewManager(store store.Store, permissionsManager permissions.Manager, manager resources.Manager) Manager {
func NewManager(store store.Store, permissionsManager permissions.Manager, resourceManager resources.Manager, routersManager routers.Manager, accountManager s.AccountManager) Manager {
return &managerImpl{
store: store,
permissionsManager: permissionsManager,
resourcesManager: manager,
resourcesManager: resourceManager,
routersManager: routersManager,
accountManager: accountManager,
}
}
@ -58,7 +65,14 @@ func (m *managerImpl) CreateNetwork(ctx context.Context, userID string, network
network.ID = xid.New().String()
return network, m.store.SaveNetwork(ctx, store.LockingStrengthUpdate, network)
err = m.store.SaveNetwork(ctx, store.LockingStrengthUpdate, network)
if err != nil {
return nil, fmt.Errorf("failed to save network: %w", err)
}
m.accountManager.StoreEvent(ctx, userID, network.ID, network.AccountID, activity.NetworkCreated, network.EventMeta())
return network, nil
}
func (m *managerImpl) GetNetwork(ctx context.Context, accountID, userID, networkID string) (*types.Network, error) {
@ -82,6 +96,13 @@ func (m *managerImpl) UpdateNetwork(ctx context.Context, userID string, network
return nil, status.NewPermissionDeniedError()
}
_, err = m.store.GetNetworkByID(ctx, store.LockingStrengthUpdate, network.AccountID, network.ID)
if err != nil {
return nil, fmt.Errorf("failed to get network: %w", err)
}
m.accountManager.StoreEvent(ctx, userID, network.ID, network.AccountID, activity.NetworkUpdated, network.EventMeta())
return network, m.store.SaveNetwork(ctx, store.LockingStrengthUpdate, network)
}
@ -94,20 +115,24 @@ func (m *managerImpl) DeleteNetwork(ctx context.Context, accountID, userID, netw
return status.NewPermissionDeniedError()
}
unlock := m.store.AcquireWriteLockByUID(ctx, accountID)
defer unlock()
network, err := m.store.GetNetworkByID(ctx, store.LockingStrengthUpdate, accountID, networkID)
if err != nil {
return fmt.Errorf("failed to get network: %w", err)
}
return m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
var eventsToStore []func()
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
resources, err := transaction.GetNetworkResourcesByNetID(ctx, store.LockingStrengthUpdate, accountID, networkID)
if err != nil {
return fmt.Errorf("failed to get resources in network: %w", err)
}
for _, resource := range resources {
err = m.resourcesManager.DeleteResourceInTransaction(ctx, transaction, accountID, networkID, resource.ID)
event, err := m.resourcesManager.DeleteResourceInTransaction(ctx, transaction, accountID, networkID, resource.ID)
if err != nil {
return fmt.Errorf("failed to delete resource: %w", err)
}
eventsToStore = append(eventsToStore, event...)
}
routers, err := transaction.GetNetworkRoutersByNetID(ctx, store.LockingStrengthUpdate, accountID, networkID)
@ -116,12 +141,33 @@ func (m *managerImpl) DeleteNetwork(ctx context.Context, accountID, userID, netw
}
for _, router := range routers {
err = transaction.DeleteNetworkRouter(ctx, store.LockingStrengthUpdate, accountID, router.ID)
event, err := m.routersManager.DeleteRouterInTransaction(ctx, transaction, accountID, networkID, router.ID)
if err != nil {
return fmt.Errorf("failed to delete router: %w", err)
}
eventsToStore = append(eventsToStore, event)
}
return transaction.DeleteNetwork(ctx, store.LockingStrengthUpdate, accountID, networkID)
})
err = transaction.DeleteNetwork(ctx, store.LockingStrengthUpdate, accountID, networkID)
if err != nil {
return fmt.Errorf("failed to delete network: %w", err)
}
eventsToStore = append(eventsToStore, func() {
m.accountManager.StoreEvent(ctx, userID, networkID, accountID, activity.NetworkDeleted, network.EventMeta())
})
return nil
})
if err != nil {
return fmt.Errorf("failed to delete network: %w", err)
}
for _, event := range eventsToStore {
event()
}
go m.accountManager.UpdateAccountPeers(ctx, accountID)
return nil
}

View File

@ -9,6 +9,7 @@ import (
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/mock_server"
"github.com/netbirdio/netbird/management/server/networks/resources"
"github.com/netbirdio/netbird/management/server/networks/routers"
"github.com/netbirdio/netbird/management/server/networks/types"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/store"
@ -27,8 +28,9 @@ func Test_GetAllNetworksReturnsNetworks(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
networks, err := manager.GetAllNetworks(ctx, accountID, userID)
require.NoError(t, err)
@ -49,8 +51,9 @@ func Test_GetAllNetworksReturnsPermissionDenied(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
networks, err := manager.GetAllNetworks(ctx, accountID, userID)
require.Error(t, err)
@ -71,8 +74,9 @@ func Test_GetNetworkReturnsNetwork(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
networks, err := manager.GetNetwork(ctx, accountID, userID, networkID)
require.NoError(t, err)
@ -93,8 +97,9 @@ func Test_GetNetworkReturnsPermissionDenied(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
network, err := manager.GetNetwork(ctx, accountID, userID, networkID)
require.Error(t, err)
@ -117,8 +122,9 @@ func Test_CreateNetworkSuccessfully(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
createdNetwork, err := manager.CreateNetwork(ctx, userID, network)
require.NoError(t, err)
@ -141,8 +147,9 @@ func Test_CreateNetworkFailsWithPermissionDenied(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
createdNetwork, err := manager.CreateNetwork(ctx, userID, network)
require.Error(t, err)
@ -163,8 +170,9 @@ func Test_DeleteNetworkSuccessfully(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
err = manager.DeleteNetwork(ctx, accountID, userID, networkID)
require.NoError(t, err)
@ -184,8 +192,9 @@ func Test_DeleteNetworkFailsWithPermissionDenied(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
err = manager.DeleteNetwork(ctx, accountID, userID, networkID)
require.Error(t, err)
@ -208,8 +217,9 @@ func Test_UpdateNetworkSuccessfully(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
updatedNetwork, err := manager.UpdateNetwork(ctx, userID, network)
require.NoError(t, err)
@ -234,8 +244,9 @@ func Test_UpdateNetworkFailsWithPermissionDenied(t *testing.T) {
am := mock_server.MockAccountManager{}
permissionsManager := permissions.NewManagerMock()
groupsManager := groups.NewManagerMock()
routerManager := routers.NewManagerMock()
resourcesManager := resources.NewManager(s, permissionsManager, groupsManager, &am)
manager := NewManager(s, permissionsManager, resourcesManager)
manager := NewManager(s, permissionsManager, resourcesManager, routerManager, &am)
updatedNetwork, err := manager.UpdateNetwork(ctx, userID, network)
require.Error(t, err)

View File

@ -6,6 +6,7 @@ import (
"fmt"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/networks/resources/types"
"github.com/netbirdio/netbird/management/server/permissions"
@ -23,7 +24,7 @@ type Manager interface {
GetResource(ctx context.Context, accountID, userID, networkID, resourceID string) (*types.NetworkResource, error)
UpdateResource(ctx context.Context, userID string, resource *types.NetworkResource) (*types.NetworkResource, error)
DeleteResource(ctx context.Context, accountID, userID, networkID, resourceID string) error
DeleteResourceInTransaction(ctx context.Context, transaction store.Store, accountID, networkID, resourceID string) error
DeleteResourceInTransaction(ctx context.Context, transaction store.Store, accountID, networkID, resourceID string) ([]func(), error)
}
type managerImpl struct {
@ -102,26 +103,38 @@ func (m *managerImpl) CreateResource(ctx context.Context, userID string, resourc
return nil, fmt.Errorf("failed to create new network resource: %w", err)
}
var eventsToStore []func()
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
_, err = transaction.GetNetworkResourceByName(ctx, store.LockingStrengthShare, resource.AccountID, resource.Name)
if err == nil {
return errors.New("resource already exists")
}
network, err := transaction.GetNetworkByID(ctx, store.LockingStrengthUpdate, resource.AccountID, resource.NetworkID)
if err != nil {
return fmt.Errorf("failed to get network: %w", err)
}
err = transaction.SaveNetworkResource(ctx, store.LockingStrengthUpdate, resource)
if err != nil {
return fmt.Errorf("failed to save network resource: %w", err)
}
event := func() {
m.accountManager.StoreEvent(ctx, userID, resource.ID, resource.AccountID, activity.NetworkResourceCreated, resource.EventMeta(network.Name))
}
eventsToStore = append(eventsToStore, event)
res := nbtypes.Resource{
ID: resource.ID,
Type: resource.Type.String(),
}
for _, groupID := range resource.GroupIDs {
err = m.groupsManager.AddResourceToGroupInTransaction(ctx, transaction, resource.AccountID, groupID, &res)
event, err := m.groupsManager.AddResourceToGroupInTransaction(ctx, transaction, resource.AccountID, groupID, &res)
if err != nil {
return fmt.Errorf("failed to add resource to group: %w", err)
}
eventsToStore = append(eventsToStore, event)
}
return nil
@ -130,6 +143,10 @@ func (m *managerImpl) CreateResource(ctx context.Context, userID string, resourc
return nil, fmt.Errorf("failed to create network resource: %w", err)
}
for _, event := range eventsToStore {
event()
}
go m.accountManager.UpdateAccountPeers(ctx, resource.AccountID)
return resource, nil
@ -174,7 +191,17 @@ func (m *managerImpl) UpdateResource(ctx context.Context, userID string, resourc
resource.Domain = domain
resource.Prefix = prefix
var eventsToStore []func()
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
network, err := transaction.GetNetworkByID(ctx, store.LockingStrengthUpdate, resource.AccountID, resource.NetworkID)
if err != nil {
return fmt.Errorf("failed to get network: %w", err)
}
if network.ID != resource.NetworkID {
return status.NewResourceNotPartOfNetworkError(resource.ID, resource.NetworkID)
}
_, err = transaction.GetNetworkResourceByID(ctx, store.LockingStrengthShare, resource.AccountID, resource.ID)
if err != nil {
return fmt.Errorf("failed to get network resource: %w", err)
@ -195,8 +222,19 @@ func (m *managerImpl) UpdateResource(ctx context.Context, userID string, resourc
return fmt.Errorf("failed to save network resource: %w", err)
}
return m.updateResourceGroups(ctx, transaction, resource, oldResource)
events, err := m.updateResourceGroups(ctx, transaction, resource, oldResource)
if err != nil {
return fmt.Errorf("failed to update resource groups: %w", err)
}
eventsToStore = append(eventsToStore, events...)
eventsToStore = append(eventsToStore, func() {
m.accountManager.StoreEvent(ctx, userID, resource.ID, resource.AccountID, activity.NetworkResourceUpdated, resource.EventMeta(network.Name))
})
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to update network resource: %w", err)
}
@ -206,7 +244,7 @@ func (m *managerImpl) UpdateResource(ctx context.Context, userID string, resourc
return resource, nil
}
func (m *managerImpl) updateResourceGroups(ctx context.Context, transaction store.Store, newResource, oldResource *types.NetworkResource) error {
func (m *managerImpl) updateResourceGroups(ctx context.Context, transaction store.Store, newResource, oldResource *types.NetworkResource) ([]func(), error) {
res := nbtypes.Resource{
ID: newResource.ID,
Type: newResource.Type.String(),
@ -214,7 +252,7 @@ func (m *managerImpl) updateResourceGroups(ctx context.Context, transaction stor
oldResourceGroups, err := m.groupsManager.GetResourceGroupsInTransaction(ctx, transaction, store.LockingStrengthUpdate, oldResource.AccountID, oldResource.ID)
if err != nil {
return fmt.Errorf("failed to get resource groups: %w", err)
return nil, fmt.Errorf("failed to get resource groups: %w", err)
}
oldGroupsIds := make([]string, 0)
@ -222,23 +260,26 @@ func (m *managerImpl) updateResourceGroups(ctx context.Context, transaction stor
oldGroupsIds = append(oldGroupsIds, group.ID)
}
var eventsToStore []func()
groupsToAdd := util.Difference(newResource.GroupIDs, oldGroupsIds)
for _, groupID := range groupsToAdd {
err = m.groupsManager.AddResourceToGroupInTransaction(ctx, transaction, newResource.AccountID, groupID, &res)
events, err := m.groupsManager.AddResourceToGroupInTransaction(ctx, transaction, newResource.AccountID, groupID, &res)
if err != nil {
return fmt.Errorf("failed to add resource to group: %w", err)
return nil, fmt.Errorf("failed to add resource to group: %w", err)
}
eventsToStore = append(eventsToStore, events)
}
groupsToRemove := util.Difference(oldGroupsIds, newResource.GroupIDs)
for _, groupID := range groupsToRemove {
err = m.groupsManager.RemoveResourceFromGroupInTransaction(ctx, transaction, newResource.AccountID, groupID, res.ID)
events, err := m.groupsManager.RemoveResourceFromGroupInTransaction(ctx, transaction, newResource.AccountID, groupID, res.ID)
if err != nil {
return fmt.Errorf("failed to add resource to group: %w", err)
return nil, fmt.Errorf("failed to add resource to group: %w", err)
}
eventsToStore = append(eventsToStore, events)
}
return nil
return eventsToStore, nil
}
func (m *managerImpl) DeleteResource(ctx context.Context, accountID, userID, networkID, resourceID string) error {
@ -253,44 +294,68 @@ func (m *managerImpl) DeleteResource(ctx context.Context, accountID, userID, net
unlock := m.store.AcquireWriteLockByUID(ctx, accountID)
defer unlock()
var events []func()
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
return m.DeleteResourceInTransaction(ctx, transaction, accountID, networkID, resourceID)
events, err = m.DeleteResourceInTransaction(ctx, transaction, accountID, networkID, resourceID)
return err
})
if err != nil {
return fmt.Errorf("failed to delete network resource: %w", err)
}
for _, event := range events {
event()
}
go m.accountManager.UpdateAccountPeers(ctx, accountID)
return nil
}
func (m *managerImpl) DeleteResourceInTransaction(ctx context.Context, transaction store.Store, accountID, networkID, resourceID string) error {
func (m *managerImpl) DeleteResourceInTransaction(ctx context.Context, transaction store.Store, accountID, networkID, resourceID string) ([]func(), error) {
resource, err := transaction.GetNetworkResourceByID(ctx, store.LockingStrengthUpdate, accountID, resourceID)
if err != nil {
return fmt.Errorf("failed to get network resource: %w", err)
return nil, fmt.Errorf("failed to get network resource: %w", err)
}
network, err := transaction.GetNetworkByID(ctx, store.LockingStrengthUpdate, accountID, networkID)
if err != nil {
return nil, fmt.Errorf("failed to get network: %w", err)
}
if resource.NetworkID != networkID {
return errors.New("resource not part of network")
return nil, errors.New("resource not part of network")
}
groups, err := m.groupsManager.GetResourceGroupsInTransaction(ctx, transaction, store.LockingStrengthUpdate, accountID, resourceID)
if err != nil {
return fmt.Errorf("failed to get resource groups: %w", err)
return nil, fmt.Errorf("failed to get resource groups: %w", err)
}
var eventsToStore []func()
for _, group := range groups {
err = m.groupsManager.RemoveResourceFromGroupInTransaction(ctx, transaction, accountID, group.ID, resourceID)
event, err := m.groupsManager.RemoveResourceFromGroupInTransaction(ctx, transaction, accountID, group.ID, resourceID)
if err != nil {
return fmt.Errorf("failed to remove resource from group: %w", err)
return nil, fmt.Errorf("failed to remove resource from group: %w", err)
}
eventsToStore = append(eventsToStore, event)
}
err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID)
if err != nil {
return fmt.Errorf("failed to increment network serial: %w", err)
return nil, fmt.Errorf("failed to increment network serial: %w", err)
}
return transaction.DeleteNetworkResource(ctx, store.LockingStrengthUpdate, accountID, resourceID)
err = transaction.DeleteNetworkResource(ctx, store.LockingStrengthUpdate, accountID, resourceID)
if err != nil {
return nil, fmt.Errorf("failed to delete network resource: %w", err)
}
eventsToStore = append(eventsToStore, func() {
m.accountManager.StoreEvent(ctx, accountID, resourceID, accountID, activity.NetworkResourceDeleted, resource.EventMeta(network.Name))
})
return eventsToStore, nil
}

View File

@ -142,6 +142,10 @@ func (n *NetworkResource) ToRoute(peer *nbpeer.Peer, router *routerTypes.Network
return r
}
func (n *NetworkResource) EventMeta(networkName string) map[string]any {
return map[string]any{"name": n.Name, "type": n.Type, "network_name": networkName}
}
// GetResourceType returns the type of the resource based on the address
func GetResourceType(address string) (NetworkResourceType, string, netip.Prefix, error) {
if prefix, err := netip.ParsePrefix(address); err == nil {

View File

@ -8,7 +8,9 @@ import (
"github.com/rs/xid"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/networks/routers/types"
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/store"
@ -21,6 +23,7 @@ type Manager interface {
GetRouter(ctx context.Context, accountID, userID, networkID, routerID string) (*types.NetworkRouter, error)
UpdateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error)
DeleteRouter(ctx context.Context, accountID, userID, networkID, routerID string) error
DeleteRouterInTransaction(ctx context.Context, transaction store.Store, accountID, networkID, routerID string) (func(), error)
}
type managerImpl struct {
@ -29,6 +32,9 @@ type managerImpl struct {
accountManager s.AccountManager
}
type mockManager struct {
}
func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager s.AccountManager) Manager {
return &managerImpl{
store: store,
@ -80,13 +86,32 @@ func (m *managerImpl) CreateRouter(ctx context.Context, userID string, router *t
return nil, status.NewPermissionDeniedError()
}
var network *networkTypes.Network
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
network, err = transaction.GetNetworkByID(ctx, store.LockingStrengthShare, router.AccountID, router.NetworkID)
if err != nil {
return fmt.Errorf("failed to get network: %w", err)
}
if network.ID != router.NetworkID {
return status.NewNetworkNotFoundError(router.NetworkID)
}
router.ID = xid.New().String()
err = m.store.SaveNetworkRouter(ctx, store.LockingStrengthUpdate, router)
err = transaction.SaveNetworkRouter(ctx, store.LockingStrengthUpdate, router)
if err != nil {
return nil, fmt.Errorf("failed to create network router: %w", err)
return fmt.Errorf("failed to create network router: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
m.accountManager.StoreEvent(ctx, userID, router.ID, router.AccountID, activity.NetworkRouterCreated, router.EventMeta(network.Name))
go m.accountManager.UpdateAccountPeers(ctx, router.AccountID)
return router, nil
@ -122,11 +147,30 @@ func (m *managerImpl) UpdateRouter(ctx context.Context, userID string, router *t
return nil, status.NewPermissionDeniedError()
}
err = m.store.SaveNetworkRouter(ctx, store.LockingStrengthUpdate, router)
var network *networkTypes.Network
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
network, err = transaction.GetNetworkByID(ctx, store.LockingStrengthShare, router.AccountID, router.NetworkID)
if err != nil {
return nil, fmt.Errorf("failed to update network router: %w", err)
return fmt.Errorf("failed to get network: %w", err)
}
if network.ID != router.NetworkID {
return status.NewRouterNotPartOfNetworkError(router.ID, router.NetworkID)
}
err = transaction.SaveNetworkRouter(ctx, store.LockingStrengthUpdate, router)
if err != nil {
return fmt.Errorf("failed to update network router: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
m.accountManager.StoreEvent(ctx, userID, router.ID, router.AccountID, activity.NetworkRouterUpdated, router.EventMeta(network.Name))
go m.accountManager.UpdateAccountPeers(ctx, router.AccountID)
return router, nil
@ -141,12 +185,77 @@ func (m *managerImpl) DeleteRouter(ctx context.Context, accountID, userID, netwo
return status.NewPermissionDeniedError()
}
err = m.store.DeleteNetworkRouter(ctx, store.LockingStrengthUpdate, accountID, routerID)
var event func()
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
event, err = m.DeleteRouterInTransaction(ctx, transaction, accountID, networkID, routerID)
return err
})
if err != nil {
return fmt.Errorf("failed to delete network router: %w", err)
return err
}
event()
go m.accountManager.UpdateAccountPeers(ctx, accountID)
return nil
}
func (m *managerImpl) DeleteRouterInTransaction(ctx context.Context, transaction store.Store, accountID, networkID, routerID string) (func(), error) {
network, err := transaction.GetNetworkByID(ctx, store.LockingStrengthShare, accountID, networkID)
if err != nil {
return nil, fmt.Errorf("failed to get network: %w", err)
}
router, err := transaction.GetNetworkRouterByID(ctx, store.LockingStrengthUpdate, accountID, routerID)
if err != nil {
return nil, fmt.Errorf("failed to get network router: %w", err)
}
if router.NetworkID != networkID {
return nil, status.NewRouterNotPartOfNetworkError(routerID, networkID)
}
err = transaction.DeleteNetworkRouter(ctx, store.LockingStrengthUpdate, accountID, routerID)
if err != nil {
return nil, fmt.Errorf("failed to delete network router: %w", err)
}
event := func() {
m.accountManager.StoreEvent(ctx, "", routerID, accountID, activity.NetworkRouterDeleted, router.EventMeta(network.Name))
}
return event, nil
}
func NewManagerMock() Manager {
return &mockManager{}
}
func (m *mockManager) GetAllRoutersInNetwork(ctx context.Context, accountID, userID, networkID string) ([]*types.NetworkRouter, error) {
return []*types.NetworkRouter{}, nil
}
func (m *mockManager) GetAllRoutersInAccount(ctx context.Context, accountID, userID string) (map[string][]*types.NetworkRouter, error) {
return map[string][]*types.NetworkRouter{}, nil
}
func (m *mockManager) CreateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error) {
return router, nil
}
func (m *mockManager) GetRouter(ctx context.Context, accountID, userID, networkID, routerID string) (*types.NetworkRouter, error) {
return &types.NetworkRouter{}, nil
}
func (m *mockManager) UpdateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error) {
return router, nil
}
func (m *mockManager) DeleteRouter(ctx context.Context, accountID, userID, networkID, routerID string) error {
return nil
}
func (m *mockManager) DeleteRouterInTransaction(ctx context.Context, transaction store.Store, accountID, networkID, routerID string) (func(), error) {
return func() {}, nil
}

View File

@ -68,3 +68,7 @@ func (n *NetworkRouter) Copy() *NetworkRouter {
Metric: n.Metric,
}
}
func (n *NetworkRouter) EventMeta(networkName string) map[string]any {
return map[string]any{"network_name": networkName}
}

View File

@ -49,3 +49,7 @@ func (n *Network) Copy() *Network {
Description: n.Description,
}
}
func (n *Network) EventMeta() map[string]any {
return map[string]any{"name": n.Name}
}

View File

@ -178,3 +178,11 @@ func NewPermissionDeniedError() error {
func NewPermissionValidationError(err error) error {
return Errorf(PermissionDenied, "failed to vlidate user permissions: %s", err)
}
func NewResourceNotPartOfNetworkError(resourceID, networkID string) error {
return Errorf(BadRequest, "resource %s is not part of the network %s", resourceID, networkID)
}
func NewRouterNotPartOfNetworkError(routerID, networkID string) error {
return Errorf(BadRequest, "router %s is not part of the network %s", routerID, networkID)
}