mirror of
https://github.com/netbirdio/netbird.git
synced 2025-02-23 05:31:28 +01:00
Management - add serial to Network reflecting network updates (#179)
* chore: [management] - add account serial ID * Fix concurrency on the client (#183) * reworked peer connection establishment logic eliminating race conditions and deadlocks while running many peers * chore: move serial to Network from Account * feature: increment Network serial ID when adding/removing peers * chore: extract network struct init to network.go * chore: add serial test when adding peer to the account * test: add ModificationID test on AddPeer and DeletePeer
This commit is contained in:
parent
bafa71fc2e
commit
9d1ecbbfb2
@ -1,13 +1,11 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/wiretrustee/wiretrustee/util"
|
"github.com/wiretrustee/wiretrustee/util"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"net"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,6 +27,12 @@ type Account struct {
|
|||||||
Users map[string]*User
|
Users map[string]*User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewAccount creates a new Account with a generated ID and generated default setup keys
|
||||||
|
func NewAccount(userId string) *Account {
|
||||||
|
accountId := xid.New().String()
|
||||||
|
return newAccountWithId(accountId, userId)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Account) Copy() *Account {
|
func (a *Account) Copy() *Account {
|
||||||
peers := map[string]*Peer{}
|
peers := map[string]*Peer{}
|
||||||
for id, peer := range a.Peers {
|
for id, peer := range a.Peers {
|
||||||
@ -186,7 +190,7 @@ func (am *AccountManager) AddAccount(accountId string, userId string) (*Account,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (am *AccountManager) createAccount(accountId string, userId string) (*Account, error) {
|
func (am *AccountManager) createAccount(accountId string, userId string) (*Account, error) {
|
||||||
account, _ := newAccountWithId(accountId, userId)
|
account := newAccountWithId(accountId, userId)
|
||||||
|
|
||||||
err := am.Store.SaveAccount(account)
|
err := am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -197,7 +201,7 @@ func (am *AccountManager) createAccount(accountId string, userId string) (*Accou
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id
|
// newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id
|
||||||
func newAccountWithId(accountId string, userId string) (*Account, *SetupKey) {
|
func newAccountWithId(accountId string, userId string) *Account {
|
||||||
|
|
||||||
log.Debugf("creating new account")
|
log.Debugf("creating new account")
|
||||||
|
|
||||||
@ -206,22 +210,13 @@ func newAccountWithId(accountId string, userId string) (*Account, *SetupKey) {
|
|||||||
oneOffKey := GenerateSetupKey("One-off key", SetupKeyOneOff, DefaultSetupKeyDuration)
|
oneOffKey := GenerateSetupKey("One-off key", SetupKeyOneOff, DefaultSetupKeyDuration)
|
||||||
setupKeys[defaultKey.Key] = defaultKey
|
setupKeys[defaultKey.Key] = defaultKey
|
||||||
setupKeys[oneOffKey.Key] = oneOffKey
|
setupKeys[oneOffKey.Key] = oneOffKey
|
||||||
network := &Network{
|
network := NewNetwork()
|
||||||
Id: uuid.New().String(),
|
|
||||||
Net: net.IPNet{IP: net.ParseIP("100.64.0.0"), Mask: net.IPMask{255, 192, 0, 0}},
|
|
||||||
Dns: ""}
|
|
||||||
peers := make(map[string]*Peer)
|
peers := make(map[string]*Peer)
|
||||||
users := make(map[string]*User)
|
users := make(map[string]*User)
|
||||||
|
|
||||||
log.Debugf("created new account %s with setup key %s", accountId, defaultKey.Key)
|
log.Debugf("created new account %s with setup key %s", accountId, defaultKey.Key)
|
||||||
|
|
||||||
return &Account{Id: accountId, SetupKeys: setupKeys, Network: network, Peers: peers, Users: users, CreatedBy: userId}, defaultKey
|
return &Account{Id: accountId, SetupKeys: setupKeys, Network: network, Peers: peers, Users: users, CreatedBy: userId}
|
||||||
}
|
|
||||||
|
|
||||||
// newAccount creates a new Account with a default SetupKey and a provided User.Id of a user who issued account creation (doesn't store in a Store)
|
|
||||||
func newAccount(userId string) (*Account, *SetupKey) {
|
|
||||||
accountId := xid.New().String()
|
|
||||||
return newAccountWithId(accountId, userId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAccountSetupKeyById(acc *Account, keyId string) *SetupKey {
|
func getAccountSetupKeyById(acc *Account, keyId string) *SetupKey {
|
||||||
|
@ -146,6 +146,8 @@ func TestAccountManager_AddPeer(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serial := account.Network.Serial() //should be 0
|
||||||
|
|
||||||
var setupKey *SetupKey
|
var setupKey *SetupKey
|
||||||
for _, key := range account.SetupKeys {
|
for _, key := range account.SetupKeys {
|
||||||
setupKey = key
|
setupKey = key
|
||||||
@ -156,6 +158,11 @@ func TestAccountManager_AddPeer(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if account.Network.serial != 0 {
|
||||||
|
t.Errorf("expecting account network to have an initial serial=0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
key, err := wgtypes.GenerateKey()
|
key, err := wgtypes.GenerateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -174,6 +181,12 @@ func TestAccountManager_AddPeer(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
account, err = manager.GetAccount(account.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if peer.Key != expectedPeerKey {
|
if peer.Key != expectedPeerKey {
|
||||||
t.Errorf("expecting just added peer to have key = %s, got %s", expectedPeerKey, peer.Key)
|
t.Errorf("expecting just added peer to have key = %s, got %s", expectedPeerKey, peer.Key)
|
||||||
}
|
}
|
||||||
@ -182,7 +195,64 @@ func TestAccountManager_AddPeer(t *testing.T) {
|
|||||||
t.Errorf("expecting just added peer to have IP = %s, got %s", expectedPeerIP, peer.IP.String())
|
t.Errorf("expecting just added peer to have IP = %s, got %s", expectedPeerIP, peer.IP.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if account.Network.Serial() != 1 {
|
||||||
|
t.Errorf("expecting Network serial=%d to be incremented by 1 and be equal to %d when adding new peer to account", serial, account.Network.Serial())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccountManager_DeletePeer(t *testing.T) {
|
||||||
|
manager, err := createManager(t)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := manager.AddAccount("test_account", "account_creator")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var setupKey *SetupKey
|
||||||
|
for _, key := range account.SetupKeys {
|
||||||
|
setupKey = key
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := wgtypes.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
peerKey := key.PublicKey().String()
|
||||||
|
|
||||||
|
_, err = manager.AddPeer(setupKey.Key, Peer{
|
||||||
|
Key: peerKey,
|
||||||
|
Meta: PeerSystemMeta{},
|
||||||
|
Name: peerKey,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expecting peer to be added, got failure %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = manager.DeletePeer(account.Id, peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err = manager.GetAccount(account.Id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if account.Network.Serial() != 2 {
|
||||||
|
t.Errorf("expecting Network serial=%d to be incremented and be equal to 2 after adding and deleteing a peer", account.Network.Serial())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func createManager(t *testing.T) (*AccountManager, error) {
|
func createManager(t *testing.T) (*AccountManager, error) {
|
||||||
store, err := createStore(t)
|
store, err := createStore(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -32,7 +32,7 @@ func TestNewStore(t *testing.T) {
|
|||||||
func TestSaveAccount(t *testing.T) {
|
func TestSaveAccount(t *testing.T) {
|
||||||
store := newStore(t)
|
store := newStore(t)
|
||||||
|
|
||||||
account, _ := newAccount("testuser")
|
account := NewAccount("testuser")
|
||||||
account.Users["testuser"] = NewAdminUser("testuser")
|
account.Users["testuser"] = NewAdminUser("testuser")
|
||||||
setupKey := GenerateDefaultSetupKey()
|
setupKey := GenerateDefaultSetupKey()
|
||||||
account.SetupKeys[setupKey.Key] = setupKey
|
account.SetupKeys[setupKey.Key] = setupKey
|
||||||
@ -72,7 +72,7 @@ func TestSaveAccount(t *testing.T) {
|
|||||||
func TestStore(t *testing.T) {
|
func TestStore(t *testing.T) {
|
||||||
store := newStore(t)
|
store := newStore(t)
|
||||||
|
|
||||||
account, _ := newAccount("testuser")
|
account := NewAccount("testuser")
|
||||||
account.Users["testuser"] = NewAdminUser("testuser")
|
account.Users["testuser"] = NewAdminUser("testuser")
|
||||||
account.Peers["testpeer"] = &Peer{
|
account.Peers["testpeer"] = &Peer{
|
||||||
Key: "peerkey",
|
Key: "peerkey",
|
||||||
|
@ -3,7 +3,9 @@ package server
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/rs/xid"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -15,13 +17,42 @@ type Network struct {
|
|||||||
Id string
|
Id string
|
||||||
Net net.IPNet
|
Net net.IPNet
|
||||||
Dns string
|
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:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetwork creates a new Network initializing it with a serial=0
|
||||||
|
func NewNetwork() *Network {
|
||||||
|
return &Network{
|
||||||
|
Id: xid.New().String(),
|
||||||
|
Net: net.IPNet{IP: net.ParseIP("100.64.0.0"), Mask: net.IPMask{255, 192, 0, 0}},
|
||||||
|
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 = n.serial + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial returns the Network.serial of the network (latest state id)
|
||||||
|
func (n *Network) Serial() uint64 {
|
||||||
|
n.mu.Lock()
|
||||||
|
defer n.mu.Unlock()
|
||||||
|
return n.serial
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Network) Copy() *Network {
|
func (n *Network) Copy() *Network {
|
||||||
return &Network{
|
return &Network{
|
||||||
Id: n.Id,
|
Id: n.Id,
|
||||||
Net: n.Net,
|
Net: n.Net,
|
||||||
Dns: n.Dns,
|
Dns: n.Dns,
|
||||||
|
serial: n.serial,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,11 +118,22 @@ func (am *AccountManager) DeletePeer(accountId string, peerKey string) (*Peer, e
|
|||||||
am.mux.Lock()
|
am.mux.Lock()
|
||||||
defer am.mux.Unlock()
|
defer am.mux.Unlock()
|
||||||
|
|
||||||
|
account, err := am.Store.GetAccount(accountId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||||
|
}
|
||||||
|
|
||||||
peer, err := am.Store.DeletePeer(accountId, peerKey)
|
peer, err := am.Store.DeletePeer(accountId, peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
account.Network.IncSerial()
|
||||||
|
err = am.Store.SaveAccount(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
err = am.peersUpdateManager.SendUpdate(peerKey,
|
err = am.peersUpdateManager.SendUpdate(peerKey,
|
||||||
&UpdateMessage{
|
&UpdateMessage{
|
||||||
Update: &proto.SyncResponse{
|
Update: &proto.SyncResponse{
|
||||||
@ -255,6 +266,8 @@ func (am *AccountManager) AddPeer(setupKey string, peer Peer) (*Peer, error) {
|
|||||||
|
|
||||||
account.Peers[newPeer.Key] = newPeer
|
account.Peers[newPeer.Key] = newPeer
|
||||||
account.SetupKeys[sk.Key] = sk.IncrementUsage()
|
account.SetupKeys[sk.Key] = sk.IncrementUsage()
|
||||||
|
account.Network.IncSerial()
|
||||||
|
|
||||||
err = am.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed adding peer")
|
return nil, status.Errorf(codes.Internal, "failed adding peer")
|
||||||
|
@ -47,7 +47,7 @@ func (am *AccountManager) GetOrCreateAccountByUser(userId string) (*Account, err
|
|||||||
account, err := am.Store.GetUserAccount(userId)
|
account, err := am.Store.GetUserAccount(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
||||||
account, _ = newAccount(userId)
|
account = NewAccount(userId)
|
||||||
account.Users[userId] = NewAdminUser(userId)
|
account.Users[userId] = NewAdminUser(userId)
|
||||||
err = am.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user