Merge pull request #3 from netbirdio/fetaure/add-networks

Fetaure/add networks
This commit is contained in:
Pascal Fischer
2025-06-06 13:27:57 +02:00
committed by GitHub
62 changed files with 1587 additions and 143 deletions
+1 -1
View File
@@ -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
@@ -1,4 +1,35 @@
package network_map
import (
"fmt"
log "github.com/sirupsen/logrus"
"management/internal/shared/db"
appmetrics "management/internal/shared/metrics"
)
type Controller struct {
repo Repository
metrics *metrics
}
func NewController(store *db.Store, metrics *appmetrics.AppMetrics) *Controller {
cMetrics, err := appmetrics.RegisterMetrics(metrics, newMetrics)
if err != nil {
log.Fatalf("Failed to register app metrics: %v", err)
}
return &Controller{
repo: newRepository(store, cMetrics),
metrics: cMetrics,
}
}
func (c *Controller) UpdatePeers(accountID string) error {
data, err := c.repo.GetNetworkMapData(accountID)
if err != nil {
return fmt.Errorf("get network map data: %w", err)
}
return nil
}
@@ -0,0 +1,31 @@
package network_map
import (
"context"
"time"
"go.opentelemetry.io/otel/metric"
)
type metrics struct {
dbAccessDuration metric.Int64Histogram
}
func newMetrics(meter metric.Meter) (*metrics, error) {
dbAccessDuration, err := meter.Int64Histogram(
"sync_request_duration_seconds",
metric.WithDescription("Duration of sync requests in seconds"),
metric.WithUnit("s"),
)
if err != nil {
return nil, err
}
return &metrics{
dbAccessDuration: dbAccessDuration,
}, nil
}
func (m *metrics) RecordDBAccessDuration(duration time.Duration) {
m.dbAccessDuration.Record(context.Background(), duration.Milliseconds(), metric.WithAttributes())
}
@@ -0,0 +1,65 @@
package network_map
import (
"time"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"management/internal/modules/accounts"
"management/internal/modules/groups"
"management/internal/modules/networks"
"management/internal/modules/networks/resources"
"management/internal/modules/networks/routers"
"management/internal/modules/policies"
"management/internal/shared/db"
)
type NetworkMapData struct {
// we have to name column to aid as it collides with Network.Id when work with associations
Id string `gorm:"primaryKey"`
Domain string `gorm:"index"`
DomainCategory string
IsDomainPrimaryAccount bool
Network *accounts.Network `gorm:"embedded;embeddedPrefix:network_"`
Peers []nbpeer.Peer `json:"-" gorm:"foreignKey:AccountID;references:id"`
Groups []groups.Group `json:"-" gorm:"foreignKey:AccountID;references:id"`
Policies []*policies.Policy `gorm:"foreignKey:AccountID;references:id"`
Networks []*networks.Network `gorm:"foreignKey:AccountID;references:id"`
NetworkRouters []*routers.NetworkRouter `gorm:"foreignKey:AccountID;references:id"`
NetworkResources []*resources.NetworkResource `gorm:"foreignKey:AccountID;references:id"`
}
type Repository interface {
GetNetworkMapData(accountID string) (*NetworkMapData, error)
}
type repository struct {
store *db.Store
metrics *metrics
}
func newRepository(s *db.Store, metrics *metrics) Repository {
return &repository{
store: s,
metrics: metrics,
}
}
func (r *repository) GetNetworkMapData(accountID string) (*NetworkMapData, error) {
start := time.Now()
var networkMapData NetworkMapData
err := r.store.GetOne(nil, db.LockingStrengthShare, &networkMapData, "id = ?", accountID)
if err != nil {
return nil, err
}
if err := r.store.Load(&networkMapData, "Peers", "Groups", "Policies", "Networks", "NetworkRouters", "NetworkResources"); err != nil {
return nil, err
}
r.metrics.RecordDBAccessDuration(time.Since(start))
return &networkMapData, nil
}
@@ -1,4 +1,4 @@
package server
package network_map
import (
"context"
@@ -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 {
-9
View File
@@ -1,10 +1 @@
package accounts
import "management/pkg/logging"
var log = logging.LoggerForThisPackage()
type Manager struct {
repo Repository
handler *handler
}
@@ -1,4 +1,4 @@
package accounts
package manager
import (
"encoding/json"
@@ -0,0 +1,10 @@
package manager
import "management/pkg/logging"
var log = logging.LoggerForThisPackage()
type Manager struct {
repo Repository
handler *handler
}
@@ -1,4 +1,4 @@
package accounts
package manager
import "management/internal/shared/db"
+73
View File
@@ -0,0 +1,73 @@
package accounts
import (
"math/rand"
"net"
"sync"
"time"
"github.com/c-robinson/iplib"
"github.com/rs/xid"
)
const (
// SubnetSize is a size of the subnet of the global network, e.g. 100.77.0.0/16
SubnetSize = 16
// NetSize is a global network size 100.64.0.0/10
NetSize = 10
// AllowedIPsFormat generates Wireguard AllowedIPs format (e.g. 100.64.30.1/32)
AllowedIPsFormat = "%s/32"
)
type Network struct {
Identifier string `json:"id"`
Net net.IPNet `gorm:"serializer:json"`
Dns string
// Serial is an ID that increments by 1 when any change to the network happened (e.g. new peer has been added).
// Used to synchronize state to the client apps.
Serial uint64
Mu sync.Mutex `json:"-" gorm:"-"`
}
// NewNetwork creates a new Network initializing it with a Serial=0
// It takes a random /16 subnet from 100.64.0.0/10 (64 different subnets)
func NewNetwork() *Network {
n := iplib.NewNet4(net.ParseIP("100.64.0.0"), NetSize)
sub, _ := n.Subnet(SubnetSize)
s := rand.NewSource(time.Now().Unix())
r := rand.New(s)
intn := r.Intn(len(sub))
return &Network{
Identifier: xid.New().String(),
Net: sub[intn].IPNet,
Dns: "",
Serial: 0}
}
// IncSerial increments Serial by 1 reflecting that the network state has been changed
func (n *Network) IncSerial() {
n.Mu.Lock()
defer n.Mu.Unlock()
n.Serial++
}
// CurrentSerial returns the Network.Serial of the network (latest state id)
func (n *Network) CurrentSerial() uint64 {
n.Mu.Lock()
defer n.Mu.Unlock()
return n.Serial
}
func (n *Network) Copy() *Network {
return &Network{
Identifier: n.Identifier,
Net: n.Net,
Dns: n.Dns,
Serial: n.Serial,
}
}
@@ -1,4 +1,4 @@
package settings
package manager
import (
"encoding/json"
@@ -1,4 +1,4 @@
package settings
package manager
import (
"context"
@@ -1,4 +1,4 @@
package settings
package manager
import (
"management/internal/modules/accounts/settings/types"
@@ -1,4 +1,4 @@
package types
package settings
import (
"time"
+25
View File
@@ -0,0 +1,25 @@
package groups
import "github.com/netbirdio/netbird/management/server/integration_reference"
type Group struct {
// ID of the group
ID string `gorm:"primaryKey"`
// AccountID is a reference to Account that this object belongs
AccountID string `json:"-" gorm:"index"`
// Name visible in the UI
Name string
// Issued defines how this group was created (enum of "api", "integration" or "jwt")
Issued string
// Peers list of the group
Peers []string `gorm:"serializer:json"`
// Resources contains a list of resources in that group
Resources []Resource `gorm:"serializer:json"`
IntegrationReference integration_reference.IntegrationReference `gorm:"embedded;embeddedPrefix:integration_ref_"`
}
@@ -1,4 +1,4 @@
package groups
package manager
import (
"encoding/json"
@@ -1,4 +1,4 @@
package groups
package manager
import (
"context"
@@ -1,4 +1,4 @@
package groups
package manager
import (
"management/internal/shared/db"
+28
View File
@@ -0,0 +1,28 @@
package groups
import "github.com/netbirdio/netbird/management/server/http/api"
type Resource struct {
ID string
Type string
}
func (r *Resource) ToAPIResponse() *api.Resource {
if r.ID == "" && r.Type == "" {
return nil
}
return &api.Resource{
Id: r.ID,
Type: api.ResourceType(r.Type),
}
}
func (r *Resource) FromAPIRequest(req *api.Resource) {
if req == nil {
return
}
r.ID = req.Id
r.Type = string(req.Type)
}
+19
View File
@@ -0,0 +1,19 @@
package networks
import (
"context"
"management/internal/shared/db"
"management/internal/shared/hook"
)
type Manager interface {
GetAllNetworks(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, userID string) ([]*Network, error)
GetNetwork(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, userID, networkID string) (*Network, error)
CreateNetwork(ctx context.Context, tx db.Transaction, userID string, network *Network) (*Network, error)
UpdateNetwork(ctx context.Context, tx db.Transaction, userID string, network *Network) (*Network, error)
DeleteNetwork(ctx context.Context, tx db.Transaction, accountID, userID, networkID string) error
// events
OnNetworkDelete() *hook.Hook[*NetworkEvent]
}

Some files were not shown because too many files have changed in this diff Show More