Merge pull request #10 from tim-beatham/9-add-ci-support

9 add ci support
This commit is contained in:
Tim Beatham 2023-11-05 18:07:52 +00:00 committed by GitHub
commit 4dc85f3861
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1941 additions and 156 deletions

View File

@ -9,7 +9,6 @@ import (
ctrlserver "github.com/tim-beatham/wgmesh/pkg/ctrlserver"
"github.com/tim-beatham/wgmesh/pkg/ipc"
logging "github.com/tim-beatham/wgmesh/pkg/log"
"github.com/tim-beatham/wgmesh/pkg/middleware"
"github.com/tim-beatham/wgmesh/pkg/robin"
"github.com/tim-beatham/wgmesh/pkg/sync"
"github.com/tim-beatham/wgmesh/pkg/timestamp"
@ -37,12 +36,10 @@ func main() {
var robinRpc robin.WgRpc
var robinIpc robin.IpcHandler
var authProvider middleware.AuthRpcProvider
var syncProvider sync.SyncServiceImpl
ctrlServerParams := ctrlserver.NewCtrlServerParams{
Conf: conf,
AuthProvider: &authProvider,
CtrlProvider: &robinRpc,
SyncProvider: &syncProvider,
Client: client,

View File

@ -85,16 +85,6 @@ func NewCrdtNodeManager(params *NewCrdtNodeMangerParams) (*CrdtMeshManager, erro
return &manager, nil
}
func (c *CrdtMeshManager) removeNode(endpoint string) error {
err := c.doc.Path("nodes").Map().Delete(endpoint)
if err != nil {
return err
}
return nil
}
// GetNode: returns a mesh node crdt.Close releases resources used by a Client.
func (m *CrdtMeshManager) GetNode(endpoint string) (*MeshNodeCrdt, error) {
node, err := m.doc.Path("nodes").Map().Get(endpoint)
@ -176,7 +166,7 @@ func (m *CrdtMeshManager) SetDescription(nodeId string, description string) erro
}
if node.Kind() != automerge.KindMap {
return errors.New(fmt.Sprintf("%s does not exist", nodeId))
return fmt.Errorf("%s does not exist", nodeId)
}
err = node.Map().Set("description", description)
@ -197,6 +187,10 @@ func (m *CrdtMeshManager) AddRoutes(nodeId string, routes ...string) error {
return err
}
if nodeVal.Kind() != automerge.KindMap {
return fmt.Errorf("node does not exist")
}
routeMap, err := nodeVal.Map().Get("routes")
if err != nil {

View File

@ -0,0 +1,366 @@
package crdt
import (
"slices"
"strings"
"testing"
"time"
"github.com/tim-beatham/wgmesh/pkg/conf"
"github.com/tim-beatham/wgmesh/pkg/lib"
"github.com/tim-beatham/wgmesh/pkg/mesh"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type TestParams struct {
manager *CrdtMeshManager
}
func setUpTests() *TestParams {
manager, _ := NewCrdtNodeManager(&NewCrdtNodeMangerParams{
MeshId: "timsmesh123",
DevName: "wg0",
Port: 5000,
Client: nil,
Conf: conf.WgMeshConfiguration{},
})
return &TestParams{
manager: manager,
}
}
func getTestNode() mesh.MeshNode {
return &MeshNodeCrdt{
HostEndpoint: "public-endpoint:8080",
WgEndpoint: "public-endpoint:21906",
WgHost: "3e9a:1fb3:5e50:8173:9690:f917:b1ab:d218/128",
PublicKey: "AAAAAAAAAAAA",
Timestamp: time.Now().Unix(),
Description: "A node that we are adding",
}
}
func getTestNode2() mesh.MeshNode {
return &MeshNodeCrdt{
HostEndpoint: "public-endpoint:8081",
WgEndpoint: "public-endpoint:21907",
WgHost: "3e9a:1fb3:5e50:8173:9690:f917:b1ab:d219/128",
PublicKey: "BBBBBBBBB",
Timestamp: time.Now().Unix(),
Description: "A node that we are adding",
}
}
func TestAddNodeNodeExists(t *testing.T) {
testParams := setUpTests()
testParams.manager.AddNode(getTestNode())
node, err := testParams.manager.GetNode("public-endpoint:8080")
if err != nil {
t.Error(err)
}
if node == nil {
t.Fatalf(`node not added to the mesh when it should be`)
}
}
func TestAddNodeAddRoute(t *testing.T) {
testParams := setUpTests()
testNode := getTestNode()
testParams.manager.AddNode(testNode)
testParams.manager.AddRoutes(testNode.GetHostEndpoint(), "fd:1c64:1d00::/48")
updatedNode, err := testParams.manager.GetNode(testNode.GetHostEndpoint())
if err != nil {
t.Error(err)
}
if updatedNode == nil {
t.Fatalf(`Node does not exist in the mesh`)
}
routes := updatedNode.GetRoutes()
if !slices.Contains(routes, "fd:1c64:1d00::/48") {
t.Fatal("Route node not added")
}
if len(routes) != 1 {
t.Fatal(`Route length mismatch`)
}
}
func TestGetMeshIdReturnsTheMeshId(t *testing.T) {
testParams := setUpTests()
if len(testParams.manager.GetMeshId()) == 0 {
t.Fatal(`Meshid is less than 0`)
}
}
// Add 2 nodes to the mesh and then get the mesh.s
// It should return the 2 nodes that have been added to the mesh
func TestAdd2NodesGetMesh(t *testing.T) {
testParams := setUpTests()
node1 := getTestNode()
node2 := getTestNode2()
testParams.manager.AddNode(node1)
testParams.manager.AddNode(node2)
mesh, err := testParams.manager.GetMesh()
if err != nil {
t.Error(err)
}
nodes := mesh.GetNodes()
if len(nodes) != 2 {
t.Fatalf(`Mismatch in node slice`)
}
for _, node := range nodes {
if node.GetHostEndpoint() != node1.GetHostEndpoint() && node.GetHostEndpoint() != node2.GetHostEndpoint() {
t.Fatalf(`Node should not exist`)
}
}
}
func TestSaveMeshReturnsMeshBytes(t *testing.T) {
testParams := setUpTests()
node1 := getTestNode()
testParams.manager.AddNode(node1)
bytes := testParams.manager.Save()
if len(bytes) <= 0 {
t.Fatalf(`bytes in the mesh is less than 0`)
}
}
func TestSaveMeshThenLoad(t *testing.T) {
testParams := setUpTests()
testParams2 := setUpTests()
node1 := getTestNode()
testParams.manager.AddNode(node1)
bytes := testParams.manager.Save()
err := testParams2.manager.Load(bytes)
if err != nil {
t.Error(err)
}
if len(bytes) <= 0 {
t.Fatalf(`bytes in the mesh is less than 0`)
}
mesh2, err := testParams2.manager.GetMesh()
if err != nil {
t.Error(err)
}
nodes := mesh2.GetNodes()
if lib.MapValues(nodes)[0].GetHostEndpoint() != node1.GetHostEndpoint() {
t.Fatalf(`Node should be in the list of nodes`)
}
}
func TestLengthNoNodes(t *testing.T) {
testParams := setUpTests()
if testParams.manager.Length() != 0 {
t.Fatalf(`Number of nodes should be 0`)
}
}
func TestLength1Node(t *testing.T) {
testParams := setUpTests()
node := getTestNode()
testParams.manager.AddNode(node)
if testParams.manager.Length() != 1 {
t.Fatalf(`Number of nodes should be 1`)
}
}
func TestLengthMultipleNodes(t *testing.T) {
testParams := setUpTests()
node := getTestNode()
node1 := getTestNode2()
testParams.manager.AddNode(node)
testParams.manager.AddNode(node1)
if testParams.manager.Length() != 2 {
t.Fatalf(`Number of nodes should be 2`)
}
}
func TestHasChangesNoChanges(t *testing.T) {
testParams := setUpTests()
if testParams.manager.HasChanges() {
t.Fatalf(`Should not have changes just created document`)
}
}
func TestHasChangesChanges(t *testing.T) {
testParams := setUpTests()
node := getTestNode()
testParams.manager.AddNode(node)
if !testParams.manager.HasChanges() {
t.Fatalf(`Should have changes just added node`)
}
}
func TestHasChangesSavedChanges(t *testing.T) {
testParams := setUpTests()
node := getTestNode()
testParams.manager.AddNode(node)
testParams.manager.SaveChanges()
if testParams.manager.HasChanges() {
t.Fatalf(`Should not have changes just saved document`)
}
}
func TestUpdateTimeStampNodeDoesNotExist(t *testing.T) {
testParams := setUpTests()
err := testParams.manager.UpdateTimeStamp("AAAAAA")
if err == nil {
t.Fatalf(`Error should have returned`)
}
}
func TestUpdateTimeStampNodeExists(t *testing.T) {
testParams := setUpTests()
node := getTestNode()
testParams.manager.AddNode(node)
err := testParams.manager.UpdateTimeStamp(node.GetHostEndpoint())
if err != nil {
t.Error(err)
}
}
func TestSetDescriptionNodeDoesNotExist(t *testing.T) {
testParams := setUpTests()
err := testParams.manager.SetDescription("AAAAA", "Bob 123")
if err == nil {
t.Fatalf(`Error should have returned`)
}
}
func TestSetDescriptionNodeExists(t *testing.T) {
testParams := setUpTests()
node := getTestNode()
err := testParams.manager.SetDescription(node.GetHostEndpoint(), "Bob 123")
if err == nil {
t.Fatalf(`Error should have returned`)
}
}
func TestAddRoutesNodeDoesNotExist(t *testing.T) {
testParams := setUpTests()
err := testParams.manager.AddRoutes("AAAAA", "fd:1c64:1d00::/48")
if err == nil {
t.Error(err)
}
}
func TestCompareComparesByPublicKey(t *testing.T) {
node := getTestNode().(*MeshNodeCrdt)
node2 := getTestNode2().(*MeshNodeCrdt)
if node.Compare(node2) != -1 {
t.Fatalf(`node is alphabetically before node2`)
}
if node2.Compare(node) != 1 {
t.Fatalf(`node is alphabetical;y before node2`)
}
if node.Compare(node) != 0 {
t.Fatalf(`node is equal to node`)
}
}
func TestGetHostEndpoint(t *testing.T) {
node := getTestNode()
if (node.(*MeshNodeCrdt)).HostEndpoint != node.GetHostEndpoint() {
t.Fatalf(`get hostendpoint should get the host endpoint`)
}
}
func TestGetPublicKey(t *testing.T) {
key1, _ := wgtypes.GenerateKey()
node := getTestNode()
node.(*MeshNodeCrdt).PublicKey = key1.String()
pubKey, err := node.GetPublicKey()
if err != nil {
t.Error(err)
}
if pubKey.String() != key1.String() {
t.Fatalf(`Expected %s got %s`, key1.String(), pubKey.String())
}
}
func TestGetWgEndpoint(t *testing.T) {
node := getTestNode()
if node.(*MeshNodeCrdt).WgEndpoint != node.GetWgEndpoint() {
t.Fatal(`Did not return the correct wgEndpoint`)
}
}
func TestGetWgHost(t *testing.T) {
node := getTestNode()
ip := node.GetWgHost()
if node.(*MeshNodeCrdt).WgHost != ip.String() {
t.Fatal(`Did not parse WgHost correctly`)
}
}
func TestGetTimeStamp(t *testing.T) {
node := getTestNode()
if node.(*MeshNodeCrdt).Timestamp != node.GetTimeStamp() {
t.Fatal(`Did not return return the correct timestamp`)
}
}
func TestGetIdentifierDoesNotContainPrefix(t *testing.T) {
node := getTestNode()
if strings.Contains(node.GetIdentifier(), "/128") {
t.Fatal(`Identifier should not contain prefix`)
}
}

View File

@ -8,6 +8,14 @@ import (
"gopkg.in/yaml.v3"
)
type WgMeshConfigurationError struct {
msg string
}
func (m *WgMeshConfigurationError) Error() string {
return m.msg
}
type WgMeshConfiguration struct {
// CertificatePath is the path to the certificate to use in mTLS
CertificatePath string `yaml:"certificatePath"`
@ -33,6 +41,70 @@ type WgMeshConfiguration struct {
KeepAliveRate int `yaml:"keepAliveRate"`
}
func ValidateConfiguration(c *WgMeshConfiguration) error {
if len(c.CertificatePath) == 0 {
return &WgMeshConfigurationError{
msg: "A public certificate must be specified for mTLS",
}
}
if len(c.PrivateKeyPath) == 0 {
return &WgMeshConfigurationError{
msg: "A private key must be specified for mTLS",
}
}
if len(c.CaCertificatePath) == 0 {
return &WgMeshConfigurationError{
msg: "A ca certificate must be specified for mTLS",
}
}
if len(c.GrpcPort) == 0 {
return &WgMeshConfigurationError{
msg: "A grpc port must be specified",
}
}
if c.ClusterSize <= 0 {
return &WgMeshConfigurationError{
msg: "A cluster size must not be 0",
}
}
if c.SyncRate <= 0 {
return &WgMeshConfigurationError{
msg: "SyncRate cannot be negative",
}
}
if c.BranchRate <= 0 {
return &WgMeshConfigurationError{
msg: "Branch rate cannot be negative",
}
}
if c.InfectionCount <= 0 {
return &WgMeshConfigurationError{
msg: "Infection count cannot be less than 1",
}
}
if c.KeepAliveRate <= 0 {
return &WgMeshConfigurationError{
msg: "KeepAliveRate cannot be less than negative",
}
}
if c.InterClusterChance <= 0 {
return &WgMeshConfigurationError{
msg: "Intercluster chance cannot be less than 0",
}
}
return nil
}
// ParseConfiguration parses the mesh configuration
func ParseConfiguration(filePath string) (*WgMeshConfiguration, error) {
var conf WgMeshConfiguration
@ -51,5 +123,5 @@ func ParseConfiguration(filePath string) (*WgMeshConfiguration, error) {
return nil, err
}
return &conf, nil
return &conf, ValidateConfiguration(&conf)
}

130
pkg/conf/conf_test.go Normal file
View File

@ -0,0 +1,130 @@
package conf
import "testing"
func getExampleConfiguration() *WgMeshConfiguration {
return &WgMeshConfiguration{
CertificatePath: "./cert/cert.pem",
PrivateKeyPath: "./cert/key.pem",
CaCertificatePath: "./cert/ca.pems",
SkipCertVerification: true,
GrpcPort: "8080",
AdvertiseRoutes: true,
Endpoint: "localhost",
ClusterSize: 1,
SyncRate: 1,
InterClusterChance: 0.1,
BranchRate: 2,
KeepAliveRate: 1,
InfectionCount: 1,
}
}
func TestConfigurationCertificatePathEmpty(t *testing.T) {
conf := getExampleConfiguration()
conf.CertificatePath = ""
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func TestConfigurationPrivateKeyPathEmpty(t *testing.T) {
conf := getExampleConfiguration()
conf.PrivateKeyPath = ""
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func TestConfigurationCaCertificatePathEmpty(t *testing.T) {
conf := getExampleConfiguration()
conf.CaCertificatePath = ""
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func TestConfigurationGrpcPortEmpty(t *testing.T) {
conf := getExampleConfiguration()
conf.GrpcPort = ""
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func TestClusterSizeZero(t *testing.T) {
conf := getExampleConfiguration()
conf.ClusterSize = 0
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func SyncRateZero(t *testing.T) {
conf := getExampleConfiguration()
conf.SyncRate = 0
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func BranchRateZero(t *testing.T) {
conf := getExampleConfiguration()
conf.BranchRate = 0
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func InfectionCountZero(t *testing.T) {
conf := getExampleConfiguration()
conf.InfectionCount = 0
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func KeepAliveRateZero(t *testing.T) {
conf := getExampleConfiguration()
conf.KeepAliveRate = 0
err := ValidateConfiguration(conf)
if err == nil {
t.Fatal(`error should be thrown`)
}
}
func TestValidCOnfiguration(t *testing.T) {
conf := getExampleConfiguration()
err := ValidateConfiguration(conf)
if err != nil {
t.Error(err)
}
}

View File

@ -23,19 +23,21 @@ func binarySearch(global []string, selfId string, groupSize int) (int, int) {
lower := 0
higher := len(global) - 1
mid := lower + (lower+higher)/2
mid := (lower + higher) / 2
for (higher+1)-lower > groupSize {
if global[mid] <= selfId {
lower = mid
if global[mid] < selfId {
lower = mid + 1
} else if global[mid] > selfId {
higher = mid - 1
} else {
higher = mid
break
}
mid = lower + (lower+higher)/2
mid = (lower + higher) / 2
}
return lower, higher + 1
return lower, int(math.Min(float64(lower+groupSize), float64(len(global))))
}
// GetNeighbours return the neighbours 'nearest' to you. In this implementation the

116
pkg/conn/cluster_test.go Normal file
View File

@ -0,0 +1,116 @@
package conn
import (
"math/rand"
"slices"
"testing"
)
func TestGetNeighboursClusterSizeTwo(t *testing.T) {
cluster := &ConnClusterImpl{
clusterSize: 2,
}
neighbours := []string{
"a",
"b",
"c",
"d",
}
result := cluster.GetNeighbours(neighbours, "b")
if len(result) != 2 {
t.Fatalf(`neighbour length should be 2`)
}
if result[0] != "a" && result[1] != "b" {
t.Fatalf(`Expected value b`)
}
}
func TestGetNeighboursGlobalListLessThanClusterSize(t *testing.T) {
cluster := &ConnClusterImpl{
clusterSize: 4,
}
neighbours := []string{
"a",
"b",
"c",
}
result := cluster.GetNeighbours(neighbours, "a")
if len(result) != 3 {
t.Fatalf(`neighbour length should be 3`)
}
slices.Sort(result)
if !slices.Equal(result, neighbours) {
t.Fatalf(`Cluster and neighbours should be equal`)
}
}
func TestGetNeighboursClusterSize4(t *testing.T) {
cluster := &ConnClusterImpl{
clusterSize: 4,
}
neighbours := []string{
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o",
}
result := cluster.GetNeighbours(neighbours, "k")
if len(result) != 4 {
t.Fatalf(`cluster size must be 4`)
}
slices.Sort(result)
if !slices.Equal(neighbours[8:12], result) {
t.Fatalf(`Cluster should be i, j, k, l`)
}
}
func TestGetNeighboursClusterSize4OneReturned(t *testing.T) {
cluster := &ConnClusterImpl{
clusterSize: 4,
}
neighbours := []string{
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o",
}
result := cluster.GetNeighbours(neighbours, "o")
if len(result) != 3 {
t.Fatalf(`Cluster should be of length 3`)
}
if !slices.Equal(neighbours[12:15], result) {
t.Fatalf(`Cluster should be m, n, o`)
}
}
func TestInterClusterNotInCluster(t *testing.T) {
rand.Seed(1)
cluster := &ConnClusterImpl{
clusterSize: 4,
}
global := []string{
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o",
}
neighbours := cluster.GetNeighbours(global, "c")
interCluster := cluster.GetInterCluster(global, "c")
if slices.Contains(neighbours, interCluster) {
t.Fatalf(`intercluster cannot be in your cluster`)
}
}

View File

@ -18,6 +18,8 @@ type PeerConnection interface {
GetClient() (*grpc.ClientConn, error)
}
type PeerConnectionFactory = func(clientConfig *tls.Config, server string) (PeerConnection, error)
// WgCtrlConnection implements PeerConnection.
type WgCtrlConnection struct {
clientConfig *tls.Config
@ -26,12 +28,12 @@ type WgCtrlConnection struct {
}
// NewWgCtrlConnection creates a new instance of a WireGuard control connection
func NewWgCtrlConnection(clientConfig *tls.Config, server string) (*WgCtrlConnection, error) {
func NewWgCtrlConnection(clientConfig *tls.Config, server string) (PeerConnection, error) {
var conn WgCtrlConnection
conn.clientConfig = clientConfig
conn.endpoint = server
if err := conn.createGrpcConn(); err != nil {
if err := conn.CreateGrpcConnection(); err != nil {
return nil, err
}
@ -39,7 +41,7 @@ func NewWgCtrlConnection(clientConfig *tls.Config, server string) (*WgCtrlConnec
}
// ConnectWithToken: Connects to a new gRPC peer given the address of the other server.
func (c *WgCtrlConnection) createGrpcConn() error {
func (c *WgCtrlConnection) CreateGrpcConnection() error {
conn, err := grpc.Dial(c.endpoint,
grpc.WithTransportCredentials(credentials.NewTLS(c.clientConfig)))
@ -62,7 +64,7 @@ func (c *WgCtrlConnection) GetClient() (*grpc.ClientConn, error) {
var err error = nil
if c.conn == nil {
err = errors.New("The client's config does not exist")
err = errors.New("the client's config does not exist")
}
return c.conn, err

View File

@ -34,10 +34,11 @@ type ConnectionManagerImpl struct {
clientConnections map[string]PeerConnection
serverConfig *tls.Config
clientConfig *tls.Config
connFactory PeerConnectionFactory
}
// Create a new instance of a connection manager.
type NewConnectionManageParams struct {
type NewConnectionManagerParams struct {
// The path to the certificate
CertificatePath string
// The private key of the node
@ -45,11 +46,12 @@ type NewConnectionManageParams struct {
// Whether or not to skip certificate verification
SkipCertVerification bool
CaCert string
ConnFactory PeerConnectionFactory
}
// NewConnectionManager: Creates a new instance of a ConnectionManager or an error
// if something went wrong.
func NewConnectionManager(params *NewConnectionManageParams) (ConnectionManager, error) {
func NewConnectionManager(params *NewConnectionManagerParams) (ConnectionManager, error) {
cert, err := tls.LoadX509KeyPair(params.CertificatePath, params.PrivateKey)
if err != nil {
@ -94,10 +96,12 @@ func NewConnectionManager(params *NewConnectionManageParams) (ConnectionManager,
}
connections := make(map[string]PeerConnection)
connMgr := ConnectionManagerImpl{sync.RWMutex{},
connMgr := ConnectionManagerImpl{
sync.RWMutex{},
connections,
serverConfig,
clientConfig,
params.ConnFactory,
}
return &connMgr, nil
@ -127,7 +131,7 @@ func (m *ConnectionManagerImpl) AddConnection(endPoint string) (PeerConnection,
return conn, nil
}
connections, err := NewWgCtrlConnection(m.clientConfig, endPoint)
connections, err := m.connFactory(m.clientConfig, endPoint)
if err != nil {
return nil, err

View File

@ -0,0 +1,145 @@
package conn
import (
"crypto/tls"
"errors"
"log"
"testing"
)
func getConnectionManagerParams() *NewConnectionManagerParams {
return &NewConnectionManagerParams{
CertificatePath: "./test/cert.pem",
PrivateKey: "./test/priv.pem",
CaCert: "./test/cacert.pem",
SkipCertVerification: false,
ConnFactory: MockFactory,
}
}
func TestNewConnectionManagerCertificatePathDoesNotExist(t *testing.T) {
params := getConnectionManagerParams()
params.CertificatePath = "./cert/sdfjdskjdsjkd.pem"
_, err := NewConnectionManager(params)
if err == nil {
t.Fatalf(`Expected error as certificate does not exist`)
}
}
func TestNewConnectionManagerPrivateKeyDoesNotExist(t *testing.T) {
params := getConnectionManagerParams()
params.PrivateKey = "./cert/sdjdjdks.pem"
_, err := NewConnectionManager(params)
if err == nil {
t.Fatalf(`Expected error as private key does not exist`)
}
}
func TestNewConnectionManagerCACertDoesNotExistAndVerify(t *testing.T) {
params := getConnectionManagerParams()
params.CaCert = "./cert/sdjdsjdksjdks.pem"
params.SkipCertVerification = false
_, err := NewConnectionManager(params)
if err == nil {
t.Fatal(`Expected error as ca cert does not exist and skip is false`)
}
}
func TestNewConnectionManagerCACertDoesNotExistAndNotVerify(t *testing.T) {
params := getConnectionManagerParams()
params.CaCert = ""
params.SkipCertVerification = true
_, err := NewConnectionManager(params)
if err != nil {
t.Fatal(`an error should not be thrown`)
}
}
func TestGetConnectionConnectionDoesNotExistAddsConnection(t *testing.T) {
params := getConnectionManagerParams()
m, _ := NewConnectionManager(params)
conn, err := m.GetConnection("abc-123.com")
if err != nil {
t.Error(err)
}
if conn == nil {
t.Fatal(`the connection should not be nil`)
}
conn2, _ := m.GetConnection("abc-123.com")
if conn != conn2 {
log.Fatalf(`should return the same connection instance`)
}
}
func TestAddConnectionThrowsAnErrorIfFactoryThrowsError(t *testing.T) {
params := getConnectionManagerParams()
params.ConnFactory = func(clientConfig *tls.Config, server string) (PeerConnection, error) {
return nil, errors.New("this is an error")
}
m, _ := NewConnectionManager(params)
_, err := m.AddConnection("abc-123.com")
if err == nil || err.Error() != "this is an error" {
t.Error(err)
}
}
func TestAddConnectionConnectionDoesNotExist(t *testing.T) {
params := getConnectionManagerParams()
m, _ := NewConnectionManager(params)
conn, err := m.AddConnection("abc-123.com")
if err != nil {
t.Error(err)
}
if conn == nil {
t.Fatal(`connection should not be nil`)
}
conn1, _ := m.GetConnection("abc-123.com")
if conn != conn1 {
t.Fatal(`underlying connections should be the same`)
}
}
func TestHasConnectionConnectionDoesNotExist(t *testing.T) {
params := getConnectionManagerParams()
m, _ := NewConnectionManager(params)
if m.HasConnection("abc-123.com") {
t.Fatal(`should return that the connection does not exist`)
}
}
func TestHasConnectionConnectionExists(t *testing.T) {
params := getConnectionManagerParams()
m, _ := NewConnectionManager(params)
m.AddConnection("abc-123.com")
if !m.HasConnection("abc-123.com") {
t.Fatal(`should return that the connection exists`)
}
}

View File

@ -16,9 +16,7 @@ type ConnectionServer struct {
// tlsConfiguration of the server
serverConfig *tls.Config
// server an instance of the grpc server
server *grpc.Server
// the authentication service to authenticate nodes
authProvider rpc.AuthenticationServer
server *grpc.Server // the authentication service to authenticate nodes
// the ctrl service to manage node
ctrlProvider rpc.MeshCtrlServerServer
// the sync service to synchronise nodes
@ -30,7 +28,6 @@ type ConnectionServer struct {
// NewConnectionServerParams contains params for creating a new connection server
type NewConnectionServerParams struct {
Conf *conf.WgMeshConfiguration
AuthProvider rpc.AuthenticationServer
CtrlProvider rpc.MeshCtrlServerServer
SyncProvider rpc.SyncServiceServer
}
@ -59,14 +56,12 @@ func NewConnectionServer(params *NewConnectionServerParams) (*ConnectionServer,
grpc.Creds(credentials.NewTLS(serverConfig)),
)
authProvider := params.AuthProvider
ctrlProvider := params.CtrlProvider
syncProvider := params.SyncProvider
connServer := ConnectionServer{
serverConfig: serverConfig,
server: server,
authProvider: authProvider,
ctrlProvider: ctrlProvider,
syncProvider: syncProvider,
Conf: params.Conf,
@ -78,7 +73,6 @@ func NewConnectionServer(params *NewConnectionServerParams) (*ConnectionServer,
// Listen for incoming requests. Returns an error if something went wrong.
func (s *ConnectionServer) Listen() error {
rpc.RegisterMeshCtrlServerServer(s.server, s.ctrlProvider)
rpc.RegisterAuthenticationServer(s.server, s.authProvider)
rpc.RegisterSyncServiceServer(s.server, s.syncProvider)

51
pkg/conn/stub.go Normal file
View File

@ -0,0 +1,51 @@
package conn
import (
"crypto/tls"
"google.golang.org/grpc"
)
type ConnectionManagerStub struct {
Endpoints map[string]PeerConnection
}
func (s *ConnectionManagerStub) AddConnection(endPoint string) (PeerConnection, error) {
mock := &PeerConnectionMock{}
s.Endpoints[endPoint] = mock
return mock, nil
}
func (s *ConnectionManagerStub) GetConnection(endPoint string) (PeerConnection, error) {
endpoint, ok := s.Endpoints[endPoint]
if !ok {
return s.AddConnection(endPoint)
}
return endpoint, nil
}
func (s *ConnectionManagerStub) HasConnection(endPoint string) bool {
_, ok := s.Endpoints[endPoint]
return ok
}
func (s *ConnectionManagerStub) Close() error {
return nil
}
type PeerConnectionMock struct {
}
func (c *PeerConnectionMock) Close() error {
return nil
}
func (c *PeerConnectionMock) GetClient() (*grpc.ClientConn, error) {
return &grpc.ClientConn{}, nil
}
var MockFactory PeerConnectionFactory = func(clientConfig *tls.Config, server string) (PeerConnection, error) {
return &PeerConnectionMock{}, nil
}

21
pkg/conn/test/cacert.pem Normal file
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUDRIRI8UnHU2a4znsun0gxFwlrFQwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzEwMjcxNTIzMDZaFw0yNDEw
MjYxNTIzMDZaMEUxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDJ5hOmzilimA/zM5hYP7CQf4iRmICtSbVLgt6/rTDP
p3JsGGQWZ4pZNofzGnGa7aEMoXS2Ztl7GzZbr1p4+rd6MBbVt8XZ/hP+X4zasCXi
/YubG0TYyBuAt+JrcYb0cbsTBkMXXnFcNIXDfeYFsNq+pfyJwq2ElMUUZ6SQmVhH
ovn1Wk9Fv4t2GJMhmUcObrSIoYdgo4Vf9CfQnn0PCaRf+RjspY/Kz33oyqDI6xJx
I0rfJR7f9B6ZKosfAkt4oTTfT9P8w/d1I95oBENhDkalgkdJCuNJ/AwKGxZrYf/P
aefcc91HheauObjBYPFrSn6bUj3LMJEfj4IeBK+fOZCfAgMBAAGjUzBRMB0GA1Ud
DgQWBBSpcF7jtpd9n73VM3xhPmI1GMEkFjAfBgNVHSMEGDAWgBSpcF7jtpd9n73V
M3xhPmI1GMEkFjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCK
GplAveP9nVo9zmg+/mkDpyVoo5rp64oJh4DFtm6X+EI31FmH6Cb71Kn2ZzXhQvSq
qrP7+VoGeBDxk4guJtAs/fhnuDupJG2SjsctjiFnDbSrJjWJjGhC0kuL0wcjLU5G
qUpCEJu13GkDlYHKKw0z+oLUOw+OHmvE5/sD23sKl2KxBWKItx0hwSCkGtm0RQld
8mfjOsHqJ2V/FOcHK6X2DSV1728PAhu4l/PRSB0drBA+7kdeCuWIRZw5RA/OyxvU
CuC5dfUh75MrK7KL6sZsXklsoXo8BZp4rRRUt/v1D3r/SMBJPULSGXh6QDjXQX1D
km71c3DEDyKznHTpGxPt
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJ5hOmzilimA/z
M5hYP7CQf4iRmICtSbVLgt6/rTDPp3JsGGQWZ4pZNofzGnGa7aEMoXS2Ztl7GzZb
r1p4+rd6MBbVt8XZ/hP+X4zasCXi/YubG0TYyBuAt+JrcYb0cbsTBkMXXnFcNIXD
feYFsNq+pfyJwq2ElMUUZ6SQmVhHovn1Wk9Fv4t2GJMhmUcObrSIoYdgo4Vf9CfQ
nn0PCaRf+RjspY/Kz33oyqDI6xJxI0rfJR7f9B6ZKosfAkt4oTTfT9P8w/d1I95o
BENhDkalgkdJCuNJ/AwKGxZrYf/Paefcc91HheauObjBYPFrSn6bUj3LMJEfj4Ie
BK+fOZCfAgMBAAECggEADqAjoUxC9Dj2wtPkf9QRSs5qSr3E6Iiz4OX4k+MMa6aC
I/F6YqMagw7vtz0dqK75ISybA1GdBI16mRaxU5056FiOdunqo7mDokQytG7ZN8HN
OK23hYqtb1wiw0zEjXWlqyGjf5BgXuERJZG7tYLTvcbRbftTzYxnYGyHn8/z9LBp
GsTJ5X8XMLM5+bTvg1Ovv5s0q31FCeqAuw+auHH4pBNP+ylV6dF5XOWq4HO3TJ2b
grHxWB94JZChZnDC/K+HxQ6aHJfbZ5XCoXfIaIVkoXfnyPzgjvgK+/IpHEF8f/3I
uT/NBiArTpRl29pX5flEO4R121VaW93eM1tuzL32VQKBgQD6Trctx9SYuhzgfiO7
kdefvR43Kl9SFyEw3hN3HW1cxSNGCCFotjmdem+QdtMBtUd27UJ9tuiKJC0lcCER
t3WRz4kVd/cb0eC1DPzpGHA81o1rUUR3nMr1o7aBfvQ06VAxFUrFAOPpF8nD7tI4
0CiOh7/sL1ElThA3bOPUpXkYHQKBgQDOfYbP8dppIkC8pRTnHWe0qUY0G4YXxg7r
UtTo4GYOLJeKH/MKoK8MjBDS5VN5n5TAHJ8yUVzhpWXZIPIGzNEhIRDMa56sRPgI
9mLJNs5z/ZIxd/7ZQbDHrD4T3PKeTjzVUtjXrhLowokPlPB/RMQL6ZT+qMao+3bS
fDITSfLG6wKBgBpbcZSDh1JxvpqxDagxqkfqzSS39IObZeZUbC5NzfdH1vgH4SS6
k4SOoPLQYFW8tgLC5w5/1Sq+tnZLwV+xNtMczG2TTVUDm6rU7EjLRv5RBWE4lIIX
45NMIuqt6J8ttkEE4fOurVEdLSTRoBdVa//eMYp4TQ4lkzWS5Ma+ierNAoGAYO3z
1rFFQYzerq8ffM4E3H2JgvRYodhLMJQVdavAvG6aRDBzOk3rXgxx6U3VPYZ3oSbO
ZCRlYVbu1FnuwtpqYQ7Qf+UU+vD1Ld/ax3F+wFwLwET/0KRRg6mLCm/xQ/ad/9WA
DN6d6b1H8ZSMwHFbRexEELbRaomAYZYDO6K+4DkCgYEAv5De85hPnWtAvKhPzwQi
9mtyWo/cfQgtwL8IKNu6hBHl5RXDpPgX/+pNbXLJfBPwVR3H62x1CMYJDkWVuE6/
ZjtF7FSucZMz/mR6r1GhSOXy3YLwQ6JLPjjKzvnEjahGlKwALJNL0O2ZucjsZxHE
PM4rmhRZT9opiapiltEhRm0=
-----END PRIVATE KEY-----

19
pkg/conn/test/cert.pem Normal file
View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCjCCAfICFB/Vd2eOXWdNdrakThJhFIRtZmhUMA0GCSqGSIb3DQEBCwUAMEUx
CzAJBgNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMxMDI3MTUzNDM1WhcNMjMxMTI2MTUz
NDM1WjA+MQswCQYDVQQGEwJHQjENMAsGA1UECAwERmlmZTENMAsGA1UEBwwEY2l0
eTERMA8GA1UECgwITWVzaCBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDVgcLtNU5AYfPML/mE5PyC7YYKvZn2mt6vEiJ7M/6EzYeTXFeYexD5ZqHg
ewGEd1fwiQWQsATsWd+EM4OnCAXAaNiOH6gGY7FR8CThfT+k8yIGPrl1BovzHHYS
Orekna17UFeIyFMHDPIjl4d2WiJPvmNn5PhLEppPHPBWPhl3J3sMrSbqyRuYbtta
oFIzN8mFcikixLg0SnBPtwlLC72ah9G+MF5CwEcU/E0bYbLQZXv+WhG5aw5JEzes
K2GLxVNgM0xXB7hSyLoX1wBc8DdQyLCMkOp55Hl04UKTxtVE82MiuAOVqMUuKFjR
u2a1C+/Gbk/PS5SHgenGjdZ8sZGpAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHMc
jIFG5Rn9KaVmo7E+/UAq+3ld/3y2yMHg5wq7oG8b7/z0mlSGErHdFMzo75AFLN4r
kOuiF5ItF6dRLNrG8IUFSNMGVH3b3ukw1EI8E89L8ak3CM+wpLT6GVP3BfV8ah+X
4RRix40Tmx4C81l+Lf5W10rHIdlXBCanJy/Fa0ae+S+oXFc9jeXHlK9qlgszrECT
Pa3VCR95LAIc6o9pDL2Z8tpEkSbyzvIWhp53fnC80PyXpSsFMfIw657shagBc/Ov
e7/aPpPf3V3CafJlEIraQp24MDI5ZM59lT5vhRq2AC50gelL6UPV16mVVUlGVhWE
vYyejod5i5ZbuLFOy2g=
-----END CERTIFICATE-----

28
pkg/conn/test/priv.pem Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDVgcLtNU5AYfPM
L/mE5PyC7YYKvZn2mt6vEiJ7M/6EzYeTXFeYexD5ZqHgewGEd1fwiQWQsATsWd+E
M4OnCAXAaNiOH6gGY7FR8CThfT+k8yIGPrl1BovzHHYSOrekna17UFeIyFMHDPIj
l4d2WiJPvmNn5PhLEppPHPBWPhl3J3sMrSbqyRuYbttaoFIzN8mFcikixLg0SnBP
twlLC72ah9G+MF5CwEcU/E0bYbLQZXv+WhG5aw5JEzesK2GLxVNgM0xXB7hSyLoX
1wBc8DdQyLCMkOp55Hl04UKTxtVE82MiuAOVqMUuKFjRu2a1C+/Gbk/PS5SHgenG
jdZ8sZGpAgMBAAECggEARJNAggLYhtpPPVp9WJ9ZsU3L+0AppujYND/tXkf1bD89
V+nVYq7IZWp+/MRVWPAiCSphZLb8ZdN59JK9KtVrT4D9aSymwaKcjfZFSj15xyem
Wn4j///hzGxsSe+dE1znnw9PhindbQrN7Pua8TsDATzj3bdPvoETmexwDysz765i
u4zXvxP+xAessz1OYa5IUaDXdlWOf0e1zNXWwanjRggzCeWR3lTofG49GX087oVC
Sb9ASy+AScnOlwpTdQ8sKy1r9gXmE5ey4AULVb0nJ8LDvrCoBBhKBtVE5mJHepE6
bdC9l6poL6roGvHfMAo3SmiUUT5XceqUxBtHcyHX3wKBgQD1uh+Dv0PrH3CTW9cF
bwHL1rmQNJrbDzDAaounGBe9mcot1RrBhyQAoGw1no4c+QWDAwYRuBP2+Rp6JLU/
XnEXSyN85rJN6LajlrLEr+BNmKw6ghNsnAFUZBLaJ7epRi6OjACUwmtvH6hRIef8
aMg4WiOyDT+Z4Xe81pdXb91HXwKBgQDebs3idgVEau3LCKGYnqvmUhzv8iiQiJmD
R29o2G5Xrahf3r1O5gJdGLO1DaCBtdrI7J4xUOlM935KaEYFe5B7RVGXg23tNWgb
2M+YQqu5qz61bDxhg7dGkegHrdvKNcSkV6GUSm5w9rdxJlY8+l45p/7QpSkatcbd
IRiVzMNr9wKBgQC/+Z5fbpFgYxqvdaPicdxkZShqOj71f8OlwFfEvrTlgv4KmqAh
rDP7bVm89leu2PpuZXFbbIXkgK8n1//mNyGBgkmCbjXFWlc+LSETOxixZuK/fxov
0x3S0bBM0ZTSYatD4KsfjVkj4wa8BBJbB33NUNbsZx9WWGkUlk58mD+3XwKBgQDV
mgR+n6WJQUIfwqckH+Ol517AkYSg33zEE9qKDaVQ74QMpKKY3MqSSkFw8agcR93V
K1zysOeJsPYHUEFFzJY/up6S6HSs4aebbkZUylmMkEVFBa6qWkmrLDxs+2lgsuem
hjy1YhDSzCn3L8CLCEdqCMjr5l8ltkBFZB3u5NcZmwKBgHE9ODedQm783JfvDNBb
lB/IoUjMhMR0J2vHC3zxgTU4nIK+MR0vXvA7fmZebpaQNwYrHY9gvrL0/QevOrmG
PtXlkQ9GITMxTlqfHWV5jXZuRBIGTqh1QW3tKbVAhUhNlM0XDNBmBvjKIFjxUIo3
zMRw/o4R4cIaazyVxguZbsa2
-----END PRIVATE KEY-----

View File

@ -18,7 +18,6 @@ import (
type NewCtrlServerParams struct {
Conf *conf.WgMeshConfiguration
Client *wgctrl.Client
AuthProvider rpc.AuthenticationServer
CtrlProvider rpc.MeshCtrlServerServer
SyncProvider rpc.SyncServiceServer
Querier query.Querier
@ -44,16 +43,17 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
IdGenerator: idGenerator,
IPAllocator: ipAllocator,
InterfaceManipulator: interfaceManipulator,
ConfigApplyer: mesh.NewWgMeshConfigApplyer(ctrlServer.MeshManager),
}
ctrlServer.MeshManager = mesh.NewMeshManager(meshManagerParams)
ctrlServer.Conf = params.Conf
connManagerParams := conn.NewConnectionManageParams{
connManagerParams := conn.NewConnectionManagerParams{
CertificatePath: params.Conf.CertificatePath,
PrivateKey: params.Conf.PrivateKeyPath,
SkipCertVerification: params.Conf.SkipCertVerification,
CaCert: params.Conf.CaCertificatePath,
ConnFactory: conn.NewWgCtrlConnection,
}
connMgr, err := conn.NewConnectionManager(&connManagerParams)
@ -65,7 +65,6 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
ctrlServer.ConnectionManager = connMgr
connServerParams := conn.NewConnectionServerParams{
Conf: params.Conf,
AuthProvider: params.AuthProvider,
CtrlProvider: params.CtrlProvider,
SyncProvider: params.SyncProvider,
}
@ -82,6 +81,26 @@ func NewCtrlServer(params *NewCtrlServerParams) (*MeshCtrlServer, error) {
return ctrlServer, nil
}
func (s *MeshCtrlServer) GetConfiguration() *conf.WgMeshConfiguration {
return s.Conf
}
func (s *MeshCtrlServer) GetClient() *wgctrl.Client {
return s.Client
}
func (s *MeshCtrlServer) GetQuerier() query.Querier {
return s.Querier
}
func (s *MeshCtrlServer) GetMeshManager() mesh.MeshManager {
return s.MeshManager
}
func (s *MeshCtrlServer) GetConnectionManager() conn.ConnectionManager {
return s.ConnectionManager
}
// Close closes the ctrl server tearing down any connections that exist
func (s *MeshCtrlServer) Close() error {
if err := s.ConnectionManager.Close(); err != nil {

View File

@ -25,10 +25,19 @@ type Mesh struct {
Nodes map[string]MeshNode
}
type CtrlServer interface {
GetConfiguration() *conf.WgMeshConfiguration
GetClient() *wgctrl.Client
GetQuerier() query.Querier
GetMeshManager() mesh.MeshManager
Close() error
GetConnectionManager() conn.ConnectionManager
}
// Represents a ctrlserver to be used in WireGuard
type MeshCtrlServer struct {
Client *wgctrl.Client
MeshManager *mesh.MeshManager
MeshManager mesh.MeshManager
ConnectionManager conn.ConnectionManager
ConnectionServer *conn.ConnectionServer
Conf *conf.WgMeshConfiguration

51
pkg/ctrlserver/stub.go Normal file
View File

@ -0,0 +1,51 @@
package ctrlserver
import (
"github.com/tim-beatham/wgmesh/pkg/conf"
"github.com/tim-beatham/wgmesh/pkg/conn"
"github.com/tim-beatham/wgmesh/pkg/mesh"
"github.com/tim-beatham/wgmesh/pkg/query"
"golang.zx2c4.com/wireguard/wgctrl"
)
type CtrlServerStub struct {
manager mesh.MeshManager
querier query.Querier
connectionManager conn.ConnectionManager
}
func NewCtrlServerStub() *CtrlServerStub {
var manager mesh.MeshManager = mesh.NewMeshManagerStub()
return &CtrlServerStub{
manager: manager,
querier: query.NewJmesQuerier(manager),
connectionManager: &conn.ConnectionManagerStub{},
}
}
func (c *CtrlServerStub) GetConfiguration() *conf.WgMeshConfiguration {
return &conf.WgMeshConfiguration{
GrpcPort: "8080",
Endpoint: "abc.com",
}
}
func (c *CtrlServerStub) GetClient() *wgctrl.Client {
return &wgctrl.Client{}
}
func (c *CtrlServerStub) GetQuerier() query.Querier {
return c.querier
}
func (c *CtrlServerStub) GetMeshManager() mesh.MeshManager {
return c.manager
}
func (c *CtrlServerStub) Close() error {
return nil
}
func (c *CtrlServerStub) GetConnectionManager() conn.ConnectionManager {
return c.connectionManager
}

View File

@ -30,7 +30,7 @@ func MapKeys[K comparable, V any](m map[K]V) []K {
values := make([]K, len(m))
i := 0
for k, _ := range m {
for k := range m {
values[i] = k
i++
}
@ -58,7 +58,7 @@ type filterFunc[V any] func(V) bool
func Filter[V any](list []V, f filterFunc[V]) []V {
newList := make([]V, 0)
for _, elem := range newList {
for _, elem := range list {
if f(elem) {
newList = append(newList, elem)
}

144
pkg/lib/conv_test.go Normal file
View File

@ -0,0 +1,144 @@
package lib
import (
"slices"
"testing"
)
func stringToInt(input string) int {
return len(input)
}
func intDiv(input int) int {
return input / 2
}
func TestMapValuesMapsValues(t *testing.T) {
values := []int{1, 4, 11, 92}
var theMap map[string]int = map[string]int{
"mynameisjeff": values[0],
"tim": values[1],
"bob": values[2],
"derek": values[3],
}
mapValues := MapValues(theMap)
for _, elem := range mapValues {
if !slices.Contains(values, elem) {
t.Fatalf(`%d is not an expected value`, elem)
}
}
if len(mapValues) != len(theMap) {
t.Fatalf(`Expected length %d got %d`, len(theMap), len(mapValues))
}
}
func TestMapValuesWithExcludeExcludesValues(t *testing.T) {
values := []int{1, 9, 22}
var theMap map[string]int = map[string]int{
"mynameisbob": values[0],
"tim": values[1],
"bob": values[2],
}
exclude := map[string]struct{}{
"tim": {},
}
mapValues := MapValuesWithExclude(theMap, exclude)
if slices.Contains(mapValues, values[1]) {
t.Fatalf(`Failed to exclude expected value`)
}
if len(mapValues) != 2 {
t.Fatalf(`Incorrect expected length`)
}
for _, value := range theMap {
if !slices.Contains(values, value) {
t.Fatalf(`Element does not exist in the list of
expected values`)
}
}
}
func TestMapKeys(t *testing.T) {
keys := []string{"1", "2", "3"}
theMap := map[string]int{
keys[0]: 1,
keys[1]: 2,
keys[2]: 3,
}
mapKeys := MapKeys(theMap)
for _, elem := range mapKeys {
if !slices.Contains(keys, elem) {
t.Fatalf(`%s elem is not an expected key`, elem)
}
}
if len(mapKeys) != len(theMap) {
t.Fatalf(`Missing expected values`)
}
}
func TestMapValues(t *testing.T) {
array := []string{"mynameisjeff", "tim", "bob", "derek"}
intArray := Map(array, stringToInt)
for index, elem := range intArray {
if len(array[index]) != elem {
t.Fatalf(`Have %d want %d`, elem, len(array[index]))
}
}
}
func TestFilterFilterAll(t *testing.T) {
values := []int{1, 2, 3, 4, 5}
filterFunc := func(n int) bool {
return false
}
newValues := Filter(values, filterFunc)
if len(newValues) != 0 {
t.Fatalf(`Expected value was 0`)
}
}
func TestFilterFilterNone(t *testing.T) {
values := []int{1, 2, 3, 4, 5}
filterFunc := func(n int) bool {
return true
}
newValues := Filter(values, filterFunc)
if !slices.Equal(values, newValues) {
t.Fatalf(`Expected lists to be the same`)
}
}
func TestFilterFilterSome(t *testing.T) {
values := []int{1, 2, 3, 4, 5}
filterFunc := func(n int) bool {
return n < 3
}
expected := []int{1, 2}
actual := Filter(values, filterFunc)
if !slices.Equal(expected, actual) {
t.Fatalf(`Expected expected and actual to be the same`)
}
}

46
pkg/lib/random_test.go Normal file
View File

@ -0,0 +1,46 @@
package lib
import (
"slices"
"testing"
)
// Test that a random subset of length 0 produces a zero length
// list
func TestRandomSubsetOfLength0(t *testing.T) {
values := []int{1, 2, 3, 4, 5, 6, 7, 8}
randomValues := RandomSubsetOfLength(values, 0)
if len(randomValues) != 0 {
t.Fatalf(`Expected length to be 0`)
}
}
func TestRandomSubsetOfLength1(t *testing.T) {
values := []int{1, 2, 3, 4, 5}
randomValues := RandomSubsetOfLength(values, 1)
if len(randomValues) != 1 {
t.Fatalf(`Expected length to be 1`)
}
if !slices.Contains(values, randomValues[0]) {
t.Fatalf(`Expected length to be 1`)
}
}
func TestRandomSubsetEntireList(t *testing.T) {
values := []int{1, 2, 3, 4, 5}
randomValues := RandomSubsetOfLength(values, len(values))
if len(randomValues) != len(values) {
t.Fatalf(`Expected length to be %d was %d`, len(values), len(randomValues))
}
slices.Sort(randomValues)
if !slices.Equal(values, randomValues) {
t.Fatalf(`Expected slices to be equal`)
}
}

View File

@ -16,7 +16,7 @@ type MeshConfigApplyer interface {
// WgMeshConfigApplyer applies WireGuard configuration
type WgMeshConfigApplyer struct {
meshManager *MeshManager
meshManager MeshManager
}
func convertMeshNode(node MeshNode) (*wgtypes.PeerConfig, error) {
@ -82,11 +82,11 @@ func (m *WgMeshConfigApplyer) updateWgConf(mesh MeshProvider) error {
return err
}
return m.meshManager.Client.ConfigureDevice(dev.Name, cfg)
return m.meshManager.GetClient().ConfigureDevice(dev.Name, cfg)
}
func (m *WgMeshConfigApplyer) ApplyConfig() error {
for _, mesh := range m.meshManager.Meshes {
for _, mesh := range m.meshManager.GetMeshes() {
err := m.updateWgConf(mesh)
if err != nil {
@ -110,7 +110,7 @@ func (m *WgMeshConfigApplyer) RemovePeers(meshId string) error {
return err
}
m.meshManager.Client.ConfigureDevice(dev.Name, wgtypes.Config{
m.meshManager.GetClient().ConfigureDevice(dev.Name, wgtypes.Config{
ReplacePeers: true,
Peers: make([]wgtypes.PeerConfig, 1),
})
@ -118,6 +118,6 @@ func (m *WgMeshConfigApplyer) RemovePeers(meshId string) error {
return nil
}
func NewWgMeshConfigApplyer(manager *MeshManager) MeshConfigApplyer {
func NewWgMeshConfigApplyer(manager MeshManager) MeshConfigApplyer {
return &WgMeshConfigApplyer{meshManager: manager}
}

View File

@ -15,7 +15,7 @@ type MeshGraphConverter interface {
}
type MeshDOTConverter struct {
manager *MeshManager
manager MeshManager
}
func (c *MeshDOTConverter) Generate(meshId string) (string, error) {
@ -34,7 +34,7 @@ func (c *MeshDOTConverter) Generate(meshId string) (string, error) {
}
for _, node := range snapshot.GetNodes() {
c.graphNode(g, node)
c.graphNode(g, node, meshId)
}
nodes := lib.MapValues(snapshot.GetNodes())
@ -55,11 +55,13 @@ func (c *MeshDOTConverter) Generate(meshId string) (string, error) {
}
// graphNode: graphs a node within the mesh
func (c *MeshDOTConverter) graphNode(g *graph.Graph, node MeshNode) {
func (c *MeshDOTConverter) graphNode(g *graph.Graph, node MeshNode, meshId string) {
nodeId := fmt.Sprintf("\"%s\"", node.GetIdentifier())
g.PutNode(nodeId, graph.CIRCLE)
if node.GetHostEndpoint() == c.manager.HostParameters.HostEndpoint {
self, _ := c.manager.GetSelf(meshId)
if node.GetHostEndpoint() == self.GetHostEndpoint() {
return
}
@ -70,6 +72,6 @@ func (c *MeshDOTConverter) graphNode(g *graph.Graph, node MeshNode) {
}
}
func NewMeshDotConverter(m *MeshManager) MeshGraphConverter {
func NewMeshDotConverter(m MeshManager) MeshGraphConverter {
return &MeshDOTConverter{manager: m}
}

View File

@ -13,7 +13,24 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type MeshManager struct {
type MeshManager interface {
CreateMesh(devName string, port int) (string, error)
AddMesh(params *AddMeshParams) error
HasChanges(meshid string) bool
GetMesh(meshId string) MeshProvider
EnableInterface(meshId string) error
GetPublicKey(meshId string) (*wgtypes.Key, error)
AddSelf(params *AddSelfParams) error
LeaveMesh(meshId string) error
GetSelf(meshId string) (MeshNode, error)
ApplyConfig() error
SetDescription(description string) error
UpdateTimeStamp() error
GetClient() *wgctrl.Client
GetMeshes() map[string]MeshProvider
}
type MeshManagerImpl struct {
Meshes map[string]MeshProvider
RouteManager RouteManager
Client *wgctrl.Client
@ -30,7 +47,7 @@ type MeshManager struct {
}
// CreateMesh: Creates a new mesh, stores it and returns the mesh id
func (m *MeshManager) CreateMesh(devName string, port int) (string, error) {
func (m *MeshManagerImpl) CreateMesh(devName string, port int) (string, error) {
meshId, err := m.idGenerator.GetId()
if err != nil {
@ -76,7 +93,7 @@ type AddMeshParams struct {
}
// AddMesh: Add the mesh to the list of meshes
func (m *MeshManager) AddMesh(params *AddMeshParams) error {
func (m *MeshManagerImpl) AddMesh(params *AddMeshParams) error {
meshProvider, err := m.meshProviderFactory.CreateMesh(&MeshProviderFactoryParams{
DevName: params.DevName,
Port: params.WgPort,
@ -104,18 +121,18 @@ func (m *MeshManager) AddMesh(params *AddMeshParams) error {
}
// HasChanges returns true if the mesh has changes
func (m *MeshManager) HasChanges(meshId string) bool {
func (m *MeshManagerImpl) HasChanges(meshId string) bool {
return m.Meshes[meshId].HasChanges()
}
// GetMesh returns the mesh with the given meshid
func (m *MeshManager) GetMesh(meshId string) MeshProvider {
theMesh, _ := m.Meshes[meshId]
func (m *MeshManagerImpl) GetMesh(meshId string) MeshProvider {
theMesh := m.Meshes[meshId]
return theMesh
}
// EnableInterface: Enables the given WireGuard interface.
func (s *MeshManager) EnableInterface(meshId string) error {
func (s *MeshManagerImpl) EnableInterface(meshId string) error {
err := s.configApplyer.ApplyConfig()
if err != nil {
@ -144,7 +161,7 @@ func (s *MeshManager) EnableInterface(meshId string) error {
}
// GetPublicKey: Gets the public key of the WireGuard mesh
func (s *MeshManager) GetPublicKey(meshId string) (*wgtypes.Key, error) {
func (s *MeshManagerImpl) GetPublicKey(meshId string) (*wgtypes.Key, error) {
mesh, ok := s.Meshes[meshId]
if !ok {
@ -171,7 +188,7 @@ type AddSelfParams struct {
}
// AddSelf adds this host to the mesh
func (s *MeshManager) AddSelf(params *AddSelfParams) error {
func (s *MeshManagerImpl) AddSelf(params *AddSelfParams) error {
pubKey, err := s.GetPublicKey(params.MeshId)
if err != nil {
@ -196,7 +213,7 @@ func (s *MeshManager) AddSelf(params *AddSelfParams) error {
}
// LeaveMesh leaves the mesh network
func (s *MeshManager) LeaveMesh(meshId string) error {
func (s *MeshManagerImpl) LeaveMesh(meshId string) error {
_, exists := s.Meshes[meshId]
if !exists {
@ -208,7 +225,7 @@ func (s *MeshManager) LeaveMesh(meshId string) error {
return nil
}
func (s *MeshManager) GetSelf(meshId string) (MeshNode, error) {
func (s *MeshManagerImpl) GetSelf(meshId string) (MeshNode, error) {
meshInstance, ok := s.Meshes[meshId]
if !ok {
@ -230,11 +247,11 @@ func (s *MeshManager) GetSelf(meshId string) (MeshNode, error) {
return node, nil
}
func (s *MeshManager) ApplyConfig() error {
func (s *MeshManagerImpl) ApplyConfig() error {
return s.configApplyer.ApplyConfig()
}
func (s *MeshManager) SetDescription(description string) error {
func (s *MeshManagerImpl) SetDescription(description string) error {
for _, mesh := range s.Meshes {
err := mesh.SetDescription(s.HostParameters.HostEndpoint, description)
@ -247,7 +264,7 @@ func (s *MeshManager) SetDescription(description string) error {
}
// UpdateTimeStamp updates the timestamp of this node in all meshes
func (s *MeshManager) UpdateTimeStamp() error {
func (s *MeshManagerImpl) UpdateTimeStamp() error {
for _, mesh := range s.Meshes {
snapshot, err := mesh.GetMesh()
@ -269,6 +286,14 @@ func (s *MeshManager) UpdateTimeStamp() error {
return nil
}
func (s *MeshManagerImpl) GetClient() *wgctrl.Client {
return s.Client
}
func (s *MeshManagerImpl) GetMeshes() map[string]MeshProvider {
return s.Meshes
}
// NewMeshManagerParams params required to create an instance of a mesh manager
type NewMeshManagerParams struct {
Conf conf.WgMeshConfiguration
@ -278,10 +303,11 @@ type NewMeshManagerParams struct {
IdGenerator lib.IdGenerator
IPAllocator ip.IPAllocator
InterfaceManipulator wg.WgInterfaceManipulator
ConfigApplyer MeshConfigApplyer
}
// Creates a new instance of a mesh manager with the given parameters
func NewMeshManager(params *NewMeshManagerParams) *MeshManager {
func NewMeshManager(params *NewMeshManagerParams) *MeshManagerImpl {
hostParams := HostParameters{}
switch params.Conf.Endpoint {
@ -293,7 +319,7 @@ func NewMeshManager(params *NewMeshManagerParams) *MeshManager {
logging.Log.WriteInfof("Endpoint %s", hostParams.HostEndpoint)
m := &MeshManager{
m := &MeshManagerImpl{
Meshes: make(map[string]MeshProvider),
HostParameters: &hostParams,
meshProviderFactory: params.MeshProvider,
@ -301,7 +327,7 @@ func NewMeshManager(params *NewMeshManagerParams) *MeshManager {
Client: params.Client,
conf: &params.Conf,
}
m.configApplyer = NewWgMeshConfigApplyer(m)
m.configApplyer = params.ConfigApplyer
m.RouteManager = NewRouteManager(m)
m.idGenerator = params.IdGenerator
m.ipAllocator = params.IPAllocator

246
pkg/mesh/manager_test.go Normal file
View File

@ -0,0 +1,246 @@
package mesh
import (
"testing"
"github.com/tim-beatham/wgmesh/pkg/conf"
"github.com/tim-beatham/wgmesh/pkg/ip"
"github.com/tim-beatham/wgmesh/pkg/lib"
"github.com/tim-beatham/wgmesh/pkg/wg"
)
func getMeshConfiguration() *conf.WgMeshConfiguration {
return &conf.WgMeshConfiguration{
GrpcPort: "8080",
Endpoint: "abc.com",
ClusterSize: 64,
SyncRate: 4,
BranchRate: 3,
InterClusterChance: 0.15,
InfectionCount: 2,
KeepAliveRate: 60,
}
}
func getMeshManager() *MeshManagerImpl {
manager := NewMeshManager(&NewMeshManagerParams{
Conf: *getMeshConfiguration(),
Client: nil,
MeshProvider: &StubMeshProviderFactory{},
NodeFactory: &StubNodeFactory{Config: getMeshConfiguration()},
IdGenerator: &lib.UUIDGenerator{},
IPAllocator: &ip.ULABuilder{},
InterfaceManipulator: &wg.WgInterfaceManipulatorStub{},
ConfigApplyer: &MeshConfigApplyerStub{},
})
return manager
}
func TestCreateMeshCreatesANewMeshProvider(t *testing.T) {
manager := getMeshManager()
meshId, err := manager.CreateMesh("wg0", 5000)
if err != nil {
t.Error(err)
}
if len(meshId) == 0 {
t.Fatal(`meshId should not be empty`)
}
_, exists := manager.Meshes[meshId]
if !exists {
t.Fatal(`mesh was not created when it should be`)
}
}
func TestAddMeshAddsAMesh(t *testing.T) {
manager := getMeshManager()
meshId := "meshid123"
manager.AddMesh(&AddMeshParams{
MeshId: meshId,
DevName: "wg0",
WgPort: 6000,
MeshBytes: make([]byte, 0),
})
mesh := manager.GetMesh(meshId)
if mesh == nil || mesh.GetMeshId() != meshId {
t.Fatalf(`mesh has not been added to the list of meshes`)
}
}
func TestAddMeshMeshAlreadyExistsReplacesIt(t *testing.T) {
manager := getMeshManager()
meshId := "meshid123"
for i := 0; i < 2; i++ {
err := manager.AddMesh(&AddMeshParams{
MeshId: meshId,
DevName: "wg0",
WgPort: 6000,
MeshBytes: make([]byte, 0),
})
if err != nil {
t.Error(err)
}
}
mesh := manager.GetMesh(meshId)
if mesh == nil || mesh.GetMeshId() != meshId {
t.Fatalf(`mesh has not been added to the list of meshes`)
}
}
func TestAddSelfAddsSelfToTheMesh(t *testing.T) {
manager := getMeshManager()
meshId := "meshid123"
err := manager.AddMesh(&AddMeshParams{
MeshId: meshId,
DevName: "wg0",
WgPort: 6000,
MeshBytes: make([]byte, 0),
})
if err != nil {
t.Error(err)
}
err = manager.AddSelf(&AddSelfParams{
MeshId: meshId,
WgPort: 5000,
Endpoint: "abc.com",
})
if err != nil {
t.Error(err)
}
mesh, err := manager.GetMesh(meshId).GetMesh()
if err != nil {
t.Error(err)
}
_, ok := mesh.GetNodes()["abc.com"]
if !ok {
t.Fatalf(`node has not been added`)
}
}
func TestAddSelfToMeshAlreadyInMesh(t *testing.T) {
TestAddSelfAddsSelfToTheMesh(t)
TestAddSelfAddsSelfToTheMesh(t)
}
func TestAddSelfToMeshMeshDoesNotExist(t *testing.T) {
manager := getMeshManager()
meshId := "meshid123"
err := manager.AddSelf(&AddSelfParams{
MeshId: meshId,
WgPort: 5000,
Endpoint: "abc.com",
})
if err == nil {
t.Fatalf(`Expected error to be thrown`)
}
}
func TestLeaveMeshMeshDoesNotExist(t *testing.T) {
manager := getMeshManager()
meshId := "meshid123"
err := manager.LeaveMesh(meshId)
if err == nil {
t.Fatalf(`Expected error to be thrown`)
}
}
func TestLeaveMeshDeletesMesh(t *testing.T) {
manager := getMeshManager()
meshId := "meshid123"
err := manager.AddMesh(&AddMeshParams{
MeshId: meshId,
DevName: "wg0",
WgPort: 6000,
MeshBytes: make([]byte, 0),
})
if err != nil {
t.Error(err)
}
err = manager.LeaveMesh(meshId)
if err != nil {
t.Error(err)
}
_, exists := manager.Meshes[meshId]
if exists {
t.Fatalf(`expected mesh to have been deleted`)
}
}
func TestSetDescription(t *testing.T) {
manager := getMeshManager()
description := "wooooo"
meshId1, _ := manager.CreateMesh("wg0", 5000)
meshId2, _ := manager.CreateMesh("wg0", 5001)
manager.AddSelf(&AddSelfParams{
MeshId: meshId1,
WgPort: 5000,
Endpoint: "abc.com:8080",
})
manager.AddSelf(&AddSelfParams{
MeshId: meshId2,
WgPort: 5000,
Endpoint: "abc.com:8080",
})
err := manager.SetDescription(description)
if err != nil {
t.Fatalf(`failed to set the descriptions`)
}
}
func TestUpdateTimeStampUpdatesAllMeshes(t *testing.T) {
manager := getMeshManager()
meshId1, _ := manager.CreateMesh("wg0", 5000)
meshId2, _ := manager.CreateMesh("wg0", 5001)
manager.AddSelf(&AddSelfParams{
MeshId: meshId1,
WgPort: 5000,
Endpoint: "abc.com:8080",
})
manager.AddSelf(&AddSelfParams{
MeshId: meshId2,
WgPort: 5000,
Endpoint: "abc.com:8080",
})
err := manager.UpdateTimeStamp()
if err != nil {
t.Fatalf(`failed to update the timestamp`)
}
}

View File

@ -11,12 +11,12 @@ type RouteManager interface {
}
type RouteManagerImpl struct {
meshManager *MeshManager
meshManager MeshManager
routeInstaller route.RouteInstaller
}
func (r *RouteManagerImpl) UpdateRoutes() error {
meshes := r.meshManager.Meshes
meshes := r.meshManager.GetMeshes()
ulaBuilder := new(ip.ULABuilder)
for _, mesh1 := range meshes {
@ -32,7 +32,13 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
return err
}
err = mesh1.AddRoutes(r.meshManager.HostParameters.HostEndpoint, ipNet.String())
self, err := r.meshManager.GetSelf(mesh1.GetMeshId())
if err != nil {
return err
}
err = mesh1.AddRoutes(self.GetHostEndpoint(), ipNet.String())
if err != nil {
return err
@ -43,6 +49,6 @@ func (r *RouteManagerImpl) UpdateRoutes() error {
return nil
}
func NewRouteManager(m *MeshManager) RouteManager {
func NewRouteManager(m MeshManager) RouteManager {
return &RouteManagerImpl{meshManager: m, routeInstaller: route.NewRouteInstaller()}
}

227
pkg/mesh/stub_types.go Normal file
View File

@ -0,0 +1,227 @@
package mesh
import (
"fmt"
"net"
"time"
"github.com/tim-beatham/wgmesh/pkg/conf"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type MeshNodeStub struct {
hostEndpoint string
publicKey wgtypes.Key
wgEndpoint string
wgHost *net.IPNet
timeStamp int64
routes []string
identifier string
description string
}
func (m *MeshNodeStub) GetHostEndpoint() string {
return m.hostEndpoint
}
func (m *MeshNodeStub) GetPublicKey() (wgtypes.Key, error) {
return m.publicKey, nil
}
func (m *MeshNodeStub) GetWgEndpoint() string {
return m.wgEndpoint
}
func (m *MeshNodeStub) GetWgHost() *net.IPNet {
return m.wgHost
}
func (m *MeshNodeStub) GetTimeStamp() int64 {
return m.timeStamp
}
func (m *MeshNodeStub) GetRoutes() []string {
return m.routes
}
func (m *MeshNodeStub) GetIdentifier() string {
return m.identifier
}
func (m *MeshNodeStub) GetDescription() string {
return m.description
}
type MeshSnapshotStub struct {
nodes map[string]MeshNode
}
func (s *MeshSnapshotStub) GetNodes() map[string]MeshNode {
return s.nodes
}
type MeshProviderStub struct {
meshId string
snapshot *MeshSnapshotStub
}
// UpdateTimeStamp implements MeshProvider.
func (*MeshProviderStub) UpdateTimeStamp(nodeId string) error {
return nil
}
func (s *MeshProviderStub) AddNode(node MeshNode) {
s.snapshot.nodes[node.GetHostEndpoint()] = node
}
func (s *MeshProviderStub) GetMesh() (MeshSnapshot, error) {
return s.snapshot, nil
}
func (s *MeshProviderStub) GetMeshId() string {
return s.meshId
}
func (s *MeshProviderStub) Save() []byte {
return make([]byte, 0)
}
func (s *MeshProviderStub) Load(bytes []byte) error {
return nil
}
func (s *MeshProviderStub) GetDevice() (*wgtypes.Device, error) {
pubKey, _ := wgtypes.GenerateKey()
return &wgtypes.Device{
PublicKey: pubKey,
}, nil
}
func (s *MeshProviderStub) SaveChanges() {}
func (s *MeshProviderStub) HasChanges() bool {
return false
}
func (s *MeshProviderStub) AddRoutes(nodeId string, route ...string) error {
return nil
}
func (s *MeshProviderStub) GetSyncer() MeshSyncer {
return nil
}
func (s *MeshProviderStub) SetDescription(nodeId string, description string) error {
return nil
}
type StubMeshProviderFactory struct{}
func (s *StubMeshProviderFactory) CreateMesh(params *MeshProviderFactoryParams) (MeshProvider, error) {
return &MeshProviderStub{
meshId: params.MeshId,
snapshot: &MeshSnapshotStub{nodes: make(map[string]MeshNode)},
}, nil
}
type StubNodeFactory struct {
Config *conf.WgMeshConfiguration
}
func (s *StubNodeFactory) Build(params *MeshNodeFactoryParams) MeshNode {
_, wgHost, _ := net.ParseCIDR(fmt.Sprintf("%s/128", params.NodeIP.String()))
return &MeshNodeStub{
hostEndpoint: params.Endpoint,
publicKey: *params.PublicKey,
wgEndpoint: fmt.Sprintf("%s:%s", params.Endpoint, s.Config.GrpcPort),
wgHost: wgHost,
timeStamp: time.Now().Unix(),
routes: make([]string, 0),
identifier: "abc",
description: "A Mesh Node Stub",
}
}
type MeshConfigApplyerStub struct{}
func (a *MeshConfigApplyerStub) ApplyConfig() error {
return nil
}
func (a *MeshConfigApplyerStub) RemovePeers(meshId string) error {
return nil
}
type MeshManagerStub struct {
meshes map[string]MeshProvider
}
func NewMeshManagerStub() MeshManager {
return &MeshManagerStub{meshes: make(map[string]MeshProvider)}
}
func (m *MeshManagerStub) CreateMesh(devName string, port int) (string, error) {
return "tim123", nil
}
func (m *MeshManagerStub) AddMesh(params *AddMeshParams) error {
m.meshes[params.MeshId] = &MeshProviderStub{
params.MeshId,
&MeshSnapshotStub{nodes: make(map[string]MeshNode)},
}
return nil
}
func (m *MeshManagerStub) HasChanges(meshId string) bool {
return false
}
func (m *MeshManagerStub) GetMesh(meshId string) MeshProvider {
return &MeshProviderStub{
meshId: meshId,
snapshot: &MeshSnapshotStub{nodes: make(map[string]MeshNode)}}
}
func (m *MeshManagerStub) EnableInterface(meshId string) error {
return nil
}
func (m *MeshManagerStub) GetPublicKey(meshId string) (*wgtypes.Key, error) {
key, _ := wgtypes.GenerateKey()
return &key, nil
}
func (m *MeshManagerStub) AddSelf(params *AddSelfParams) error {
return nil
}
func (m *MeshManagerStub) GetSelf(meshId string) (MeshNode, error) {
return nil, nil
}
func (m *MeshManagerStub) ApplyConfig() error {
return nil
}
func (m *MeshManagerStub) SetDescription(description string) error {
return nil
}
func (m *MeshManagerStub) UpdateTimeStamp() error {
return nil
}
func (m *MeshManagerStub) GetClient() *wgctrl.Client {
return nil
}
func (m *MeshManagerStub) GetMeshes() map[string]MeshProvider {
return m.meshes
}
func (m *MeshManagerStub) LeaveMesh(meshId string) error {
return nil
}

View File

@ -1,29 +0,0 @@
package middleware
import (
"context"
"errors"
logging "github.com/tim-beatham/wgmesh/pkg/log"
"github.com/tim-beatham/wgmesh/pkg/rpc"
)
// AuthRpcProvider implements the AuthRpcProvider service
type AuthRpcProvider struct {
rpc.UnimplementedAuthenticationServer
}
// JoinMesh handles a JoinMeshRequest. Succeeds by stating the node managed to join the mesh
// or returns an error if it failed
func (a *AuthRpcProvider) JoinMesh(ctx context.Context, in *rpc.JoinAuthMeshRequest) (*rpc.JoinAuthMeshReply, error) {
meshId := in.MeshId
if meshId == "" {
return nil, errors.New("Must specify the meshId")
}
logging.Log.WriteInfof("MeshID: " + in.MeshId)
var token string = ""
return &rpc.JoinAuthMeshReply{Success: true, Token: &token}, nil
}

View File

@ -16,7 +16,7 @@ type Querier interface {
}
type JmesQuerier struct {
manager *mesh.MeshManager
manager mesh.MeshManager
}
type QueryError struct {
@ -39,7 +39,7 @@ func (m *QueryError) Error() string {
// Query: queries the data
func (j *JmesQuerier) Query(meshId, queryParams string) ([]byte, error) {
mesh, ok := j.manager.Meshes[meshId]
mesh, ok := j.manager.GetMeshes()[meshId]
if !ok {
return nil, &QueryError{msg: fmt.Sprintf("%s does not exist", meshId)}
@ -79,6 +79,6 @@ func meshNodeToQueryNode(node mesh.MeshNode) *QueryNode {
return queryNode
}
func NewJmesQuerier(manager *mesh.MeshManager) Querier {
func NewJmesQuerier(manager mesh.MeshManager) Querier {
return &JmesQuerier{manager: manager}
}

View File

@ -8,25 +8,23 @@ import (
"time"
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
"github.com/tim-beatham/wgmesh/pkg/ip"
"github.com/tim-beatham/wgmesh/pkg/ipc"
"github.com/tim-beatham/wgmesh/pkg/mesh"
"github.com/tim-beatham/wgmesh/pkg/rpc"
)
type IpcHandler struct {
Server *ctrlserver.MeshCtrlServer
ipAllocator ip.IPAllocator
Server ctrlserver.CtrlServer
}
func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
meshId, err := n.Server.MeshManager.CreateMesh(args.IfName, args.WgPort)
meshId, err := n.Server.GetMeshManager().CreateMesh(args.IfName, args.WgPort)
if err != nil {
return err
}
err = n.Server.MeshManager.AddSelf(&mesh.AddSelfParams{
err = n.Server.GetMeshManager().AddSelf(&mesh.AddSelfParams{
MeshId: meshId,
WgPort: args.WgPort,
Endpoint: args.Endpoint,
@ -37,10 +35,10 @@ func (n *IpcHandler) CreateMesh(args *ipc.NewMeshArgs, reply *string) error {
}
func (n *IpcHandler) ListMeshes(_ string, reply *ipc.ListMeshReply) error {
meshNames := make([]string, len(n.Server.MeshManager.Meshes))
meshNames := make([]string, len(n.Server.GetMeshManager().GetMeshes()))
i := 0
for meshId, _ := range n.Server.MeshManager.Meshes {
for meshId, _ := range n.Server.GetMeshManager().GetMeshes() {
meshNames[i] = meshId
i++
}
@ -50,7 +48,7 @@ func (n *IpcHandler) ListMeshes(_ string, reply *ipc.ListMeshReply) error {
}
func (n *IpcHandler) JoinMesh(args ipc.JoinMeshArgs, reply *string) error {
peerConnection, err := n.Server.ConnectionManager.GetConnection(args.IpAdress)
peerConnection, err := n.Server.GetConnectionManager().GetConnection(args.IpAdress)
if err != nil {
return err
@ -77,7 +75,7 @@ func (n *IpcHandler) JoinMesh(args ipc.JoinMeshArgs, reply *string) error {
return err
}
err = n.Server.MeshManager.AddMesh(&mesh.AddMeshParams{
err = n.Server.GetMeshManager().AddMesh(&mesh.AddMeshParams{
MeshId: args.MeshId,
DevName: args.IfName,
WgPort: args.Port,
@ -88,7 +86,7 @@ func (n *IpcHandler) JoinMesh(args ipc.JoinMeshArgs, reply *string) error {
return err
}
err = n.Server.MeshManager.AddSelf(&mesh.AddSelfParams{
err = n.Server.GetMeshManager().AddSelf(&mesh.AddSelfParams{
MeshId: args.MeshId,
WgPort: args.Port,
Endpoint: args.Endpoint,
@ -104,7 +102,7 @@ func (n *IpcHandler) JoinMesh(args ipc.JoinMeshArgs, reply *string) error {
// LeaveMesh leaves a mesh network
func (n *IpcHandler) LeaveMesh(meshId string, reply *string) error {
err := n.Server.MeshManager.LeaveMesh(meshId)
err := n.Server.GetMeshManager().LeaveMesh(meshId)
if err == nil {
*reply = fmt.Sprintf("Left Mesh %s", meshId)
@ -114,7 +112,7 @@ func (n *IpcHandler) LeaveMesh(meshId string, reply *string) error {
}
func (n *IpcHandler) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
mesh := n.Server.MeshManager.GetMesh(meshId)
mesh := n.Server.GetMeshManager().GetMesh(meshId)
meshSnapshot, err := mesh.GetMesh()
if err != nil {
@ -152,7 +150,7 @@ func (n *IpcHandler) GetMesh(meshId string, reply *ipc.GetMeshReply) error {
}
func (n *IpcHandler) EnableInterface(meshId string, reply *string) error {
err := n.Server.MeshManager.EnableInterface(meshId)
err := n.Server.GetMeshManager().EnableInterface(meshId)
if err != nil {
*reply = err.Error()
@ -164,7 +162,7 @@ func (n *IpcHandler) EnableInterface(meshId string, reply *string) error {
}
func (n *IpcHandler) GetDOT(meshId string, reply *string) error {
g := mesh.NewMeshDotConverter(n.Server.MeshManager)
g := mesh.NewMeshDotConverter(n.Server.GetMeshManager())
result, err := g.Generate(meshId)
@ -177,7 +175,7 @@ func (n *IpcHandler) GetDOT(meshId string, reply *string) error {
}
func (n *IpcHandler) Query(params ipc.QueryMesh, reply *string) error {
queryResponse, err := n.Server.Querier.Query(params.MeshId, params.Query)
queryResponse, err := n.Server.GetQuerier().Query(params.MeshId, params.Query)
if err != nil {
return err
@ -188,7 +186,7 @@ func (n *IpcHandler) Query(params ipc.QueryMesh, reply *string) error {
}
func (n *IpcHandler) PutDescription(description string, reply *string) error {
err := n.Server.MeshManager.SetDescription(description)
err := n.Server.GetMeshManager().SetDescription(description)
if err != nil {
return err
@ -199,7 +197,7 @@ func (n *IpcHandler) PutDescription(description string, reply *string) error {
}
type RobinIpcParams struct {
CtrlServer *ctrlserver.MeshCtrlServer
CtrlServer ctrlserver.CtrlServer
}
func NewRobinIpc(ipcParams RobinIpcParams) IpcHandler {

View File

@ -0,0 +1,73 @@
package robin
import (
"testing"
"github.com/tim-beatham/wgmesh/pkg/ctrlserver"
"github.com/tim-beatham/wgmesh/pkg/ipc"
"github.com/tim-beatham/wgmesh/pkg/mesh"
)
func getRequester() *IpcHandler {
return &IpcHandler{Server: ctrlserver.NewCtrlServerStub()}
}
func TestCreateMeshRepliesMeshId(t *testing.T) {
var reply string
requester := getRequester()
err := requester.CreateMesh(&ipc.NewMeshArgs{
IfName: "wg0",
WgPort: 5000,
Endpoint: "abc.com",
}, &reply)
if err != nil {
t.Error(err)
}
if len(reply) == 0 {
t.Fatalf(`reply should have been returned`)
}
}
func TestListMeshesNoMeshesListsEmpty(t *testing.T) {
var reply ipc.ListMeshReply
requester := getRequester()
err := requester.ListMeshes("", &reply)
if err != nil {
t.Error(err)
}
if len(reply.Meshes) != 0 {
t.Fatalf(`meshes should be empty`)
}
}
func TestListMeshesMeshesNotEmpty(t *testing.T) {
var reply ipc.ListMeshReply
requester := getRequester()
requester.Server.GetMeshManager().AddMesh(&mesh.AddMeshParams{
MeshId: "tim123",
DevName: "wg0",
WgPort: 5000,
MeshBytes: make([]byte, 0),
})
err := requester.ListMeshes("", &reply)
if err != nil {
t.Error(err)
}
if len(reply.Meshes) != 1 {
t.Fatalf(`only only mesh exists`)
}
if reply.Meshes[0] != "tim123" {
t.Fatalf(`meshId was %s expected %s`, reply.Meshes[0], "tim123")
}
}

View File

@ -13,29 +13,6 @@ type WgRpc struct {
Server *ctrlserver.MeshCtrlServer
}
func nodeToRpcNode(node ctrlserver.MeshNode) *rpc.MeshNode {
return &rpc.MeshNode{
PublicKey: node.PublicKey,
WgEndpoint: node.WgEndpoint,
WgHost: node.WgHost,
Endpoint: node.HostEndpoint,
}
}
func nodesToRpcNodes(nodes map[string]ctrlserver.MeshNode) []*rpc.MeshNode {
n := len(nodes)
meshNodes := make([]*rpc.MeshNode, n)
var i int = 0
for _, v := range nodes {
meshNodes[i] = nodeToRpcNode(v)
i++
}
return meshNodes
}
func (m *WgRpc) GetMesh(ctx context.Context, request *rpc.GetMeshRequest) (*rpc.GetMeshReply, error) {
mesh := m.Server.MeshManager.GetMesh(request.MeshId)

View File

@ -0,0 +1 @@
package robin

View File

@ -20,7 +20,7 @@ type Syncer interface {
}
type SyncerImpl struct {
manager *mesh.MeshManager
manager mesh.MeshManager
requester SyncRequester
infectionCount int
syncCount int
@ -56,8 +56,14 @@ func (s *SyncerImpl) Sync(meshId string) error {
return nil
}
self, err := s.manager.GetSelf(meshId)
if err != nil {
return err
}
excludedNodes := map[string]struct{}{
s.manager.HostParameters.HostEndpoint: {},
self.GetHostEndpoint(): {},
}
meshNodes := lib.MapValuesWithExclude(nodes, excludedNodes)
@ -67,7 +73,7 @@ func (s *SyncerImpl) Sync(meshId string) error {
nodeNames := lib.Map(meshNodes, getNames)
neighbours := s.cluster.GetNeighbours(nodeNames, s.manager.HostParameters.HostEndpoint)
neighbours := s.cluster.GetNeighbours(nodeNames, self.GetHostEndpoint())
randomSubset := lib.RandomSubsetOfLength(neighbours, s.conf.BranchRate)
for _, node := range randomSubset {
@ -78,7 +84,7 @@ func (s *SyncerImpl) Sync(meshId string) error {
if len(meshNodes) > s.conf.ClusterSize && rand.Float64() < s.conf.InterClusterChance {
logging.Log.WriteInfof("Sending to random cluster")
interCluster := s.cluster.GetInterCluster(nodeNames, s.manager.HostParameters.HostEndpoint)
interCluster := s.cluster.GetInterCluster(nodeNames, self.GetHostEndpoint())
randomSubset = append(randomSubset, interCluster)
}
@ -107,7 +113,7 @@ func (s *SyncerImpl) Sync(meshId string) error {
// SyncMeshes: Sync all meshes
func (s *SyncerImpl) SyncMeshes() error {
for meshId, _ := range s.manager.Meshes {
for meshId, _ := range s.manager.GetMeshes() {
err := s.Sync(meshId)
if err != nil {
@ -118,7 +124,7 @@ func (s *SyncerImpl) SyncMeshes() error {
return nil
}
func NewSyncer(m *mesh.MeshManager, conf *conf.WgMeshConfiguration, r SyncRequester) Syncer {
func NewSyncer(m mesh.MeshManager, conf *conf.WgMeshConfiguration, r SyncRequester) Syncer {
cluster, _ := conn.NewConnCluster(conf.ClusterSize)
return &SyncerImpl{
manager: m,

View File

@ -14,7 +14,7 @@ type SyncErrorHandler interface {
// SyncErrorHandlerImpl Is an implementation of the SyncErrorHandler
type SyncErrorHandlerImpl struct {
meshManager *mesh.MeshManager
meshManager mesh.MeshManager
}
func (s *SyncErrorHandlerImpl) incrementFailedCount(meshId string, endpoint string) bool {
@ -40,6 +40,6 @@ func (s *SyncErrorHandlerImpl) Handle(meshId string, endpoint string, err error)
return false
}
func NewSyncErrorHandler(m *mesh.MeshManager) SyncErrorHandler {
func NewSyncErrorHandler(m mesh.MeshManager) SyncErrorHandler {
return &SyncErrorHandlerImpl{meshManager: m}
}

View File

@ -14,7 +14,7 @@ type TimestampScheduler interface {
}
type TimeStampSchedulerImpl struct {
meshManager *mesh.MeshManager
meshManager mesh.MeshManager
updateRate int
quit chan struct{}
}
@ -39,7 +39,10 @@ func (s *TimeStampSchedulerImpl) Run() error {
}
func NewTimestampScheduler(ctrlServer *ctrlserver.MeshCtrlServer) TimestampScheduler {
return &TimeStampSchedulerImpl{meshManager: ctrlServer.MeshManager, updateRate: ctrlServer.Conf.KeepAliveRate}
return &TimeStampSchedulerImpl{
meshManager: ctrlServer.MeshManager,
updateRate: ctrlServer.Conf.KeepAliveRate,
}
}
func (s *TimeStampSchedulerImpl) Stop() error {

11
pkg/wg/stubs.go Normal file
View File

@ -0,0 +1,11 @@
package wg
type WgInterfaceManipulatorStub struct{}
func (i *WgInterfaceManipulatorStub) CreateInterface(params *CreateInterfaceParams) error {
return nil
}
func (i *WgInterfaceManipulatorStub) EnableInterface(ifName string, ip string) error {
return nil
}