[management] Add structs for new networks concept (#3006)

This commit is contained in:
Pascal Fischer
2024-12-09 19:25:58 +01:00
committed by GitHub
parent 2147bf75eb
commit 623fcb0535
8 changed files with 358 additions and 43 deletions

View File

@@ -0,0 +1,19 @@
package networks
import "github.com/rs/xid"
type Network struct {
ID string `gorm:"index"`
AccountID string `gorm:"index"`
Name string
Description string
}
func NewNetwork(accountId, name, description string) *Network {
return &Network{
ID: xid.New().String(),
AccountID: accountId,
Name: name,
Description: description,
}
}

View File

@@ -0,0 +1,68 @@
package networks
import (
"errors"
"fmt"
"net"
"regexp"
"strings"
"github.com/rs/xid"
)
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:"index"`
NetworkID string `gorm:"index"`
AccountID string `gorm:"index"`
Type NetworkResourceType
Address string
}
func NewNetworkResource(accountID, networkID, address string) (*NetworkResource, error) {
resourceType, err := getResourceType(address)
if err != nil {
return nil, fmt.Errorf("invalid address: %w", err)
}
return &NetworkResource{
ID: xid.New().String(),
AccountID: accountID,
NetworkID: networkID,
Type: resourceType,
Address: address,
}, nil
}
// getResourceType returns the type of the resource based on the address
func getResourceType(address string) (NetworkResourceType, error) {
if ip, cidr, err := net.ParseCIDR(address); err == nil {
ones, _ := cidr.Mask.Size()
if strings.HasSuffix(address, "/32") || (ip != nil && ones == 32) {
return host, nil
}
return subnet, nil
}
if net.ParseIP(address) != nil {
return host, nil
}
domainRegex := regexp.MustCompile(`^(\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`)
if domainRegex.MatchString(address) {
return domain, nil
}
return "", errors.New("not a host, subnet, or domain")
}

View File

@@ -0,0 +1,41 @@
package networks
import (
"testing"
)
func TestGetResourceType(t *testing.T) {
tests := []struct {
input string
expectedType NetworkResourceType
expectedErr bool
}{
// Valid host IPs
{"1.1.1.1", host, false},
{"1.1.1.1/32", host, false},
// Valid subnets
{"192.168.1.0/24", subnet, false},
{"10.0.0.0/16", subnet, false},
// Valid domains
{"example.com", domain, false},
{"*.example.com", domain, false},
{"sub.example.com", domain, false},
// Invalid inputs
{"invalid", "", true},
{"1.1.1.1/abc", "", true},
{"1234", "", true},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result, err := getResourceType(tt.input)
if result != tt.expectedType {
t.Errorf("Expected type %v, got %v", tt.expectedType, result)
}
if tt.expectedErr && err == nil {
t.Errorf("Expected error, got nil")
}
})
}
}

View File

@@ -0,0 +1,33 @@
package networks
import (
"errors"
"github.com/rs/xid"
)
type NetworkRouter struct {
ID string `gorm:"index"`
NetworkID string `gorm:"index"`
AccountID string `gorm:"index"`
Peer string
PeerGroups []string `gorm:"serializer:json"`
Masquerade bool
Metric int
}
func NewNetworkRouter(accountID string, networkID string, peer string, peerGroups []string, masquerade bool, metric int) (*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,
}, nil
}

View File

@@ -0,0 +1,100 @@
package networks
import "testing"
func TestNewNetworkRouter(t *testing.T) {
tests := []struct {
name string
accountID string
networkID string
peer string
peerGroups []string
masquerade bool
metric int
expectedError bool
}{
// Valid cases
{
name: "Valid with peer only",
networkID: "network-1",
accountID: "account-1",
peer: "peer-1",
peerGroups: nil,
masquerade: true,
metric: 100,
expectedError: false,
},
{
name: "Valid with peerGroups only",
networkID: "network-2",
accountID: "account-2",
peer: "",
peerGroups: []string{"group-1", "group-2"},
masquerade: false,
metric: 200,
expectedError: false,
},
{
name: "Valid with no peer or peerGroups",
networkID: "network-3",
accountID: "account-3",
peer: "",
peerGroups: nil,
masquerade: true,
metric: 300,
expectedError: false,
},
// Invalid cases
{
name: "Invalid with both peer and peerGroups",
networkID: "network-4",
accountID: "account-4",
peer: "peer-2",
peerGroups: []string{"group-3"},
masquerade: false,
metric: 400,
expectedError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
router, err := NewNetworkRouter(tt.accountID, tt.networkID, tt.peer, tt.peerGroups, tt.masquerade, tt.metric)
if tt.expectedError && err == nil {
t.Fatalf("Expected an error, got nil")
}
if tt.expectedError == false {
if router == nil {
t.Fatalf("Expected a NetworkRouter object, got nil")
}
if router.AccountID != tt.accountID {
t.Errorf("Expected AccountID %s, got %s", tt.accountID, router.AccountID)
}
if router.NetworkID != tt.networkID {
t.Errorf("Expected NetworkID %s, got %s", tt.networkID, router.NetworkID)
}
if router.Peer != tt.peer {
t.Errorf("Expected Peer %s, got %s", tt.peer, router.Peer)
}
if len(router.PeerGroups) != len(tt.peerGroups) {
t.Errorf("Expected PeerGroups %v, got %v", tt.peerGroups, router.PeerGroups)
}
if router.Masquerade != tt.masquerade {
t.Errorf("Expected Masquerade %v, got %v", tt.masquerade, router.Masquerade)
}
if router.Metric != tt.metric {
t.Errorf("Expected Metric %d, got %d", tt.metric, router.Metric)
}
}
})
}
}