mirror of https://github.com/netbirdio/netbird.git
Add DNS nameserver support to management (#484)
Add DNS package and Nameserver group objects Add CRUD operations for Nameserver Groups to account manager Add Routes and Nameservers to Account Copy method Run docker tests with timeout and serial flags
This commit is contained in:
parent
93d20e370b
commit
2cd9b11e7d
|
@ -75,13 +75,13 @@ jobs:
|
|||
- run: chmod +x *testing.bin
|
||||
|
||||
- name: Run Iface tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/iface --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/iface-testing.bin
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/iface --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/iface-testing.bin -test.timeout 5m -test.parallel 1
|
||||
|
||||
- name: Run RouteManager tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal/routemanager --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/routemanager-testing.bin
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal/routemanager --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/routemanager-testing.bin -test.timeout 5m -test.parallel 1
|
||||
|
||||
- name: Run Engine tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin -test.timeout 5m -test.parallel 1
|
||||
|
||||
- name: Run Peer tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal/peer --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/peer-testing.bin
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal/peer --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/peer-testing.bin -test.timeout 5m -test.parallel 1
|
|
@ -0,0 +1,6 @@
|
|||
// Package dns implement dns types and standard methods and functions
|
||||
// to parse and normalize dns records and configuration
|
||||
package dns
|
||||
|
||||
// DefaultDNSPort well-known port number
|
||||
const DefaultDNSPort = 53
|
|
@ -0,0 +1,183 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxGroupNameChar maximum group name size
|
||||
MaxGroupNameChar = 40
|
||||
// InvalidNameServerType invalid nameserver type
|
||||
InvalidNameServerType NameServerType = iota
|
||||
// UDPNameServerType udp nameserver type
|
||||
UDPNameServerType
|
||||
)
|
||||
|
||||
const (
|
||||
// InvalidNameServerTypeString invalid nameserver type as string
|
||||
InvalidNameServerTypeString = "invalid"
|
||||
// UDPNameServerTypeString udp nameserver type as string
|
||||
UDPNameServerTypeString = "udp"
|
||||
)
|
||||
|
||||
// NameServerType nameserver type
|
||||
type NameServerType int
|
||||
|
||||
// String returns nameserver type string
|
||||
func (n NameServerType) String() string {
|
||||
switch n {
|
||||
case UDPNameServerType:
|
||||
return UDPNameServerTypeString
|
||||
default:
|
||||
return InvalidNameServerTypeString
|
||||
}
|
||||
}
|
||||
|
||||
// ToNameServerType returns a nameserver type
|
||||
func ToNameServerType(typeString string) NameServerType {
|
||||
switch typeString {
|
||||
case UDPNameServerTypeString:
|
||||
return UDPNameServerType
|
||||
default:
|
||||
return InvalidNameServerType
|
||||
}
|
||||
}
|
||||
|
||||
// NameServerGroup group of nameservers and with group ids
|
||||
type NameServerGroup struct {
|
||||
// ID identifier of group
|
||||
ID string
|
||||
// Name group name
|
||||
Name string
|
||||
// Description group description
|
||||
Description string
|
||||
// NameServers list of nameservers
|
||||
NameServers []NameServer
|
||||
// Groups list of peer group IDs to distribute the nameservers information
|
||||
Groups []string
|
||||
// Enabled group status
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// NameServer represents a DNS nameserver
|
||||
type NameServer struct {
|
||||
// IP address of nameserver
|
||||
IP netip.Addr
|
||||
// NSType nameserver type
|
||||
NSType NameServerType
|
||||
// Port nameserver listening port
|
||||
Port int
|
||||
}
|
||||
|
||||
// Copy copies a nameserver object
|
||||
func (n *NameServer) Copy() *NameServer {
|
||||
return &NameServer{
|
||||
IP: n.IP,
|
||||
NSType: n.NSType,
|
||||
Port: n.Port,
|
||||
}
|
||||
}
|
||||
|
||||
// IsEqual compares one nameserver with the other
|
||||
func (n *NameServer) IsEqual(other *NameServer) bool {
|
||||
return other.IP == n.IP &&
|
||||
other.NSType == n.NSType &&
|
||||
other.Port == n.Port
|
||||
}
|
||||
|
||||
// ParseNameServerURL parses a nameserver url in the format <type>://<ip>:<port>, e.g., udp://1.1.1.1:53
|
||||
func ParseNameServerURL(nsURL string) (NameServer, error) {
|
||||
parsedURL, err := url.Parse(nsURL)
|
||||
if err != nil {
|
||||
return NameServer{}, err
|
||||
}
|
||||
var ns NameServer
|
||||
parsedScheme := strings.ToLower(parsedURL.Scheme)
|
||||
nsType := ToNameServerType(parsedScheme)
|
||||
if nsType == InvalidNameServerType {
|
||||
return NameServer{}, fmt.Errorf("invalid nameserver url schema type, got %s", parsedScheme)
|
||||
}
|
||||
ns.NSType = nsType
|
||||
|
||||
parsedPort, err := strconv.Atoi(parsedURL.Port())
|
||||
if err != nil {
|
||||
return NameServer{}, fmt.Errorf("invalid nameserver url port, got %s", parsedURL.Port())
|
||||
}
|
||||
ns.Port = parsedPort
|
||||
|
||||
parsedAddr, err := netip.ParseAddr(parsedURL.Hostname())
|
||||
if err != nil {
|
||||
return NameServer{}, fmt.Errorf("invalid nameserver url IP, got %s", parsedURL.Hostname())
|
||||
}
|
||||
|
||||
ns.IP = parsedAddr
|
||||
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
// Copy copies a nameserver group object
|
||||
func (g *NameServerGroup) Copy() *NameServerGroup {
|
||||
return &NameServerGroup{
|
||||
ID: g.ID,
|
||||
Name: g.Name,
|
||||
Description: g.Description,
|
||||
NameServers: g.NameServers,
|
||||
Groups: g.Groups,
|
||||
}
|
||||
}
|
||||
|
||||
// IsEqual compares one nameserver group with the other
|
||||
func (g *NameServerGroup) IsEqual(other *NameServerGroup) bool {
|
||||
return other.ID == g.ID &&
|
||||
other.Name == g.Name &&
|
||||
other.Description == g.Description &&
|
||||
compareNameServerList(g.NameServers, other.NameServers) &&
|
||||
compareGroupsList(g.Groups, other.Groups)
|
||||
}
|
||||
|
||||
func compareNameServerList(list, other []NameServer) bool {
|
||||
if len(list) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, ns := range list {
|
||||
if !containsNameServer(ns, other) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func containsNameServer(element NameServer, list []NameServer) bool {
|
||||
for _, ns := range list {
|
||||
if ns.IsEqual(&element) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func compareGroupsList(list, other []string) bool {
|
||||
if len(list) != len(other) {
|
||||
return false
|
||||
}
|
||||
for _, id := range list {
|
||||
match := false
|
||||
for _, otherID := range other {
|
||||
if id == otherID {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -1,4 +1,17 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if ! which realpath > /dev/null 2>&1
|
||||
then
|
||||
echo realpath is not installed
|
||||
echo run: brew install coreutils
|
||||
exit 1
|
||||
fi
|
||||
|
||||
old_pwd=$(pwd)
|
||||
script_path=$(dirname $(realpath "$0"))
|
||||
cd "$script_path"
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
|
||||
protoc -I proto/ proto/management.proto --go_out=. --go-grpc_out=.
|
||||
protoc -I ./ ./management.proto --go_out=../ --go-grpc_out=../
|
||||
cd "$old_pwd"
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/eko/gocache/v2/cache"
|
||||
cacheStore "github.com/eko/gocache/v2/store"
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server/idp"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
|
@ -40,6 +41,7 @@ type AccountManager interface {
|
|||
autoGroups []string,
|
||||
) (*SetupKey, error)
|
||||
SaveSetupKey(accountID string, key *SetupKey) (*SetupKey, error)
|
||||
ListSetupKeys(accountID string) ([]*SetupKey, error)
|
||||
SaveUser(accountID string, key *User) (*UserInfo, error)
|
||||
GetSetupKey(accountID, keyID string) (*SetupKey, error)
|
||||
GetAccountById(accountId string) (*Account, error)
|
||||
|
@ -78,7 +80,12 @@ type AccountManager interface {
|
|||
UpdateRoute(accountID string, routeID string, operations []RouteUpdateOperation) (*route.Route, error)
|
||||
DeleteRoute(accountID, routeID string) error
|
||||
ListRoutes(accountID string) ([]*route.Route, error)
|
||||
ListSetupKeys(accountID string) ([]*SetupKey, error)
|
||||
GetNameServerGroup(accountID, nsGroupID string) (*nbdns.NameServerGroup, error)
|
||||
CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, enabled bool) (*nbdns.NameServerGroup, error)
|
||||
SaveNameServerGroup(accountID string, nsGroupToSave *nbdns.NameServerGroup) error
|
||||
UpdateNameServerGroup(accountID, nsGroupID string, operations []NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error)
|
||||
DeleteNameServerGroup(accountID, nsGroupID string) error
|
||||
ListNameServerGroups(accountID string) ([]*nbdns.NameServerGroup, error)
|
||||
}
|
||||
|
||||
type DefaultAccountManager struct {
|
||||
|
@ -106,6 +113,7 @@ type Account struct {
|
|||
Groups map[string]*Group
|
||||
Rules map[string]*Rule
|
||||
Routes map[string]*route.Route
|
||||
NameServerGroups map[string]*nbdns.NameServerGroup
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
|
@ -142,15 +150,27 @@ func (a *Account) Copy() *Account {
|
|||
rules[id] = rule.Copy()
|
||||
}
|
||||
|
||||
routes := map[string]*route.Route{}
|
||||
for id, route := range a.Routes {
|
||||
routes[id] = route.Copy()
|
||||
}
|
||||
|
||||
nsGroups := map[string]*nbdns.NameServerGroup{}
|
||||
for id, nsGroup := range a.NameServerGroups {
|
||||
nsGroups[id] = nsGroup.Copy()
|
||||
}
|
||||
|
||||
return &Account{
|
||||
Id: a.Id,
|
||||
CreatedBy: a.CreatedBy,
|
||||
SetupKeys: setupKeys,
|
||||
Network: a.Network.Copy(),
|
||||
Peers: peers,
|
||||
Users: users,
|
||||
Groups: groups,
|
||||
Rules: rules,
|
||||
Id: a.Id,
|
||||
CreatedBy: a.CreatedBy,
|
||||
SetupKeys: setupKeys,
|
||||
Network: a.Network.Copy(),
|
||||
Peers: peers,
|
||||
Users: users,
|
||||
Groups: groups,
|
||||
Rules: rules,
|
||||
Routes: routes,
|
||||
NameServerGroups: nsGroups,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -583,18 +603,20 @@ func newAccountWithId(accountId, userId, domain string) *Account {
|
|||
peers := make(map[string]*Peer)
|
||||
users := make(map[string]*User)
|
||||
routes := make(map[string]*route.Route)
|
||||
nameServersGroups := make(map[string]*nbdns.NameServerGroup)
|
||||
users[userId] = NewAdminUser(userId)
|
||||
log.Debugf("created new account %s with setup key %s", accountId, defaultKey.Key)
|
||||
|
||||
acc := &Account{
|
||||
Id: accountId,
|
||||
SetupKeys: setupKeys,
|
||||
Network: network,
|
||||
Peers: peers,
|
||||
Users: users,
|
||||
CreatedBy: userId,
|
||||
Domain: domain,
|
||||
Routes: routes,
|
||||
Id: accountId,
|
||||
SetupKeys: setupKeys,
|
||||
Network: network,
|
||||
Peers: peers,
|
||||
Users: users,
|
||||
CreatedBy: userId,
|
||||
Domain: domain,
|
||||
Routes: routes,
|
||||
NameServerGroups: nameServersGroups,
|
||||
}
|
||||
|
||||
addAllGroup(acc)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package mock_server
|
||||
|
||||
import (
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server"
|
||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
|
@ -53,6 +54,12 @@ type MockAccountManager struct {
|
|||
SaveSetupKeyFunc func(accountID string, key *server.SetupKey) (*server.SetupKey, error)
|
||||
ListSetupKeysFunc func(accountID string) ([]*server.SetupKey, error)
|
||||
SaveUserFunc func(accountID string, user *server.User) (*server.UserInfo, error)
|
||||
GetNameServerGroupFunc func(accountID, nsGroupID string) (*nbdns.NameServerGroup, error)
|
||||
CreateNameServerGroupFunc func(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, enabled bool) (*nbdns.NameServerGroup, error)
|
||||
SaveNameServerGroupFunc func(accountID string, nsGroupToSave *nbdns.NameServerGroup) error
|
||||
UpdateNameServerGroupFunc func(accountID, nsGroupID string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error)
|
||||
DeleteNameServerGroupFunc func(accountID, nsGroupID string) error
|
||||
ListNameServerGroupsFunc func(accountID string) ([]*nbdns.NameServerGroup, error)
|
||||
}
|
||||
|
||||
// GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface
|
||||
|
@ -430,3 +437,51 @@ func (am *MockAccountManager) SaveUser(accountID string, user *server.User) (*se
|
|||
}
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SaveUser is not implemented")
|
||||
}
|
||||
|
||||
// GetNameServerGroup mocks GetNameServerGroup of the AccountManager interface
|
||||
func (am *MockAccountManager) GetNameServerGroup(accountID, nsGroupID string) (*nbdns.NameServerGroup, error) {
|
||||
if am.GetNameServerGroupFunc != nil {
|
||||
return am.GetNameServerGroupFunc(accountID, nsGroupID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CreateNameServerGroup mocks CreateNameServerGroup of the AccountManager interface
|
||||
func (am *MockAccountManager) CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, enabled bool) (*nbdns.NameServerGroup, error) {
|
||||
if am.CreateNameServerGroupFunc != nil {
|
||||
return am.CreateNameServerGroupFunc(accountID, name, description, nameServerList, groups, enabled)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SaveNameServerGroup mocks SaveNameServerGroup of the AccountManager interface
|
||||
func (am *MockAccountManager) SaveNameServerGroup(accountID string, nsGroupToSave *nbdns.NameServerGroup) error {
|
||||
if am.SaveNameServerGroupFunc != nil {
|
||||
return am.SaveNameServerGroupFunc(accountID, nsGroupToSave)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateNameServerGroup mocks UpdateNameServerGroup of the AccountManager interface
|
||||
func (am *MockAccountManager) UpdateNameServerGroup(accountID, nsGroupID string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) {
|
||||
if am.UpdateNameServerGroupFunc != nil {
|
||||
return am.UpdateNameServerGroupFunc(accountID, nsGroupID, operations)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// DeleteNameServerGroup mocks DeleteNameServerGroup of the AccountManager interface
|
||||
func (am *MockAccountManager) DeleteNameServerGroup(accountID, nsGroupID string) error {
|
||||
if am.DeleteNameServerGroupFunc != nil {
|
||||
return am.DeleteNameServerGroupFunc(accountID, nsGroupID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListNameServerGroups mocks ListNameServerGroups of the AccountManager interface
|
||||
func (am *MockAccountManager) ListNameServerGroups(accountID string) ([]*nbdns.NameServerGroup, error) {
|
||||
if am.ListNameServerGroupsFunc != nil {
|
||||
return am.ListNameServerGroupsFunc(accountID)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/rs/xid"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
// UpdateNameServerGroupName indicates a nameserver group name update operation
|
||||
UpdateNameServerGroupName NameServerGroupUpdateOperationType = iota
|
||||
// UpdateNameServerGroupDescription indicates a nameserver group description update operation
|
||||
UpdateNameServerGroupDescription
|
||||
// UpdateNameServerGroupNameServers indicates a nameserver group nameservers list update operation
|
||||
UpdateNameServerGroupNameServers
|
||||
// UpdateNameServerGroupGroups indicates a nameserver group' groups update operation
|
||||
UpdateNameServerGroupGroups
|
||||
// UpdateNameServerGroupEnabled indicates a nameserver group status update operation
|
||||
UpdateNameServerGroupEnabled
|
||||
)
|
||||
|
||||
// NameServerGroupUpdateOperationType operation type
|
||||
type NameServerGroupUpdateOperationType int
|
||||
|
||||
func (t NameServerGroupUpdateOperationType) String() string {
|
||||
switch t {
|
||||
case UpdateNameServerGroupDescription:
|
||||
return "UpdateNameServerGroupDescription"
|
||||
case UpdateNameServerGroupName:
|
||||
return "UpdateNameServerGroupName"
|
||||
case UpdateNameServerGroupNameServers:
|
||||
return "UpdateNameServerGroupNameServers"
|
||||
case UpdateNameServerGroupGroups:
|
||||
return "UpdateNameServerGroupGroups"
|
||||
case UpdateNameServerGroupEnabled:
|
||||
return "UpdateNameServerGroupEnabled"
|
||||
default:
|
||||
return "InvalidOperation"
|
||||
}
|
||||
}
|
||||
|
||||
// NameServerGroupUpdateOperation operation object with type and values to be applied
|
||||
type NameServerGroupUpdateOperation struct {
|
||||
Type NameServerGroupUpdateOperationType
|
||||
Values []string
|
||||
}
|
||||
|
||||
// GetNameServerGroup gets a nameserver group object from account and nameserver group IDs
|
||||
func (am *DefaultAccountManager) GetNameServerGroup(accountID, nsGroupID string) (*nbdns.NameServerGroup, error) {
|
||||
am.mux.Lock()
|
||||
defer am.mux.Unlock()
|
||||
|
||||
account, err := am.Store.GetAccount(accountID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||
}
|
||||
|
||||
nsGroup, found := account.NameServerGroups[nsGroupID]
|
||||
if found {
|
||||
return nsGroup.Copy(), nil
|
||||
}
|
||||
|
||||
return nil, status.Errorf(codes.NotFound, "nameserver group with ID %s not found", nsGroupID)
|
||||
}
|
||||
|
||||
// CreateNameServerGroup creates and saves a new nameserver group
|
||||
func (am *DefaultAccountManager) CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, enabled bool) (*nbdns.NameServerGroup, error) {
|
||||
am.mux.Lock()
|
||||
defer am.mux.Unlock()
|
||||
|
||||
account, err := am.Store.GetAccount(accountID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||
}
|
||||
|
||||
newNSGroup := &nbdns.NameServerGroup{
|
||||
ID: xid.New().String(),
|
||||
Name: name,
|
||||
Description: description,
|
||||
NameServers: nameServerList,
|
||||
Groups: groups,
|
||||
Enabled: enabled,
|
||||
}
|
||||
|
||||
err = validateNameServerGroup(false, newNSGroup, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if account.NameServerGroups == nil {
|
||||
account.NameServerGroups = make(map[string]*nbdns.NameServerGroup)
|
||||
}
|
||||
|
||||
account.NameServerGroups[newNSGroup.ID] = newNSGroup
|
||||
|
||||
account.Network.IncSerial()
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newNSGroup.Copy(), nil
|
||||
}
|
||||
|
||||
// SaveNameServerGroup saves nameserver group
|
||||
func (am *DefaultAccountManager) SaveNameServerGroup(accountID string, nsGroupToSave *nbdns.NameServerGroup) error {
|
||||
am.mux.Lock()
|
||||
defer am.mux.Unlock()
|
||||
|
||||
if nsGroupToSave == nil {
|
||||
return status.Errorf(codes.InvalidArgument, "nameserver group provided is nil")
|
||||
}
|
||||
|
||||
account, err := am.Store.GetAccount(accountID)
|
||||
if err != nil {
|
||||
return status.Errorf(codes.NotFound, "account not found")
|
||||
}
|
||||
|
||||
err = validateNameServerGroup(true, nsGroupToSave, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account.NameServerGroups[nsGroupToSave.ID] = nsGroupToSave
|
||||
|
||||
account.Network.IncSerial()
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateNameServerGroup updates existing nameserver group with set of operations
|
||||
func (am *DefaultAccountManager) UpdateNameServerGroup(accountID, nsGroupID string, operations []NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) {
|
||||
am.mux.Lock()
|
||||
defer am.mux.Unlock()
|
||||
|
||||
account, err := am.Store.GetAccount(accountID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||
}
|
||||
|
||||
if len(operations) == 0 {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "operations shouldn't be empty")
|
||||
}
|
||||
|
||||
nsGroupToUpdate, ok := account.NameServerGroups[nsGroupID]
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.NotFound, "nameserver group ID %s no longer exists", nsGroupID)
|
||||
}
|
||||
|
||||
newNSGroup := nsGroupToUpdate.Copy()
|
||||
|
||||
for _, operation := range operations {
|
||||
valuesCount := len(operation.Values)
|
||||
if valuesCount < 1 {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "operation %s contains invalid number of values, it should be at least 1", operation.Type.String())
|
||||
}
|
||||
|
||||
for _, value := range operation.Values {
|
||||
if value == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "operation %s contains invalid empty string value", operation.Type.String())
|
||||
}
|
||||
}
|
||||
switch operation.Type {
|
||||
case UpdateNameServerGroupDescription:
|
||||
newNSGroup.Description = operation.Values[0]
|
||||
case UpdateNameServerGroupName:
|
||||
if valuesCount > 1 {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "failed to parse name values, expected 1 value got %d", valuesCount)
|
||||
}
|
||||
err = validateNSGroupName(operation.Values[0], nsGroupID, account.NameServerGroups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newNSGroup.Name = operation.Values[0]
|
||||
case UpdateNameServerGroupNameServers:
|
||||
var nsList []nbdns.NameServer
|
||||
for _, url := range operation.Values {
|
||||
ns, err := nbdns.ParseNameServerURL(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nsList = append(nsList, ns)
|
||||
}
|
||||
err = validateNSList(nsList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newNSGroup.NameServers = nsList
|
||||
case UpdateNameServerGroupGroups:
|
||||
err = validateGroups(operation.Values, account.Groups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newNSGroup.Groups = operation.Values
|
||||
case UpdateNameServerGroupEnabled:
|
||||
enabled, err := strconv.ParseBool(operation.Values[0])
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "failed to parse enabled %s, not boolean", operation.Values[0])
|
||||
}
|
||||
newNSGroup.Enabled = enabled
|
||||
}
|
||||
}
|
||||
|
||||
account.NameServerGroups[nsGroupID] = newNSGroup
|
||||
|
||||
account.Network.IncSerial()
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newNSGroup.Copy(), nil
|
||||
}
|
||||
|
||||
// DeleteNameServerGroup deletes nameserver group with nsGroupID
|
||||
func (am *DefaultAccountManager) DeleteNameServerGroup(accountID, nsGroupID string) error {
|
||||
am.mux.Lock()
|
||||
defer am.mux.Unlock()
|
||||
|
||||
account, err := am.Store.GetAccount(accountID)
|
||||
if err != nil {
|
||||
return status.Errorf(codes.NotFound, "account not found")
|
||||
}
|
||||
|
||||
delete(account.NameServerGroups, nsGroupID)
|
||||
|
||||
account.Network.IncSerial()
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListNameServerGroups returns a list of nameserver groups from account
|
||||
func (am *DefaultAccountManager) ListNameServerGroups(accountID string) ([]*nbdns.NameServerGroup, error) {
|
||||
am.mux.Lock()
|
||||
defer am.mux.Unlock()
|
||||
|
||||
account, err := am.Store.GetAccount(accountID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "account not found")
|
||||
}
|
||||
|
||||
nsGroups := make([]*nbdns.NameServerGroup, 0, len(account.NameServerGroups))
|
||||
for _, item := range account.NameServerGroups {
|
||||
nsGroups = append(nsGroups, item.Copy())
|
||||
}
|
||||
|
||||
return nsGroups, nil
|
||||
}
|
||||
|
||||
func validateNameServerGroup(existingGroup bool, nameserverGroup *nbdns.NameServerGroup, account *Account) error {
|
||||
nsGroupID := ""
|
||||
if existingGroup {
|
||||
nsGroupID = nameserverGroup.ID
|
||||
_, found := account.NameServerGroups[nsGroupID]
|
||||
if !found {
|
||||
return status.Errorf(codes.NotFound, "nameserver group with ID %s was not found", nsGroupID)
|
||||
}
|
||||
}
|
||||
|
||||
err := validateNSGroupName(nameserverGroup.Name, nsGroupID, account.NameServerGroups)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validateNSList(nameserverGroup.NameServers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validateGroups(nameserverGroup.Groups, account.Groups)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateNSGroupName(name, nsGroupID string, nsGroupMap map[string]*nbdns.NameServerGroup) error {
|
||||
if utf8.RuneCountInString(name) > nbdns.MaxGroupNameChar || name == "" {
|
||||
return status.Errorf(codes.InvalidArgument, "nameserver group name should be between 1 and %d", nbdns.MaxGroupNameChar)
|
||||
}
|
||||
|
||||
for _, nsGroup := range nsGroupMap {
|
||||
if name == nsGroup.Name && nsGroup.ID != nsGroupID {
|
||||
return status.Errorf(codes.InvalidArgument, "a nameserver group with name %s already exist", name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateNSList(list []nbdns.NameServer) error {
|
||||
nsListLenght := len(list)
|
||||
if nsListLenght == 0 || nsListLenght > 2 {
|
||||
return status.Errorf(codes.InvalidArgument, "the list of nameservers should be 1 or 2, got %d", len(list))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateGroups(list []string, groups map[string]*Group) error {
|
||||
if len(list) == 0 {
|
||||
return status.Errorf(codes.InvalidArgument, "the list of group IDs should not be empty")
|
||||
}
|
||||
|
||||
for _, id := range list {
|
||||
if id == "" {
|
||||
return status.Errorf(codes.InvalidArgument, "group ID should not be empty string")
|
||||
}
|
||||
found := false
|
||||
for groupID := range groups {
|
||||
if id == groupID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return status.Errorf(codes.InvalidArgument, "group id %s not found", id)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,965 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/stretchr/testify/require"
|
||||
"net/netip"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
group1ID = "group1"
|
||||
group2ID = "group2"
|
||||
existingNSGroupName = "existing"
|
||||
existingNSGroupID = "existingNSGroup"
|
||||
nsGroupPeer1Key = "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8="
|
||||
nsGroupPeer2Key = "/yF0+vCfv+mRR5k0dca0TrGdO/oiNeAI58gToZm5NyI="
|
||||
)
|
||||
|
||||
func TestCreateNameServerGroup(t *testing.T) {
|
||||
type input struct {
|
||||
name string
|
||||
description string
|
||||
enabled bool
|
||||
groups []string
|
||||
nameServers []nbdns.NameServer
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputArgs input
|
||||
shouldCreate bool
|
||||
errFunc require.ErrorAssertionFunc
|
||||
expectedNSGroup *nbdns.NameServerGroup
|
||||
}{
|
||||
{
|
||||
name: "Create A NS Group",
|
||||
inputArgs: input{
|
||||
name: "super",
|
||||
description: "super",
|
||||
groups: []string{group1ID},
|
||||
nameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.NoError,
|
||||
shouldCreate: true,
|
||||
expectedNSGroup: &nbdns.NameServerGroup{
|
||||
Name: "super",
|
||||
Description: "super",
|
||||
Groups: []string{group1ID},
|
||||
NameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Should Not Create If Name Exist",
|
||||
inputArgs: input{
|
||||
name: existingNSGroupName,
|
||||
description: "super",
|
||||
groups: []string{group1ID},
|
||||
nameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Create If Name Is Small",
|
||||
inputArgs: input{
|
||||
name: "",
|
||||
description: "super",
|
||||
groups: []string{group1ID},
|
||||
nameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Create If Name Is Large",
|
||||
inputArgs: input{
|
||||
name: "1234567890123456789012345678901234567890extra",
|
||||
description: "super",
|
||||
groups: []string{group1ID},
|
||||
nameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Create A NS Group With No Nameservers Should Fail",
|
||||
inputArgs: input{
|
||||
name: "super",
|
||||
description: "super",
|
||||
groups: []string{group1ID},
|
||||
nameServers: []nbdns.NameServer{},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Create A NS Group With More Than 2 Nameservers Should Fail",
|
||||
inputArgs: input{
|
||||
name: "super",
|
||||
description: "super",
|
||||
groups: []string{group1ID},
|
||||
nameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.3.3"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Create If Groups Is Empty",
|
||||
inputArgs: input{
|
||||
name: "super",
|
||||
description: "super",
|
||||
groups: []string{},
|
||||
nameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Create If Group Doesn't Exist",
|
||||
inputArgs: input{
|
||||
name: "super",
|
||||
description: "super",
|
||||
groups: []string{"missingGroup"},
|
||||
nameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Create If Group ID Is Invalid",
|
||||
inputArgs: input{
|
||||
name: "super",
|
||||
description: "super",
|
||||
groups: []string{""},
|
||||
nameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
am, err := createNSManager(t)
|
||||
if err != nil {
|
||||
t.Error("failed to create account manager")
|
||||
}
|
||||
|
||||
account, err := initTestNSAccount(t, am)
|
||||
if err != nil {
|
||||
t.Error("failed to init testing account")
|
||||
}
|
||||
|
||||
outNSGroup, err := am.CreateNameServerGroup(
|
||||
account.Id,
|
||||
testCase.inputArgs.name,
|
||||
testCase.inputArgs.description,
|
||||
testCase.inputArgs.nameServers,
|
||||
testCase.inputArgs.groups,
|
||||
testCase.inputArgs.enabled,
|
||||
)
|
||||
|
||||
testCase.errFunc(t, err)
|
||||
|
||||
if !testCase.shouldCreate {
|
||||
return
|
||||
}
|
||||
|
||||
// assign generated ID
|
||||
testCase.expectedNSGroup.ID = outNSGroup.ID
|
||||
|
||||
if !testCase.expectedNSGroup.IsEqual(outNSGroup) {
|
||||
t.Errorf("new nameserver group didn't match expected ns group:\nGot %#v\nExpected:%#v\n", outNSGroup, testCase.expectedNSGroup)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveNameServerGroup(t *testing.T) {
|
||||
|
||||
existingNSGroup := &nbdns.NameServerGroup{
|
||||
ID: "testingNSGroup",
|
||||
Name: "super",
|
||||
Description: "super",
|
||||
NameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
Groups: []string{group1ID},
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
validGroups := []string{group2ID}
|
||||
invalidGroups := []string{"nonExisting"}
|
||||
validNameServerList := []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
}
|
||||
invalidNameServerListLarge := []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.3.3"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
}
|
||||
invalidID := "doesntExist"
|
||||
validName := "12345678901234567890qw"
|
||||
invalidNameLarge := "12345678901234567890qwertyuiopqwertyuiop1"
|
||||
invalidNameSmall := ""
|
||||
invalidNameExisting := existingNSGroupName
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
existingNSGroup *nbdns.NameServerGroup
|
||||
newID *string
|
||||
newName *string
|
||||
newNSList []nbdns.NameServer
|
||||
newGroups []string
|
||||
skipCopying bool
|
||||
shouldCreate bool
|
||||
errFunc require.ErrorAssertionFunc
|
||||
expectedNSGroup *nbdns.NameServerGroup
|
||||
}{
|
||||
{
|
||||
name: "Should Update Name Server Group",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newName: &validName,
|
||||
newGroups: validGroups,
|
||||
newNSList: validNameServerList,
|
||||
errFunc: require.NoError,
|
||||
shouldCreate: true,
|
||||
expectedNSGroup: &nbdns.NameServerGroup{
|
||||
ID: "testingNSGroup",
|
||||
Name: validName,
|
||||
Description: "super",
|
||||
NameServers: validNameServerList,
|
||||
Groups: validGroups,
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If Name Is Small",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newName: &invalidNameSmall,
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If Name Is Large",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newName: &invalidNameLarge,
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If Name Exists",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newName: &invalidNameExisting,
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If ID Don't Exist",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newID: &invalidID,
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If Nameserver List Is Small",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newNSList: []nbdns.NameServer{},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If Nameserver List Is Large",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newNSList: invalidNameServerListLarge,
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If Groups List Is Empty",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newGroups: []string{},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If Groups List Has Empty ID",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newGroups: []string{""},
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update If Groups List Has Non Existing Group ID",
|
||||
existingNSGroup: existingNSGroup,
|
||||
newGroups: invalidGroups,
|
||||
errFunc: require.Error,
|
||||
shouldCreate: false,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
am, err := createNSManager(t)
|
||||
if err != nil {
|
||||
t.Error("failed to create account manager")
|
||||
}
|
||||
|
||||
account, err := initTestNSAccount(t, am)
|
||||
if err != nil {
|
||||
t.Error("failed to init testing account")
|
||||
}
|
||||
|
||||
account.NameServerGroups[testCase.existingNSGroup.ID] = testCase.existingNSGroup
|
||||
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
t.Error("account should be saved")
|
||||
}
|
||||
|
||||
var nsGroupToSave *nbdns.NameServerGroup
|
||||
|
||||
if !testCase.skipCopying {
|
||||
nsGroupToSave = testCase.existingNSGroup.Copy()
|
||||
|
||||
if testCase.newID != nil {
|
||||
nsGroupToSave.ID = *testCase.newID
|
||||
}
|
||||
|
||||
if testCase.newName != nil {
|
||||
nsGroupToSave.Name = *testCase.newName
|
||||
}
|
||||
|
||||
if testCase.newGroups != nil {
|
||||
nsGroupToSave.Groups = testCase.newGroups
|
||||
}
|
||||
|
||||
if testCase.newNSList != nil {
|
||||
nsGroupToSave.NameServers = testCase.newNSList
|
||||
}
|
||||
}
|
||||
|
||||
err = am.SaveNameServerGroup(account.Id, nsGroupToSave)
|
||||
|
||||
testCase.errFunc(t, err)
|
||||
|
||||
if !testCase.shouldCreate {
|
||||
return
|
||||
}
|
||||
|
||||
savedNSGroup, saved := account.NameServerGroups[testCase.expectedNSGroup.ID]
|
||||
require.True(t, saved)
|
||||
|
||||
if !testCase.expectedNSGroup.IsEqual(savedNSGroup) {
|
||||
t.Errorf("new nameserver group didn't match expected group:\nGot %#v\nExpected:%#v\n", savedNSGroup, testCase.expectedNSGroup)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateNameServerGroup(t *testing.T) {
|
||||
nsGroupID := "testingNSGroup"
|
||||
|
||||
existingNSGroup := &nbdns.NameServerGroup{
|
||||
ID: nsGroupID,
|
||||
Name: "super",
|
||||
Description: "super",
|
||||
NameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
Groups: []string{group1ID},
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
existingNSGroup *nbdns.NameServerGroup
|
||||
nsGroupID string
|
||||
operations []NameServerGroupUpdateOperation
|
||||
shouldCreate bool
|
||||
errFunc require.ErrorAssertionFunc
|
||||
expectedNSGroup *nbdns.NameServerGroup
|
||||
}{
|
||||
{
|
||||
name: "Should Update Single Property",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupName,
|
||||
Values: []string{"superNew"},
|
||||
},
|
||||
},
|
||||
errFunc: require.NoError,
|
||||
shouldCreate: true,
|
||||
expectedNSGroup: &nbdns.NameServerGroup{
|
||||
ID: nsGroupID,
|
||||
Name: "superNew",
|
||||
Description: "super",
|
||||
NameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
Groups: []string{group1ID},
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Should Update Multiple Properties",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupName,
|
||||
Values: []string{"superNew"},
|
||||
},
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupDescription,
|
||||
Values: []string{"superDescription"},
|
||||
},
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupNameServers,
|
||||
Values: []string{"udp://127.0.0.1:53", "udp://8.8.8.8:53"},
|
||||
},
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupGroups,
|
||||
Values: []string{group1ID, group2ID},
|
||||
},
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupEnabled,
|
||||
Values: []string{"false"},
|
||||
},
|
||||
},
|
||||
errFunc: require.NoError,
|
||||
shouldCreate: true,
|
||||
expectedNSGroup: &nbdns.NameServerGroup{
|
||||
ID: nsGroupID,
|
||||
Name: "superNew",
|
||||
Description: "superDescription",
|
||||
NameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("127.0.0.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("8.8.8.8"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
Groups: []string{group1ID, group2ID},
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Invalid ID",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: "nonExistingNSGroup",
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Empty Operations",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Empty Values",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupName,
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Empty String",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupName,
|
||||
Values: []string{""},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Invalid Name Large String",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupName,
|
||||
Values: []string{"12345678901234567890qwertyuiopqwertyuiop1"},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Invalid On Existing Name",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupName,
|
||||
Values: []string{existingNSGroupName},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Invalid On Multiple Name Values",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupName,
|
||||
Values: []string{"nameOne", "nameTwo"},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Invalid Boolean",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupEnabled,
|
||||
Values: []string{"yes"},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Invalid Nameservers Wrong Schema",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupNameServers,
|
||||
Values: []string{"https://127.0.0.1:53"},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Invalid Nameservers Wrong IP",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupNameServers,
|
||||
Values: []string{"udp://8.8.8.300:53"},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Large Number Of Nameservers",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupNameServers,
|
||||
Values: []string{"udp://127.0.0.1:53", "udp://8.8.8.8:53", "udp://8.8.4.4:53"},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
{
|
||||
name: "Should Not Update On Invalid GroupID",
|
||||
existingNSGroup: existingNSGroup,
|
||||
nsGroupID: existingNSGroup.ID,
|
||||
operations: []NameServerGroupUpdateOperation{
|
||||
NameServerGroupUpdateOperation{
|
||||
Type: UpdateNameServerGroupGroups,
|
||||
Values: []string{"nonExistingGroupID"},
|
||||
},
|
||||
},
|
||||
errFunc: require.Error,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
am, err := createNSManager(t)
|
||||
if err != nil {
|
||||
t.Error("failed to create account manager")
|
||||
}
|
||||
|
||||
account, err := initTestNSAccount(t, am)
|
||||
if err != nil {
|
||||
t.Error("failed to init testing account")
|
||||
}
|
||||
|
||||
account.NameServerGroups[testCase.existingNSGroup.ID] = testCase.existingNSGroup
|
||||
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
t.Error("account should be saved")
|
||||
}
|
||||
|
||||
updatedRoute, err := am.UpdateNameServerGroup(account.Id, testCase.nsGroupID, testCase.operations)
|
||||
testCase.errFunc(t, err)
|
||||
|
||||
if !testCase.shouldCreate {
|
||||
return
|
||||
}
|
||||
|
||||
testCase.expectedNSGroup.ID = updatedRoute.ID
|
||||
|
||||
if !testCase.expectedNSGroup.IsEqual(updatedRoute) {
|
||||
t.Errorf("new nameserver group didn't match expected group:\nGot %#v\nExpected:%#v\n", updatedRoute, testCase.expectedNSGroup)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNameServerGroup(t *testing.T) {
|
||||
nsGroupID := "testingNSGroup"
|
||||
|
||||
testingNSGroup := &nbdns.NameServerGroup{
|
||||
ID: nsGroupID,
|
||||
Name: "super",
|
||||
Description: "super",
|
||||
NameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.1.1"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("1.1.2.2"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
Groups: []string{group1ID},
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
am, err := createNSManager(t)
|
||||
if err != nil {
|
||||
t.Error("failed to create account manager")
|
||||
}
|
||||
|
||||
account, err := initTestNSAccount(t, am)
|
||||
if err != nil {
|
||||
t.Error("failed to init testing account")
|
||||
}
|
||||
|
||||
account.NameServerGroups[testingNSGroup.ID] = testingNSGroup
|
||||
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
t.Error("failed to save account")
|
||||
}
|
||||
|
||||
err = am.DeleteNameServerGroup(account.Id, testingNSGroup.ID)
|
||||
if err != nil {
|
||||
t.Error("deleting nameserver group failed with error: ", err)
|
||||
}
|
||||
|
||||
savedAccount, err := am.Store.GetAccount(account.Id)
|
||||
if err != nil {
|
||||
t.Error("failed to retrieve saved account with error: ", err)
|
||||
}
|
||||
|
||||
_, found := savedAccount.NameServerGroups[testingNSGroup.ID]
|
||||
if found {
|
||||
t.Error("nameserver group shouldn't be found after delete")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNameServerGroup(t *testing.T) {
|
||||
|
||||
am, err := createNSManager(t)
|
||||
if err != nil {
|
||||
t.Error("failed to create account manager")
|
||||
}
|
||||
|
||||
account, err := initTestNSAccount(t, am)
|
||||
if err != nil {
|
||||
t.Error("failed to init testing account")
|
||||
}
|
||||
|
||||
foundGroup, err := am.GetNameServerGroup(account.Id, existingNSGroupID)
|
||||
if err != nil {
|
||||
t.Error("getting existing nameserver group failed with error: ", err)
|
||||
}
|
||||
|
||||
if foundGroup == nil {
|
||||
t.Error("got a nil group while getting nameserver group with ID")
|
||||
}
|
||||
|
||||
_, err = am.GetNameServerGroup(account.Id, "not existing")
|
||||
if err == nil {
|
||||
t.Error("getting not existing nameserver group should return error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||
store, err := createNSStore(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return BuildManager(store, NewPeersUpdateManager(), nil)
|
||||
}
|
||||
|
||||
func createNSStore(t *testing.T) (Store, error) {
|
||||
dataDir := t.TempDir()
|
||||
store, err := NewStore(dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return store, nil
|
||||
}
|
||||
|
||||
func initTestNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, error) {
|
||||
peer1 := &Peer{
|
||||
Key: nsGroupPeer1Key,
|
||||
Name: "test-host1@netbird.io",
|
||||
Meta: PeerSystemMeta{
|
||||
Hostname: "test-host1@netbird.io",
|
||||
GoOS: "linux",
|
||||
Kernel: "Linux",
|
||||
Core: "21.04",
|
||||
Platform: "x86_64",
|
||||
OS: "Ubuntu",
|
||||
WtVersion: "development",
|
||||
UIVersion: "development",
|
||||
},
|
||||
}
|
||||
peer2 := &Peer{
|
||||
Key: nsGroupPeer2Key,
|
||||
Name: "test-host2@netbird.io",
|
||||
Meta: PeerSystemMeta{
|
||||
Hostname: "test-host2@netbird.io",
|
||||
GoOS: "linux",
|
||||
Kernel: "Linux",
|
||||
Core: "21.04",
|
||||
Platform: "x86_64",
|
||||
OS: "Ubuntu",
|
||||
WtVersion: "development",
|
||||
UIVersion: "development",
|
||||
},
|
||||
}
|
||||
existingNSGroup := nbdns.NameServerGroup{
|
||||
ID: existingNSGroupID,
|
||||
Name: existingNSGroupName,
|
||||
Description: "",
|
||||
NameServers: []nbdns.NameServer{
|
||||
{
|
||||
IP: netip.MustParseAddr("8.8.8.8"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
{
|
||||
IP: netip.MustParseAddr("8.8.4.4"),
|
||||
NSType: nbdns.UDPNameServerType,
|
||||
Port: nbdns.DefaultDNSPort,
|
||||
},
|
||||
},
|
||||
Groups: []string{group1ID},
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
accountID := "testingAcc"
|
||||
userID := "testingUser"
|
||||
domain := "example.com"
|
||||
|
||||
account := newAccountWithId(accountID, userID, domain)
|
||||
|
||||
account.NameServerGroups[existingNSGroup.ID] = &existingNSGroup
|
||||
|
||||
defaultGroup, err := account.GetGroupAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newGroup1 := defaultGroup.Copy()
|
||||
newGroup1.ID = group1ID
|
||||
newGroup2 := defaultGroup.Copy()
|
||||
newGroup2.ID = group2ID
|
||||
|
||||
account.Groups[newGroup1.ID] = newGroup1
|
||||
account.Groups[newGroup2.ID] = newGroup2
|
||||
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = am.AddPeer("", userID, peer1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = am.AddPeer("", userID, peer2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
Loading…
Reference in New Issue