From 8cf7b07be77ad5d8ea5766f20a232213ceb2c09e Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Tue, 27 May 2025 14:24:25 +0200 Subject: [PATCH] add networks structure --- go.mod | 2 +- .../controllers/network_map/updatechannel.go | 7 +- internal/modules/networks/interface.go | 16 ++ internal/modules/networks/manager/api.go | 1 + internal/modules/networks/manager/manager.go | 145 +++++++++++++++ internal/modules/networks/manager/metrics.go | 1 + .../modules/networks/manager/repository.go | 57 ++++++ internal/modules/networks/network.go | 55 ++++++ internal/modules/networks/networkevent.go | 1 + .../modules/networks/resources/interface.go | 12 ++ .../modules/networks/resources/manager/api.go | 1 + .../networks/resources/manager/manager.go | 1 + .../networks/resources/manager/metrics.go | 1 + .../networks/resources/manager/repository.go | 61 ++++++ internal/modules/networks/resources/types.go | 173 ++++++++++++++++++ .../modules/networks/routers/interface.go | 13 ++ .../modules/networks/routers/manager/api.go | 1 + .../networks/routers/manager/manager.go | 1 + .../networks/routers/manager/metrics.go | 1 + .../networks/routers/manager/repository.go | 1 + internal/modules/networks/routers/types.go | 80 ++++++++ internal/modules/peers/manager.go | 89 ++------- internal/modules/peers/repository.go | 5 + internal/shared/db/store.go | 12 +- internal/shared/db/transaction.go | 24 ++- 25 files changed, 675 insertions(+), 86 deletions(-) create mode 100644 internal/modules/networks/interface.go create mode 100644 internal/modules/networks/manager/api.go create mode 100644 internal/modules/networks/manager/manager.go create mode 100644 internal/modules/networks/manager/metrics.go create mode 100644 internal/modules/networks/manager/repository.go create mode 100644 internal/modules/networks/network.go create mode 100644 internal/modules/networks/networkevent.go create mode 100644 internal/modules/networks/resources/interface.go create mode 100644 internal/modules/networks/resources/manager/api.go create mode 100644 internal/modules/networks/resources/manager/manager.go create mode 100644 internal/modules/networks/resources/manager/metrics.go create mode 100644 internal/modules/networks/resources/manager/repository.go create mode 100644 internal/modules/networks/resources/types.go create mode 100644 internal/modules/networks/routers/interface.go create mode 100644 internal/modules/networks/routers/manager/api.go create mode 100644 internal/modules/networks/routers/manager/manager.go create mode 100644 internal/modules/networks/routers/manager/metrics.go create mode 100644 internal/modules/networks/routers/manager/repository.go create mode 100644 internal/modules/networks/routers/types.go diff --git a/go.mod b/go.mod index 8aad61e..e1152da 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/stretchr/testify v1.10.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 google.golang.org/grpc v1.67.3 + gopkg.in/natefinch/lumberjack.v2 v2.0.0 gorm.io/driver/postgres v1.5.11 gorm.io/driver/sqlite v1.5.7 gorm.io/gorm v1.25.12 @@ -166,7 +167,6 @@ require ( google.golang.org/api v0.215.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect google.golang.org/protobuf v1.36.1 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/mysql v1.5.7 // indirect diff --git a/internal/controllers/network_map/updatechannel.go b/internal/controllers/network_map/updatechannel.go index 1b40b3e..7789d1c 100644 --- a/internal/controllers/network_map/updatechannel.go +++ b/internal/controllers/network_map/updatechannel.go @@ -15,8 +15,11 @@ import ( const channelBufferSize = 100 type UpdateMessage struct { - Update *proto.SyncResponse - NetworkMap *types.NetworkMap + Update *proto.SyncResponse + NetworkMap *types.NetworkMap + PeerManager *nbpeer.Manager + PolicyManager *nbpeer.PolicyManager + GroupManager *nbpeer.GroupManager } type UpdateChannel struct { diff --git a/internal/modules/networks/interface.go b/internal/modules/networks/interface.go new file mode 100644 index 0000000..aea8c34 --- /dev/null +++ b/internal/modules/networks/interface.go @@ -0,0 +1,16 @@ +package networks + +import ( + "context" + + "management/internal/shared/db" +) + +type Manager interface { + Using(tx db.Transaction) Manager + GetAllNetworks(ctx context.Context, strength db.LockingStrength, accountID, userID string) ([]*Network, error) + GetNetwork(ctx context.Context, strength db.LockingStrength, accountID, userID, networkID string) (*Network, error) + CreateNetwork(ctx context.Context, userID string, network *Network) (*Network, error) + UpdateNetwork(ctx context.Context, userID string, network *Network) (*Network, error) + DeleteNetwork(ctx context.Context, accountID, userID, networkID string) error +} diff --git a/internal/modules/networks/manager/api.go b/internal/modules/networks/manager/api.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/manager/api.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/manager/manager.go b/internal/modules/networks/manager/manager.go new file mode 100644 index 0000000..7ea4d87 --- /dev/null +++ b/internal/modules/networks/manager/manager.go @@ -0,0 +1,145 @@ +package manager + +import ( + "context" + "fmt" + + "github.com/netbirdio/netbird/management/server/account" + "github.com/rs/xid" + + "management/internal/modules/networks" + "management/internal/modules/networks/resources" + "management/internal/modules/networks/routers" + "management/internal/shared/db" + "management/internal/shared/errors" + "management/internal/shared/permissions" + "management/internal/shared/permissions/modules" + "management/internal/shared/permissions/operations" +) + +type managerImpl struct { + repo Repository + permissionsManager permissions.Manager + resourcesManager resources.Manager + routersManager routers.Manager +} + +func NewManager(store *db.Store, permissionsManager permissions.Manager, resourceManager resources.Manager, routersManager routers.Manager) resources.Manager { + repo := newRepository(store) + m := &managerImpl{repo: repo} + // api := newHandler(m, permissionsManager) + // api.RegisterEndpoints(router) + return m +} + +func (m *managerImpl) GetAllNetworks(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, userID string) ([]*networks.Network, error) { + ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read) + if err != nil { + return nil, errors.NewPermissionValidationError(err) + } + if !ok { + return nil, errors.NewPermissionDeniedError() + } + + return m.repo.GetAccountNetworks(tx, strength, accountID) +} + +func (m *managerImpl) CreateNetwork(ctx context.Context, tx db.Transaction, userID string, network *networks.Network) (*networks.Network, error) { + ok, err := m.permissionsManager.ValidateUserPermissions(ctx, network.AccountID, userID, modules.Networks, operations.Write) + if err != nil { + return nil, errors.NewPermissionValidationError(err) + } + if !ok { + return nil, errors.NewPermissionDeniedError() + } + + network.ID = xid.New().String() + + err = m.repo.CreateNetwork(tx, 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, tx db.Transaction, strength db.LockingStrength, accountID, userID, networkID string) (*networks.Network, error) { + ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read) + if err != nil { + return nil, errors.NewPermissionValidationError(err) + } + if !ok { + return nil, errors.NewPermissionDeniedError() + } + + return m.repo.GetNetworkByID(tx, strength, accountID, networkID) +} + +func (m *managerImpl) UpdateNetwork(ctx context.Context, tx db.Transaction, userID string, network *networks.Network) (*networks.Network, error) { + ok, err := m.permissionsManager.ValidateUserPermissions(ctx, network.AccountID, userID, modules.Networks, operations.Write) + if err != nil { + return nil, errors.NewPermissionValidationError(err) + } + if !ok { + return nil, errors.NewPermissionDeniedError() + } + + _, err = m.repo.GetNetworkByID(tx, db.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.repo.UpdateNetwork(tx, network) +} + +func (m *managerImpl) DeleteNetwork(ctx context.Context, tx db.Transaction, accountID, userID, networkID string) error { + ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Write) + if err != nil { + return errors.NewPermissionValidationError(err) + } + if !ok { + return errors.NewPermissionDeniedError() + } + + network, err := m.repo.GetNetworkByID(tx, db.LockingStrengthUpdate, accountID, networkID) + if err != nil { + return fmt.Errorf("failed to get network: %w", err) + } + + resources, err := m.resourcesManager.GetNetworkResourcesByNetID(ctx, tx, db.LockingStrengthUpdate, accountID, userID, networkID) + if err != nil { + return fmt.Errorf("failed to get resources in network: %w", err) + } + + for _, resource := range resources { + err = m.resourcesManager.DeleteResource(ctx, tx, accountID, userID, networkID, resource.ID) + if err != nil { + return fmt.Errorf("failed to delete resource: %w", err) + } + } + + routers, err := m.routersManager.GetNetworkRoutersByNetID(ctx, tx, db.LockingStrengthUpdate, accountID, userID, networkID) + if err != nil { + return fmt.Errorf("failed to get routers in network: %w", err) + } + + for _, router := range routers { + err := m.routersManager.DeleteRouter(ctx, tx, accountID, userID, networkID, router.ID) + if err != nil { + return fmt.Errorf("failed to delete router: %w", err) + } + } + + err = m.repo.DeleteNetwork(tx, &networks.Network{ID: networkID}) + if err != nil { + return fmt.Errorf("failed to delete network: %w", err) + } + + // TODO: send delete event with network + + return nil +} diff --git a/internal/modules/networks/manager/metrics.go b/internal/modules/networks/manager/metrics.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/manager/metrics.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/manager/repository.go b/internal/modules/networks/manager/repository.go new file mode 100644 index 0000000..5c89fd5 --- /dev/null +++ b/internal/modules/networks/manager/repository.go @@ -0,0 +1,57 @@ +package manager + +import ( + "management/internal/modules/networks" + "management/internal/shared/db" +) + +type Repository interface { + RunInTx(fn func(tx db.Transaction) error) error + CreateNetwork(tx db.Transaction, network *networks.Network) error + UpdateNetwork(tx db.Transaction, network *networks.Network) error + DeleteNetwork(tx db.Transaction, network *networks.Network) error + GetNetworkByID(tx db.Transaction, lockingStrength db.LockingStrength, accountID, networkID string) (*networks.Network, error) + GetAccountNetworks(tx db.Transaction, lockingStrength db.LockingStrength, accountID string) ([]*networks.Network, error) +} + +type repository struct { + store *db.Store +} + +func newRepository(s *db.Store) Repository { + return &repository{store: s} +} + +func (r *repository) RunInTx(fn func(tx db.Transaction) error) error { + return r.store.RunInTx(fn) +} + +func (r *repository) CreateNetwork(tx db.Transaction, network *networks.Network) error { + return r.store.Create(tx, network) +} + +func (r *repository) UpdateNetwork(tx db.Transaction, network *networks.Network) error { + return r.store.Update(tx, network) +} + +func (r *repository) DeleteNetwork(tx db.Transaction, network *networks.Network) error { + return r.store.Delete(tx, network) +} + +func (r *repository) GetNetworkByID(tx db.Transaction, lockingStrength db.LockingStrength, accountID, networkID string) (*networks.Network, error) { + var network networks.Network + err := r.store.GetOne(tx, lockingStrength, &network, "account_id = ? AND network_id = ?", accountID, networkID) + if err != nil { + return nil, err + } + return &network, nil +} + +func (r *repository) GetAccountNetworks(tx db.Transaction, lockingStrength db.LockingStrength, accountID string) ([]*networks.Network, error) { + var networks []*networks.Network + err := r.store.GetMany(tx, lockingStrength, &networks, "account_id = ?", accountID) + if err != nil { + return nil, err + } + return networks, nil +} diff --git a/internal/modules/networks/network.go b/internal/modules/networks/network.go new file mode 100644 index 0000000..ac30245 --- /dev/null +++ b/internal/modules/networks/network.go @@ -0,0 +1,55 @@ +package networks + +import ( + "github.com/netbirdio/netbird/management/server/http/api" + "github.com/rs/xid" +) + +type Network struct { + ID string + AccountID string + Name string + Description string +} + +func NewNetwork(accountId, name, description string) *Network { + return &Network{ + ID: xid.New().String(), + AccountID: accountId, + Name: name, + Description: description, + } +} + +func (n *Network) ToAPIResponse(routerIDs []string, resourceIDs []string, routingPeersCount int, policyIDs []string) *api.Network { + return &api.Network{ + Id: n.ID, + Name: n.Name, + Description: &n.Description, + Routers: routerIDs, + Resources: resourceIDs, + RoutingPeersCount: routingPeersCount, + Policies: policyIDs, + } +} + +func (n *Network) FromAPIRequest(req *api.NetworkRequest) { + n.Name = req.Name + if req.Description != nil { + n.Description = *req.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, + } +} + +func (n *Network) EventMeta() map[string]any { + return map[string]any{"name": n.Name} +} diff --git a/internal/modules/networks/networkevent.go b/internal/modules/networks/networkevent.go new file mode 100644 index 0000000..83c4a6a --- /dev/null +++ b/internal/modules/networks/networkevent.go @@ -0,0 +1 @@ +package networks diff --git a/internal/modules/networks/resources/interface.go b/internal/modules/networks/resources/interface.go new file mode 100644 index 0000000..cc5697e --- /dev/null +++ b/internal/modules/networks/resources/interface.go @@ -0,0 +1,12 @@ +package resources + +import ( + "context" + + "management/internal/shared/db" +) + +type Manager interface { + DeleteResource(ctx context.Context, tx db.Transaction, accountID, userID, networkID, resourceID string) error + GetNetworkResourcesByNetID(ctx context.Context, tx db.Transaction, lockingStrength db.LockingStrength, accountID, userID, networkID string) ([]*NetworkResource, error) +} diff --git a/internal/modules/networks/resources/manager/api.go b/internal/modules/networks/resources/manager/api.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/resources/manager/api.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/resources/manager/manager.go b/internal/modules/networks/resources/manager/manager.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/resources/manager/manager.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/resources/manager/metrics.go b/internal/modules/networks/resources/manager/metrics.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/resources/manager/metrics.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/resources/manager/repository.go b/internal/modules/networks/resources/manager/repository.go new file mode 100644 index 0000000..bdd0a54 --- /dev/null +++ b/internal/modules/networks/resources/manager/repository.go @@ -0,0 +1,61 @@ +package manager + +import ( + "management/internal/modules/networks" + "management/internal/shared/db" +) + +type Repository interface { + RunInTx(fn func(tx db.Transaction) error) error + Using(tx db.Transaction) Repository + CreateNetwork(tx db.Transaction, network *networks.Network) error + UpdateNetwork(tx db.Transaction, network *networks.Network) error + GetNetworkByID(tx db.Transaction, lockingStrength db.LockingStrength, accountID, networkID string) (*networks.Network, error) + GetAccountNetworks(tx db.Transaction, lockingStrength db.LockingStrength, accountID string) ([]*networks.Network, error) +} + +type repository struct { + store *db.Store +} + +func newRepository(s *db.Store) Repository { + return &repository{store: s} +} + +func (r *repository) RunInTx(fn func(tx db.Transaction) error) error { + return r.store.RunInTx(fn) +} + +func (r *repository) Using(tx db.Transaction) Repository { + return &repository{store: r.store.Using(tx)} +} + +func (r *repository) DeleteNetwork(tx db.Transaction, network *networks.Network) error { + return r.store.Create(tx, network) +} + +func (r *repository) UpdateNetwork(tx db.Transaction, network *networks.Network) error { + return r.store.Update(tx, network) +} + +func (r *repository) GetNetworkByID(tx db.Transaction, lockingStrength db.LockingStrength, accountID, networkID string) (*networks.Network, error) { + var network networks.Network + err := r.store.GetOne(tx, lockingStrength, &network, "account_id = ? AND network_id = ?", accountID, networkID) + if err != nil { + return nil, err + } + return &network, nil +} + +func (r *repository) GetAccountNetworks(tx db.Transaction, lockingStrength db.LockingStrength, accountID string) ([]*networks.Network, error) { + var networks []*networks.Network + err := r.store.GetMany(tx, lockingStrength, &networks, "account_id = ?", accountID) + if err != nil { + return nil, err + } + return networks, nil +} + +func (r *repository) CreateNetwork(tx db.Transaction, network *networks.Network) error { + return r.store.Create(tx, network) +} diff --git a/internal/modules/networks/resources/types.go b/internal/modules/networks/resources/types.go new file mode 100644 index 0000000..2aaa378 --- /dev/null +++ b/internal/modules/networks/resources/types.go @@ -0,0 +1,173 @@ +package resources + +import ( + "errors" + "fmt" + "net/netip" + "regexp" + + "github.com/netbirdio/netbird/management/server/http/api" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/route" + "github.com/rs/xid" + + "management/internal/modules/networks" + "management/internal/modules/networks/routers" +) + +type NetworkResourceType string + +const ( + Host NetworkResourceType = "host" + Subnet NetworkResourceType = "subnet" + Domain NetworkResourceType = "domain" +) + +func (p NetworkResourceType) String() string { + return string(p) +} + +type NetworkResource struct { + ID string `gorm:"primaryKey"` + NetworkID string `gorm:"index"` + AccountID string `gorm:"index"` + Name string + Description string + Type NetworkResourceType + Address string `gorm:"-"` + GroupIDs []string `gorm:"-"` + Domain string + Prefix netip.Prefix `gorm:"serializer:json"` + Enabled bool +} + +func NewNetworkResource(accountID, networkID, name, description, address string, groupIDs []string, enabled bool) (*NetworkResource, error) { + resourceType, domain, prefix, err := GetResourceType(address) + if err != nil { + return nil, fmt.Errorf("invalid address: %w", err) + } + + return &NetworkResource{ + ID: xid.New().String(), + AccountID: accountID, + NetworkID: networkID, + Name: name, + Description: description, + Type: resourceType, + Address: address, + Domain: domain, + Prefix: prefix, + GroupIDs: groupIDs, + Enabled: enabled, + }, nil +} + +func (n *NetworkResource) ToAPIResponse(groups []api.GroupMinimum) *api.NetworkResource { + addr := n.Prefix.String() + if n.Type == Domain { + addr = n.Domain + } + + return &api.NetworkResource{ + Id: n.ID, + Name: n.Name, + Description: &n.Description, + Type: api.NetworkResourceType(n.Type.String()), + Address: addr, + Groups: groups, + Enabled: n.Enabled, + } +} + +func (n *NetworkResource) FromAPIRequest(req *api.NetworkResourceRequest) { + n.Name = req.Name + + if req.Description != nil { + n.Description = *req.Description + } + n.Address = req.Address + n.GroupIDs = req.Groups + n.Enabled = req.Enabled +} + +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, + Domain: n.Domain, + Prefix: n.Prefix, + GroupIDs: n.GroupIDs, + Enabled: n.Enabled, + } +} + +func (n *NetworkResource) ToRoute(peer *nbpeer.Peer, router *routers.NetworkRouter) *route.Route { + r := &route.Route{ + ID: route.ID(fmt.Sprintf("%s:%s", n.ID, peer.ID)), + AccountID: n.AccountID, + KeepRoute: true, + NetID: route.NetID(n.Name), + Description: n.Description, + Peer: peer.Key, + PeerID: peer.ID, + PeerGroups: nil, + Masquerade: router.Masquerade, + Metric: router.Metric, + Enabled: n.Enabled, + Groups: nil, + AccessControlGroups: nil, + } + + if n.Type == Host || n.Type == Subnet { + r.Network = n.Prefix + + r.NetworkType = route.IPv4Network + if n.Prefix.Addr().Is6() { + r.NetworkType = route.IPv6Network + } + } + + if n.Type == Domain { + // domainList, err := nbDomain.FromStringList([]string{n.Domain}) + // if err != nil { + // return nil + // } + // r.Domains = domainList + r.NetworkType = route.DomainNetwork + + // add default placeholder for domain network + r.Network = netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 0, 2, 0}), 32) + } + + return r +} + +func (n *NetworkResource) EventMeta(network *networks.Network) map[string]any { + return map[string]any{"name": n.Name, "type": n.Type, "network_name": network.Name, "network_id": network.ID} +} + +// 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 { + if prefix.Bits() == 32 || prefix.Bits() == 128 { + return Host, "", prefix, nil + } + return Subnet, "", prefix, nil + } + + if ip, err := netip.ParseAddr(address); err == nil { + return Host, "", netip.PrefixFrom(ip, ip.BitLen()), nil + } + + domainRegex := regexp.MustCompile(`^(\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`) + if domainRegex.MatchString(address) { + return Domain, address, netip.Prefix{}, nil + } + + return "", "", netip.Prefix{}, errors.New("not a valid host, subnet, or domain") +} diff --git a/internal/modules/networks/routers/interface.go b/internal/modules/networks/routers/interface.go new file mode 100644 index 0000000..0d7487b --- /dev/null +++ b/internal/modules/networks/routers/interface.go @@ -0,0 +1,13 @@ +package routers + +import ( + "context" + + "management/internal/shared/db" +) + +type Manager interface { + Using(tx db.Transaction) Manager + GetNetworkRoutersByNetID(ctx context.Context, tx db.Transaction, lockingStrength db.LockingStrength, accountID, userID, networkID string) ([]*NetworkRouter, error) + DeleteRouter(ctx context.Context, tx db.Transaction, accountID, userID, networkID, routerID string) error +} diff --git a/internal/modules/networks/routers/manager/api.go b/internal/modules/networks/routers/manager/api.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/routers/manager/api.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/routers/manager/manager.go b/internal/modules/networks/routers/manager/manager.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/routers/manager/manager.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/routers/manager/metrics.go b/internal/modules/networks/routers/manager/metrics.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/routers/manager/metrics.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/routers/manager/repository.go b/internal/modules/networks/routers/manager/repository.go new file mode 100644 index 0000000..5d04392 --- /dev/null +++ b/internal/modules/networks/routers/manager/repository.go @@ -0,0 +1 @@ +package manager diff --git a/internal/modules/networks/routers/types.go b/internal/modules/networks/routers/types.go new file mode 100644 index 0000000..da73742 --- /dev/null +++ b/internal/modules/networks/routers/types.go @@ -0,0 +1,80 @@ +package routers + +import ( + "errors" + + "github.com/netbirdio/netbird/management/server/http/api" + "github.com/rs/xid" + + "management/internal/modules/networks" +) + +type NetworkRouter struct { + ID string `gorm:"primaryKey"` + NetworkID string `gorm:"index"` + AccountID string `gorm:"index"` + Peer string + PeerGroups []string `gorm:"serializer:json"` + Masquerade bool + Metric int + Enabled bool +} + +func NewNetworkRouter(accountID string, networkID string, peer string, peerGroups []string, masquerade bool, metric int, enabled bool) (*NetworkRouter, error) { + if peer != "" && len(peerGroups) > 0 { + return nil, errors.New("peer and peerGroups cannot be set at the same time") + } + + return &NetworkRouter{ + ID: xid.New().String(), + AccountID: accountID, + NetworkID: networkID, + Peer: peer, + PeerGroups: peerGroups, + Masquerade: masquerade, + Metric: metric, + Enabled: enabled, + }, nil +} + +func (n *NetworkRouter) ToAPIResponse() *api.NetworkRouter { + return &api.NetworkRouter{ + Id: n.ID, + Peer: &n.Peer, + PeerGroups: &n.PeerGroups, + Masquerade: n.Masquerade, + Metric: n.Metric, + Enabled: n.Enabled, + } +} + +func (n *NetworkRouter) FromAPIRequest(req *api.NetworkRouterRequest) { + if req.Peer != nil { + n.Peer = *req.Peer + } + + if req.PeerGroups != nil { + n.PeerGroups = *req.PeerGroups + } + + n.Masquerade = req.Masquerade + n.Metric = req.Metric + n.Enabled = req.Enabled +} + +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, + Enabled: n.Enabled, + } +} + +func (n *NetworkRouter) EventMeta(network *networks.Network) map[string]any { + return map[string]any{"network_name": network.Name, "network_id": network.ID, "peer": n.Peer, "peer_groups": n.PeerGroups} +} diff --git a/internal/modules/peers/manager.go b/internal/modules/peers/manager.go index be5af1d..6179904 100644 --- a/internal/modules/peers/manager.go +++ b/internal/modules/peers/manager.go @@ -6,6 +6,7 @@ import ( "github.com/gorilla/mux" "management/internal/modules/peers/types" + "management/internal/shared/activity" "management/internal/shared/db" "management/internal/shared/permissions" "management/pkg/logging" @@ -14,7 +15,8 @@ import ( var log = logging.LoggerForThisPackage() type Manager struct { - repo Repository + repo Repository + eventManager *activity.Manager } func NewManager(store *db.Store, router *mux.Router, permissionsManager permissions.Manager) *Manager { @@ -37,76 +39,21 @@ func (m *Manager) GetFilteredPeers(ctx context.Context, tx db.Transaction, stren return m.repo.GetFilteredPeers(tx, strength, accountID, nameFilter, ipFilter) } -import ( - "context" +func (m *Manager) UpdatePeer(ctx context.Context, tx db.Transaction, peer *types.Peer) error { + validateInput + validatePermissions + err := m.repo.RunInTx(func(tx db.Transaction) error { + othermanager.UpdatePeers + ourmanager.UpdateGroup + }) + if err != nil { + return err + } - "github.com/gorilla/mux" + err := sendPeerUpdateEvent(tx, peer) // -> goes to peerUpdtaeChannel + if err != nil { + log.Errorf("Failed to send peer update event: %v", err) + } - "management/internal/modules/peers/types" - "management/internal/shared/db" - "management/internal/shared/permissions" - "management/pkg/logging" -) - -var log = logging.LoggerForThisPackage() - -type Manager struct { - repo Repository -} - -func NewManager(store *db.Store, router *mux.Router, permissionsManager permissions.Manager) *Manager { - repo := newRepository(store) - m := &Manager{repo: repo} - api := newHandler(m, permissionsManager) - api.RegisterEndpoints(router) - return m -} - -func (m *Manager) GetPeer(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, peerID string) (*types.Peer, error) { - return m.repo.GetPeerByID(tx, strength, accountID, peerID) -} - -func (m *Manager) GetPeers(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID string) ([]*types.Peer, error) { - return m.repo.GetPeers(tx, strength, accountID) -} - -func (m *Manager) GetFilteredPeers(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, nameFilter, ipFilter string) ([]*types.Peer, error) { - return m.repo.GetFilteredPeers(tx, strength, accountID, nameFilter, ipFilter) -} - -import ( - "context" - - "github.com/gorilla/mux" - - "management/internal/modules/peers/types" - "management/internal/shared/db" - "management/internal/shared/permissions" - "management/pkg/logging" -) - -var log = logging.LoggerForThisPackage() - -type Manager struct { - repo Repository -} - -func NewManager(store *db.Store, router *mux.Router, permissionsManager permissions.Manager) *Manager { - repo := newRepository(store) - m := &Manager{repo: repo} - api := newHandler(m, permissionsManager) - api.RegisterEndpoints(router) - return m -} - -func (m *Manager) GetPeer(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, peerID string) (*types.Peer, error) { - return m.repo.GetPeerByID(tx, strength, accountID, peerID) -} - -func (m *Manager) GetPeers(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID string) ([]*types.Peer, error) { - return m.repo.GetPeers(tx, strength, accountID) -} - -func (m *Manager) GetFilteredPeers(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, nameFilter, ipFilter string) ([]*types.Peer, error) { - return m.repo.GetFilteredPeers(tx, strength, accountID, nameFilter, ipFilter) + return m.repo.UpdatePeer(tx, peer) } diff --git a/internal/modules/peers/repository.go b/internal/modules/peers/repository.go index 0c263b4..d333da3 100644 --- a/internal/modules/peers/repository.go +++ b/internal/modules/peers/repository.go @@ -10,6 +10,7 @@ type Repository interface { GetPeerByID(tx db.Transaction, strength db.LockingStrength, accountID, peerId string) (*types.Peer, error) GetPeers(tx db.Transaction, strength db.LockingStrength, accountID string) ([]*types.Peer, error) GetFilteredPeers(tx db.Transaction, strength db.LockingStrength, accountID string, nameFilter, ipFilter string) ([]*types.Peer, error) + UpdatePeer(tx db.Transaction, peer *types.Peer) error } type repository struct { @@ -63,3 +64,7 @@ func (r *repository) GetFilteredPeers(tx db.Transaction, strength db.LockingStre } return peers, nil } + +func (r *repository) UpdatePeer(tx db.Transaction, peer *types.Peer) error { + return r.store.Update(tx, peer) +} diff --git a/internal/shared/db/store.go b/internal/shared/db/store.go index 3f88d1e..ed553d5 100644 --- a/internal/shared/db/store.go +++ b/internal/shared/db/store.go @@ -81,16 +81,8 @@ func (s *Store) GetMany(tx Transaction, strength LockingStrength, dest interface return db.Find(dest).Error } -func (s *Store) Delete(value interface{}) error { - return s.db.Delete(value).Error -} - -func (s *Store) Update(tx Transaction, value interface{}) error { - return s.Using(tx).Save(value).Error -} - -func (s *Store) Update(tx Transaction, value interface{}) error { - return s.Using(tx).Save(value).Error +func (s *Store) Delete(tx Transaction, value interface{}) error { + return s.Using(tx).Delete(value).Error } func (s *Store) Update(tx Transaction, value interface{}) error { diff --git a/internal/shared/db/transaction.go b/internal/shared/db/transaction.go index 49708b3..9526ede 100644 --- a/internal/shared/db/transaction.go +++ b/internal/shared/db/transaction.go @@ -15,16 +15,36 @@ const ( type Transaction interface { Commit() error Rollback() error + AddEvent(event func()) } type storeTx struct { - db *gorm.DB + db *gorm.DB + events []func() } func (t *storeTx) Commit() error { - return t.db.Commit().Error + err := t.db.Commit().Error + if err != nil { + t.commitEvents() + } + return err } func (t *storeTx) Rollback() error { return t.db.Rollback().Error } + +func (t *storeTx) AddEvent(event func()) { + if t.events == nil { + t.events = make([]func(), 0) + } + t.events = append(t.events, event) +} + +func (t *storeTx) commitEvents() { + for _, event := range t.events { + event() + } + t.events = nil +}