Browse Source

Combined client + server code, Added HA ability, minor bug fixes

0xdcarns 3 years ago
parent
commit
989676e77f

+ 2 - 234
controllers/common.go

@@ -2,157 +2,15 @@ package controller
 
 import (
 	"encoding/json"
-	"strconv"
 	"strings"
-	"time"
 
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
-	"github.com/gravitl/netmaker/serverctl"
-	"golang.org/x/crypto/bcrypt"
 )
 
-func GetPeersList(networkName string, excludeRelayed bool, relayedNodeAddr string) ([]models.Node, error) {
-	var peers []models.Node
-	var relayNode models.Node
-	var err error
-	if relayedNodeAddr == "" {
-		peers, err = GetNodePeers(networkName, excludeRelayed)
-
-	} else {
-		relayNode, err = GetNodeRelay(networkName, relayedNodeAddr)
-		if relayNode.Address != "" {
-			relayNode = setPeerInfo(relayNode)
-			network, err := models.GetNetwork(networkName)
-			if err == nil {
-				relayNode.AllowedIPs = append(relayNode.AllowedIPs, network.AddressRange)
-			} else {
-				relayNode.AllowedIPs = append(relayNode.AllowedIPs, relayNode.RelayAddrs...)
-			}
-			nodepeers, err := GetNodePeers(networkName, false)
-			if err == nil && relayNode.UDPHolePunch == "yes" {
-				for _, nodepeer := range nodepeers {
-					if nodepeer.Address == relayNode.Address {
-						relayNode.Endpoint = nodepeer.Endpoint
-						relayNode.ListenPort = nodepeer.ListenPort
-					}
-				}
-			}
-
-			peers = append(peers, relayNode)
-		}
-	}
-	return peers, err
-}
-
-func GetNodePeers(networkName string, excludeRelayed bool) ([]models.Node, error) {
-	var peers []models.Node
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return peers, nil
-		}
-		functions.PrintUserLog("", err.Error(), 2)
-		return nil, err
-	}
-	udppeers, errN := database.GetPeers(networkName)
-	if errN != nil {
-		functions.PrintUserLog("", errN.Error(), 2)
-	}
-	for _, value := range collection {
-		var node models.Node
-		var peer models.Node
-		err := json.Unmarshal([]byte(value), &node)
-		if err != nil {
-			functions.PrintUserLog("", err.Error(), 2)
-			continue
-		}
-		if node.IsEgressGateway == "yes" { // handle egress stuff
-			peer.EgressGatewayRanges = node.EgressGatewayRanges
-			peer.IsEgressGateway = node.IsEgressGateway
-		}
-		allow := node.IsRelayed != "yes" || !excludeRelayed
-
-		if node.Network == networkName && node.IsPending != "yes" && allow {
-			peer = setPeerInfo(node)
-			if node.UDPHolePunch == "yes" && errN == nil && functions.CheckEndpoint(udppeers[node.PublicKey]) {
-				endpointstring := udppeers[node.PublicKey]
-				endpointarr := strings.Split(endpointstring, ":")
-				if len(endpointarr) == 2 {
-					port, err := strconv.Atoi(endpointarr[1])
-					if err == nil {
-						peer.Endpoint = endpointarr[0]
-						peer.ListenPort = int32(port)
-					}
-				}
-			}
-			if node.IsRelay == "yes" {
-				network, err := models.GetNetwork(networkName)
-				if err == nil {
-					peer.AllowedIPs = append(peer.AllowedIPs, network.AddressRange)
-				} else {
-					peer.AllowedIPs = append(peer.AllowedIPs, node.RelayAddrs...)
-				}
-			}
-			peers = append(peers, peer)
-		}
-	}
-
-	return peers, err
-}
-
-func setPeerInfo(node models.Node) models.Node {
-	var peer models.Node
-	peer.RelayAddrs = node.RelayAddrs
-	peer.IsRelay = node.IsRelay
-	peer.IsRelayed = node.IsRelayed
-	peer.PublicKey = node.PublicKey
-	peer.Endpoint = node.Endpoint
-	peer.LocalAddress = node.LocalAddress
-	peer.ListenPort = node.ListenPort
-	peer.AllowedIPs = node.AllowedIPs
-	peer.UDPHolePunch = node.UDPHolePunch
-	peer.Address = node.Address
-	peer.Address6 = node.Address6
-	peer.EgressGatewayRanges = node.EgressGatewayRanges
-	peer.IsEgressGateway = node.IsEgressGateway
-	peer.IngressGatewayRange = node.IngressGatewayRange
-	peer.IsIngressGateway = node.IsIngressGateway
-	peer.IsPending = node.IsPending
-	return peer
-}
-
-func GetExtPeersList(macaddress string, networkName string) ([]models.ExtPeersResponse, error) {
-
-	var peers []models.ExtPeersResponse
-	records, err := database.FetchRecords(database.EXT_CLIENT_TABLE_NAME)
-
-	if err != nil {
-		return peers, err
-	}
-
-	for _, value := range records {
-		var peer models.ExtPeersResponse
-		var extClient models.ExtClient
-		err = json.Unmarshal([]byte(value), &peer)
-		if err != nil {
-			functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal peer", 2)
-			continue
-		}
-		err = json.Unmarshal([]byte(value), &extClient)
-		if err != nil {
-			functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal ext client", 2)
-			continue
-		}
-		if extClient.Network == networkName && extClient.IngressGatewayID == macaddress {
-			peers = append(peers, peer)
-		}
-	}
-	return peers, err
-}
-
 /**
  * If being deleted by server, create a record in the DELETED_NODES_TABLE for the client to find
  * If being deleted by the client, delete completely
@@ -183,7 +41,7 @@ func DeleteNode(key string, exterminate bool) error {
 		return err
 	}
 	if servercfg.IsDNSMode() {
-		err = SetDNS()
+		err = dnslogic.SetDNS()
 	}
 	return err
 }
@@ -235,93 +93,3 @@ func GetIntClient(clientid string) (models.IntClient, error) {
 	}
 	return client, nil
 }
-
-func CreateNode(node models.Node, networkName string) (models.Node, error) {
-
-	//encrypt that password so we never see it
-	hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
-
-	if err != nil {
-		return node, err
-	}
-	//set password to encrypted password
-	node.Password = string(hash)
-
-	node.Network = networkName
-	if node.Name == models.NODE_SERVER_NAME {
-		node.IsServer = "yes"
-	}
-	if servercfg.IsDNSMode() && node.DNSOn == ""{
-		node.DNSOn = "yes"
-	}
-	node.SetDefaults()
-	node.Address, err = functions.UniqueAddress(networkName)
-	if err != nil {
-		return node, err
-	}
-	node.Address6, err = functions.UniqueAddress6(networkName)
-	if err != nil {
-		return node, err
-	}
-	//Create a JWT for the node
-	tokenString, _ := functions.CreateJWT(node.MacAddress, networkName)
-	if tokenString == "" {
-		//returnErrorResponse(w, r, errorResponse)
-		return node, err
-	}
-	err = node.Validate(false)
-	if err != nil {
-		return node, err
-	}
-	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
-	if err != nil {
-		return node, err
-	}
-	nodebytes, err := json.Marshal(&node)
-	if err != nil {
-		return node, err
-	}
-	err = database.Insert(key, string(nodebytes), database.NODES_TABLE_NAME)
-	if err != nil {
-		return node, err
-	}
-	if node.IsPending != "yes" {
-		functions.DecrimentKey(node.Network, node.AccessKey)
-	}
-	SetNetworkNodesLastModified(node.Network)
-	if servercfg.IsDNSMode() {
-		err = SetDNS()
-	}
-	return node, err
-}
-
-func SetNetworkServerPeers(networkName string) {
-	if currentPeersList, err := serverctl.GetPeers(networkName); err == nil {
-		if database.SetPeers(currentPeersList, networkName) {
-			functions.PrintUserLog(models.NODE_SERVER_NAME, "set new peers on network "+networkName, 1)
-		}
-	} else {
-		functions.PrintUserLog(models.NODE_SERVER_NAME, "could not set peers on network "+networkName, 1)
-		functions.PrintUserLog(models.NODE_SERVER_NAME, err.Error(), 1)
-	}
-}
-
-func SetNetworkNodesLastModified(networkName string) error {
-
-	timestamp := time.Now().Unix()
-
-	network, err := functions.GetParentNetwork(networkName)
-	if err != nil {
-		return err
-	}
-	network.NodesLastModified = timestamp
-	data, err := json.Marshal(&network)
-	if err != nil {
-		return err
-	}
-	err = database.Insert(networkName, string(data), database.NETWORKS_TABLE_NAME)
-	if err != nil {
-		return err
-	}
-	return nil
-}

+ 9 - 8
controllers/common_test.go

@@ -4,6 +4,7 @@ import (
 	"testing"
 
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
 )
@@ -13,20 +14,20 @@ func TestGetPeerList(t *testing.T) {
 	deleteAllNetworks()
 	createNet()
 	t.Run("NoNodes", func(t *testing.T) {
-		peers, err := GetPeersList("skynet", false, "")
+		peers, err := logic.GetPeersList("skynet", false, "")
 		assert.Nil(t, err)
 		assert.Nil(t, peers)
 	})
 	node := createTestNode()
 	t.Run("One Node", func(t *testing.T) {
-		peers, err := GetPeersList("skynet", false, "")
+		peers, err := logic.GetPeersList("skynet", false, "")
 		assert.Nil(t, err)
 		assert.Equal(t, node.Address, peers[0].Address)
 	})
 	t.Run("Multiple Nodes", func(t *testing.T) {
 		createnode := models.Node{PublicKey: "RM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.2", MacAddress: "02:02:03:04:05:06", Password: "password", Network: "skynet"}
-		CreateNode(createnode, "skynet")
-		peers, err := GetPeersList("skynet", false, "")
+		logic.CreateNode(createnode, "skynet")
+		peers, err := logic.GetPeersList("skynet", false, "")
 		assert.Nil(t, err)
 		assert.Equal(t, len(peers), 2)
 		foundNodeEndpoint := false
@@ -97,7 +98,7 @@ func TestCreateNode(t *testing.T) {
 	createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet"}
 	//err := ValidateNodeCreate("skynet", createnode)
 	//assert.Nil(t, err)
-	node, err := CreateNode(createnode, "skynet")
+	node, err := logic.CreateNode(createnode, "skynet")
 	assert.Nil(t, err)
 	assert.Equal(t, "10.0.0.1", node.Endpoint)
 	assert.Equal(t, "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", node.PublicKey)
@@ -113,17 +114,17 @@ func TestSetNetworkNodesLastModified(t *testing.T) {
 	deleteAllNetworks()
 	createNet()
 	t.Run("InvalidNetwork", func(t *testing.T) {
-		err := SetNetworkNodesLastModified("badnet")
+		err := logic.SetNetworkNodesLastModified("badnet")
 		assert.EqualError(t, err, "no result found")
 	})
 	t.Run("NetworkExists", func(t *testing.T) {
-		err := SetNetworkNodesLastModified("skynet")
+		err := logic.SetNetworkNodesLastModified("skynet")
 		assert.Nil(t, err)
 	})
 }
 
 func createTestNode() models.Node {
 	createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet"}
-	node, _ := CreateNode(createnode, "skynet")
+	node, _ := logic.CreateNode(createnode, "skynet")
 	return node
 }

+ 14 - 84
controllers/dnsHttpController.go

@@ -3,13 +3,13 @@ package controller
 import (
 	"encoding/json"
 	"net/http"
+
 	"github.com/go-playground/validator/v10"
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/servercfg"
-	"github.com/txn2/txeh"
 )
 
 func dnsHandlers(r *mux.Router) {
@@ -63,7 +63,7 @@ func GetAllDNS() ([]models.DNSEntry, error) {
 		return []models.DNSEntry{}, err
 	}
 	for _, net := range networks {
-		netdns, err := GetDNS(net.NetID)
+		netdns, err := dnslogic.GetDNS(net.NetID)
 		if err != nil {
 			return []models.DNSEntry{}, nil
 		}
@@ -103,7 +103,7 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
 	var dns []models.DNSEntry
 	var params = mux.Vars(r)
 
-	dns, err := GetCustomDNS(params["network"])
+	dns, err := dnslogic.GetCustomDNS(params["network"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -114,65 +114,11 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(dns)
 }
 
-func GetCustomDNS(network string) ([]models.DNSEntry, error) {
-
-	var dns []models.DNSEntry
-
-	collection, err := database.FetchRecords(database.DNS_TABLE_NAME)
-	if err != nil {
-		return dns, err
-	}
-	for _, value := range collection { // filter for entries based on network
-		var entry models.DNSEntry
-		if err := json.Unmarshal([]byte(value), &entry); err != nil {
-			continue
-		}
-
-		if entry.Network == network {
-			dns = append(dns, entry)
-		}
-	}
-
-	return dns, err
-}
-
-func SetDNS() error {
-	hostfile := txeh.Hosts{}
-	var corefilestring string
-	networks, err := models.GetNetworks()
-	if err != nil && !database.IsEmptyRecord(err){
-		return err
-	}
-
-	for _, net := range networks {
-		corefilestring = corefilestring + net.NetID + " "
-		dns, err := GetDNS(net.NetID)
-		if err != nil && !database.IsEmptyRecord(err) {
-			return err
-		}
-		for _, entry := range dns {
-			hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network)
-		}
-	}
-	if corefilestring == "" {
-		corefilestring = "example.com"
-	}
-
-	err = hostfile.SaveAs("./config/dnsconfig/netmaker.hosts")
-	if err != nil {
-		return err
-	}
-	if servercfg.IsSplitDNS() {
-		err = functions.SetCorefile(corefilestring)
-	}
-	return err
-}
-
 func GetDNSEntryNum(domain string, network string) (int, error) {
 
 	num := 0
 
-	entries, err := GetDNS(network)
+	entries, err := dnslogic.GetDNS(network)
 	if err != nil {
 		return 0, err
 	}
@@ -195,7 +141,7 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
 	var dns []models.DNSEntry
 	var params = mux.Vars(r)
 
-	dns, err := GetDNS(params["network"])
+	dns, err := dnslogic.GetDNS(params["network"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -204,22 +150,6 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(dns)
 }
 
-func GetDNS(network string) ([]models.DNSEntry, error) {
-
-	var dns []models.DNSEntry
-	dns, err := GetNodeDNS(network)
-	if err != nil && !database.IsEmptyRecord(err) {
-		return dns, err
-	}
-	customdns, err := GetCustomDNS(network)
-	if err != nil && !database.IsEmptyRecord(err) {
-		return dns, err
-	}
-
-	dns = append(dns, customdns...)
-	return dns, nil
-}
-
 func createDNS(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 
@@ -241,7 +171,7 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	err = SetDNS()
+	err = dnslogic.SetDNS()
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -296,7 +226,7 @@ func updateDNS(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 	}
-	err = SetDNS()
+	err = dnslogic.SetDNS()
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -319,7 +249,7 @@ func deleteDNS(w http.ResponseWriter, r *http.Request) {
 	}
 	entrytext := params["domain"] + "." + params["network"]
 	functions.PrintUserLog(models.NODE_SERVER_NAME, "deleted dns entry: "+entrytext, 1)
-	err = SetDNS()
+	err = dnslogic.SetDNS()
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -394,13 +324,13 @@ func pushDNS(w http.ResponseWriter, r *http.Request) {
 	// Set header
 	w.Header().Set("Content-Type", "application/json")
 
-	err := SetDNS()
+	err := dnslogic.SetDNS()
 
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	}
-	functions.PrintUserLog(r.Header.Get("user"),"pushed DNS updates to nameserver",1)
+	functions.PrintUserLog(r.Header.Get("user"), "pushed DNS updates to nameserver", 1)
 	json.NewEncoder(w).Encode("DNS Pushed to CoreDNS")
 }
 
@@ -421,7 +351,7 @@ func ValidateDNSCreate(entry models.DNSEntry) error {
 	err := v.Struct(entry)
 	if err != nil {
 		for _, e := range err.(validator.ValidationErrors) {
-			functions.PrintUserLog("", e.Error(),1)
+			functions.PrintUserLog("", e.Error(), 1)
 		}
 	}
 	return err
@@ -442,7 +372,7 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
 		_, err := functions.GetParentNetwork(change.Network)
 		if err != nil {
-			functions.PrintUserLog("",err.Error(),0)
+			functions.PrintUserLog("", err.Error(), 0)
 		}
 		return err == nil
 	})
@@ -465,7 +395,7 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 
 	if err != nil {
 		for _, e := range err.(validator.ValidationErrors) {
-			functions.PrintUserLog("", e.Error(),1)
+			functions.PrintUserLog("", e.Error(), 1)
 		}
 	}
 	return err

+ 3 - 2
controllers/dnsHttpController_test.go

@@ -4,6 +4,7 @@ import (
 	"testing"
 
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
 )
@@ -23,7 +24,7 @@ func TestGetCustomDNS(t *testing.T) {
 	deleteAllNetworks()
 	createNet()
 	createTestNode()
-	dns, err := GetCustomDNS("skynet")
+	dns, err := dnslogic.GetCustomDNS("skynet")
 	assert.Nil(t, err)
 	t.Log(dns)
 }
@@ -39,7 +40,7 @@ func TestGetDNSEntryNum(t *testing.T) {
 func TestGetDNS(t *testing.T) {
 	database.InitializeDatabase()
 	deleteAllNetworks()
-	dns, err := GetDNS("skynet")
+	dns, err := dnslogic.GetDNS("skynet")
 	assert.Nil(t, err)
 	t.Log(dns)
 }

+ 2 - 1
controllers/extClientHttpController.go

@@ -13,6 +13,7 @@ import (
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/skip2/go-qrcode"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -273,7 +274,7 @@ func CreateExtClient(extclient models.ExtClient) error {
 	if err = database.Insert(key, string(data), database.EXT_CLIENT_TABLE_NAME); err != nil {
 		return err
 	}
-	err = SetNetworkNodesLastModified(extclient.Network)
+	err = logic.SetNetworkNodesLastModified(extclient.Network)
 	return err
 }
 

+ 24 - 10
controllers/networkHttpController.go

@@ -12,6 +12,7 @@ import (
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/serverctl"
@@ -337,8 +338,21 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
 }
 
 func DeleteNetwork(network string) error {
-	nodeCount, err := functions.GetNetworkNodeCount(network)
+	nodeCount, err := functions.GetNetworkNonServerNodeCount(network)
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
+		// delete server nodes first then db records
+		servers, err := logic.GetSortedNetworkServerNodes(network)
+		if err == nil {
+			for _, s := range servers {
+				if err = logic.DeleteNode(s.ID, true); err != nil {
+					functions.PrintUserLog("[netmaker]", "could not removed server "+s.Name+" before deleting network "+network, 2)
+				} else {
+					functions.PrintUserLog("[netmaker]", "removed server "+s.Name+" before deleting network "+network, 2)
+				}
+			}
+		} else {
+			functions.PrintUserLog("[netmaker]", "could not remove servers before deleting network "+network, 1)
+		}
 		return database.DeleteRecord(database.NETWORKS_TABLE_NAME, network)
 	}
 	return errors.New("node check failed. All nodes must be deleted before deleting network")
@@ -465,15 +479,15 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
 	var accessToken models.AccessToken
 	s := servercfg.GetServerConfig()
 	servervals := models.ServerConfig{
-		CoreDNSAddr:    s.CoreDNSAddr,
-		APIConnString:  s.APIConnString,
-		APIHost:        s.APIHost,
-		APIPort:        s.APIPort,
-		GRPCConnString: s.GRPCConnString,
-		GRPCHost:       s.GRPCHost,
-		GRPCPort:       s.GRPCPort,
-		GRPCSSL:        s.GRPCSSL,
-		CheckinInterval:        s.CheckinInterval,
+		CoreDNSAddr:     s.CoreDNSAddr,
+		APIConnString:   s.APIConnString,
+		APIHost:         s.APIHost,
+		APIPort:         s.APIPort,
+		GRPCConnString:  s.GRPCConnString,
+		GRPCHost:        s.GRPCHost,
+		GRPCPort:        s.GRPCPort,
+		GRPCSSL:         s.GRPCSSL,
+		CheckinInterval: s.CheckinInterval,
 	}
 	accessToken.ServerConfig = servervals
 	accessToken.ClientConfig.Network = netID

+ 7 - 7
controllers/nodeGrpcController.go

@@ -8,6 +8,7 @@ import (
 
 	"github.com/gravitl/netmaker/functions"
 	nodepb "github.com/gravitl/netmaker/grpc"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 )
 
@@ -67,7 +68,7 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
 		}
 	}
 
-	node, err = CreateNode(node, node.Network)
+	node, err = logic.CreateNode(node, node.Network)
 	if err != nil {
 		return nil, err
 	}
@@ -77,7 +78,7 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
 		Data: string(nodeData),
 		Type: nodepb.NODE_TYPE,
 	}
-	err = SetNetworkNodesLastModified(node.Network)
+	err = logic.SetNetworkNodesLastModified(node.Network)
 	if err != nil {
 		return nil, err
 	}
@@ -134,15 +135,15 @@ func (s *NodeServiceServer) GetPeers(ctx context.Context, req *nodepb.Object) (*
 		if err != nil {
 			return nil, err
 		}
-		if node.IsServer == "yes" {
-			SetNetworkServerPeers(macAndNetwork[1])
+		if node.IsServer == "yes" && logic.IsLeader(&node){
+			logic.SetNetworkServerPeers(&node)
 		}
 		excludeIsRelayed := node.IsRelay != "yes"
 		var relayedNode string
 		if node.IsRelayed == "yes" {
 			relayedNode = node.Address
 		}
-		peers, err := GetPeersList(macAndNetwork[1], excludeIsRelayed, relayedNode)
+		peers, err := logic.GetPeersList(macAndNetwork[1], excludeIsRelayed, relayedNode)
 		if err != nil {
 			return nil, err
 		}
@@ -172,11 +173,10 @@ func (s *NodeServiceServer) GetExtPeers(ctx context.Context, req *nodepb.Object)
 	if len(macAndNetwork) != 2 {
 		return nil, errors.New("did not receive valid node id when fetching ext peers")
 	}
-	peers, err := GetExtPeersList(macAndNetwork[0], macAndNetwork[1])
+	peers, err := logic.GetExtPeersList(macAndNetwork[0], macAndNetwork[1])
 	if err != nil {
 		return nil, err
 	}
-	// cursor.Next() returns a boolean, if false there are no more items and loop will break
 	var extPeers []models.Node
 	for i := 0; i < len(peers); i++ {
 		extPeers = append(extPeers, models.Node{

+ 8 - 30
controllers/nodeHttpController.go

@@ -6,10 +6,11 @@ import (
 	"net/http"
 	"strings"
 	"time"
-
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/crypto/bcrypt"
@@ -267,7 +268,7 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
 	var nodes []models.Node
 	var params = mux.Vars(r)
 	networkName := params["network"]
-	nodes, err := GetNetworkNodes(networkName)
+	nodes, err := logic.GetNetworkNodes(networkName)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -279,29 +280,6 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(nodes)
 }
 
-func GetNetworkNodes(network string) ([]models.Node, error) {
-	var nodes []models.Node
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return []models.Node{}, nil
-		}
-		return nodes, err
-	}
-	for _, value := range collection {
-
-		var node models.Node
-		err := json.Unmarshal([]byte(value), &node)
-		if err != nil {
-			continue
-		}
-		if node.Network == network {
-			nodes = append(nodes, node)
-		}
-	}
-	return nodes, nil
-}
-
 //A separate function to get all nodes, not just nodes for a particular network.
 //Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
 func getAllNodes(w http.ResponseWriter, r *http.Request) {
@@ -335,7 +313,7 @@ func getUsersNodes(user models.User) ([]models.Node, error) {
 	var nodes []models.Node
 	var err error
 	for _, networkName := range user.Networks {
-		tmpNodes, err := GetNetworkNodes(networkName)
+		tmpNodes, err := logic.GetNetworkNodes(networkName)
 		if err != nil {
 			continue
 		}
@@ -437,7 +415,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	node, err = CreateNode(node, networkName)
+	node, err = logic.CreateNode(node, networkName)
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
@@ -682,7 +660,7 @@ func CreateIngressGateway(netid string, macaddress string) (models.Node, error)
 	if err != nil {
 		return models.Node{}, err
 	}
-	err = SetNetworkNodesLastModified(netid)
+	err = logic.SetNetworkNodesLastModified(netid)
 	return node, err
 }
 
@@ -733,7 +711,7 @@ func DeleteIngressGateway(networkName string, macaddress string) (models.Node, e
 	if err != nil {
 		return models.Node{}, err
 	}
-	err = SetNetworkNodesLastModified(networkName)
+	err = logic.SetNetworkNodesLastModified(networkName)
 	return node, err
 }
 
@@ -783,7 +761,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if servercfg.IsDNSMode() {
-		err = SetDNS()
+		err = dnslogic.SetDNS()
 	}
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))

+ 4 - 3
controllers/nodeHttpController_test.go

@@ -4,6 +4,7 @@ import (
 	"testing"
 
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
 )
@@ -81,19 +82,19 @@ func TestGetNetworkNodes(t *testing.T) {
 	deleteAllNetworks()
 	createNet()
 	t.Run("BadNet", func(t *testing.T) {
-		node, err := GetNetworkNodes("badnet")
+		node, err := logic.GetNetworkNodes("badnet")
 		assert.Nil(t, err)
 		assert.Equal(t, []models.Node{}, node)
 		//assert.Equal(t, "mongo: no documents in result", err.Error())
 	})
 	t.Run("NoNodes", func(t *testing.T) {
-		node, err := GetNetworkNodes("skynet")
+		node, err := logic.GetNetworkNodes("skynet")
 		assert.Nil(t, err)
 		assert.Equal(t, []models.Node{}, node)
 	})
 	t.Run("Success", func(t *testing.T) {
 		createTestNode()
-		node, err := GetNetworkNodes("skynet")
+		node, err := logic.GetNetworkNodes("skynet")
 		assert.Nil(t, err)
 		assert.NotEqual(t, []models.Node(nil), node)
 	})

+ 15 - 42
controllers/relay.go

@@ -72,6 +72,21 @@ func CreateRelay(relay models.RelayRequest) (models.Node, error) {
 	return node, nil
 }
 
+func deleteRelay(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	nodeMac := params["macaddress"]
+	netid := params["network"]
+	node, err := DeleteRelay(netid, nodeMac)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	functions.PrintUserLog(r.Header.Get("user"), "deleted egress gateway "+nodeMac+" on network "+netid, 1)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(node)
+}
+
 func SetRelayedNodes(yesOrno string, networkName string, addrs []string) error {
 
 	collections, err := database.FetchRecords(database.NODES_TABLE_NAME)
@@ -125,21 +140,6 @@ func UpdateRelay(network string, oldAddrs []string, newAddrs []string) {
 	}
 }
 
-func deleteRelay(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	nodeMac := params["macaddress"]
-	netid := params["network"]
-	node, err := DeleteRelay(netid, nodeMac)
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-	functions.PrintUserLog(r.Header.Get("user"), "deleted egress gateway "+nodeMac+" on network "+netid, 1)
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
-}
-
 func DeleteRelay(network, macaddress string) (models.Node, error) {
 
 	node, err := functions.GetNodeByMacAddress(network, macaddress)
@@ -171,30 +171,3 @@ func DeleteRelay(network, macaddress string) (models.Node, error) {
 	}
 	return node, nil
 }
-
-func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
-	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
-	var relay models.Node
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return relay, nil
-		}
-		functions.PrintUserLog("", err.Error(), 2)
-		return relay, err
-	}
-	for _, value := range collection {
-		err := json.Unmarshal([]byte(value), &relay)
-		if err != nil {
-			functions.PrintUserLog("", err.Error(), 2)
-			continue
-		}
-		if relay.IsRelay == "yes" {
-			for _, addr := range relay.RelayAddrs {
-				if addr == relayedNodeAddr {
-					return relay, nil
-				}
-			}
-		}
-	}
-	return relay, errors.New("could not find relay for node " + relayedNodeAddr)
-}

+ 3 - 3
controllers/userHttpController.go

@@ -89,17 +89,17 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
 	//Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API untill approved).
 	record, err := database.FetchRecord(database.USERS_TABLE_NAME, authRequest.UserName)
 	if err != nil {
-		return "", errors.New("user " + authRequest.UserName + " not found")
+		return "", errors.New("incorrect credentials")
 	}
 	if err = json.Unmarshal([]byte(record), &result); err != nil {
-		return "", errors.New("user " + authRequest.UserName + " not found")
+		return "", errors.New("incorrect credentials")
 	}
 
 	//compare password from request to stored password in database
 	//might be able to have a common hash (certificates?) and compare those so that a password isn't passed in in plain text...
 	//TODO: Consider a way of hashing the password client side before sending, or using certificates
 	if err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password)); err != nil {
-		return "", errors.New("wrong password")
+		return "", errors.New("incorrect credentials")
 	}
 
 	//Create a new JWT for the node

+ 2 - 2
controllers/userHttpController_test.go

@@ -219,7 +219,7 @@ func TestVerifyAuthRequest(t *testing.T) {
 		authRequest.Password = "password"
 		jwt, err := VerifyAuthRequest(authRequest)
 		assert.Equal(t, "", jwt)
-		assert.EqualError(t, err, "user admin not found")
+		assert.EqualError(t, err, "incorrect credentials")
 	})
 	t.Run("Non-Admin", func(t *testing.T) {
 		user := models.User{"nonadmin", "somepass", nil, false}
@@ -235,7 +235,7 @@ func TestVerifyAuthRequest(t *testing.T) {
 		authRequest := models.UserAuthParams{"admin", "badpass"}
 		jwt, err := VerifyAuthRequest(authRequest)
 		assert.Equal(t, "", jwt)
-		assert.EqualError(t, err, "wrong password")
+		assert.EqualError(t, err, "incorrect credentials")
 	})
 	t.Run("Success", func(t *testing.T) {
 		authRequest := models.UserAuthParams{"admin", "password"}

+ 104 - 0
dnslogic/dns.go

@@ -0,0 +1,104 @@
+package dnslogic
+
+import (
+	"encoding/json"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/servercfg"
+	"github.com/txn2/txeh"
+)
+
+func SetDNS() error {
+	hostfile := txeh.Hosts{}
+	var corefilestring string
+	networks, err := models.GetNetworks()
+	if err != nil && !database.IsEmptyRecord(err) {
+		return err
+	}
+
+	for _, net := range networks {
+		corefilestring = corefilestring + net.NetID + " "
+		dns, err := GetDNS(net.NetID)
+		if err != nil && !database.IsEmptyRecord(err) {
+			return err
+		}
+		for _, entry := range dns {
+			hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network)
+		}
+	}
+	if corefilestring == "" {
+		corefilestring = "example.com"
+	}
+
+	err = hostfile.SaveAs("./config/dnsconfig/netmaker.hosts")
+	if err != nil {
+		return err
+	}
+	if servercfg.IsSplitDNS() {
+		err = functions.SetCorefile(corefilestring)
+	}
+	return err
+}
+
+func GetDNS(network string) ([]models.DNSEntry, error) {
+
+	var dns []models.DNSEntry
+	dns, err := GetNodeDNS(network)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return dns, err
+	}
+	customdns, err := GetCustomDNS(network)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return dns, err
+	}
+
+	dns = append(dns, customdns...)
+	return dns, nil
+}
+
+func GetNodeDNS(network string) ([]models.DNSEntry, error) {
+
+	var dns []models.DNSEntry
+
+	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil {
+		return dns, err
+	}
+
+	for _, value := range collection {
+		var entry models.DNSEntry
+		var node models.Node
+		if err = json.Unmarshal([]byte(value), &node); err != nil {
+			continue
+		}
+		if err = json.Unmarshal([]byte(value), &entry); node.Network == network && err == nil {
+			dns = append(dns, entry)
+		}
+	}
+
+	return dns, nil
+}
+
+func GetCustomDNS(network string) ([]models.DNSEntry, error) {
+
+	var dns []models.DNSEntry
+
+	collection, err := database.FetchRecords(database.DNS_TABLE_NAME)
+	if err != nil {
+		return dns, err
+	}
+	for _, value := range collection { // filter for entries based on network
+		var entry models.DNSEntry
+		if err := json.Unmarshal([]byte(value), &entry); err != nil {
+			continue
+		}
+
+		if entry.Network == network {
+			dns = append(dns, entry)
+		}
+	}
+
+	return dns, err
+}

+ 1 - 9
docker/Dockerfile-netmaker-slim

@@ -9,24 +9,16 @@ ENV GO111MODULE=auto
 
 RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=1 /usr/local/go/bin/go build -ldflags="-w -s" -o netmaker main.go
 
-WORKDIR /app/netclient
-
-RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-w -s" -o netclient main.go
-#second stage
-
 FROM alpine:3.13.6
 # add a c lib
 RUN apk add gcompat iptables
 # set the working directory
 WORKDIR /root/
 
-RUN mkdir /etc/netclient
+RUN mkdir -p /etc/netclient/config
 
 COPY --from=builder /app/netmaker .
 COPY --from=builder /app/config config
-COPY --from=builder /app/netclient/netclient  /etc/netclient/netclient
-
-RUN chmod 0755 /etc/netclient/netclient
 
 EXPOSE 8081
 EXPOSE 50051

+ 5 - 16
functions/helpers.go

@@ -20,11 +20,6 @@ import (
 	"github.com/gravitl/netmaker/servercfg"
 )
 
-func CheckEndpoint(endpoint string) bool {
-	endpointarr := strings.Split(endpoint, ":")
-	return len(endpointarr) == 2
-}
-
 func PrintUserLog(username string, message string, loglevel int) {
 	log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile))
 	if int32(loglevel) <= servercfg.GetVerbose() && servercfg.GetVerbose() != 0 {
@@ -103,9 +98,9 @@ func CreateServerToken(netID string) (string, error) {
 	} else {
 		log.Println("server on linux")
 		servervals = models.ServerConfig{
-			APIConnString:  "127.0.0.1:" + servercfg.GetAPIPort(),
-			GRPCConnString: "127.0.0.1:" + servercfg.GetGRPCPort(),
-			GRPCSSL:        "off",
+			APIConnString:   "127.0.0.1:" + servercfg.GetAPIPort(),
+			GRPCConnString:  "127.0.0.1:" + servercfg.GetGRPCPort(),
+			GRPCSSL:         "off",
 			CheckinInterval: servercfg.GetCheckinInterval(),
 		}
 	}
@@ -380,7 +375,7 @@ func IsMacAddressUnique(macaddress string, networkName string) (bool, error) {
 	return true, nil
 }
 
-func GetNetworkNodeCount(networkName string) (int, error) {
+func GetNetworkNonServerNodeCount(networkName string) (int, error) {
 
 	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
 	count := 0
@@ -392,7 +387,7 @@ func GetNetworkNodeCount(networkName string) (int, error) {
 		if err = json.Unmarshal([]byte(value), &node); err != nil {
 			return count, err
 		} else {
-			if node.Network == networkName {
+			if node.Network == networkName && node.IsServer != "yes" {
 				count++
 			}
 		}
@@ -491,12 +486,6 @@ func IsIpCIDR(host string) bool {
 	return ip != nil && ipnet != nil
 }
 
-//This is used to validate public keys (make sure they're base64 encoded like all public keys should be).
-func IsBase64(s string) bool {
-	_, err := base64.StdEncoding.DecodeString(s)
-	return err == nil
-}
-
 //This  checks to  make sure a network name is valid.
 //Switch to REGEX?
 func NameInNetworkCharSet(name string) bool {

+ 38 - 0
logic/extpeers.go

@@ -0,0 +1,38 @@
+package logic
+
+import (
+	"encoding/json"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+)
+
+func GetExtPeersList(macaddress string, networkName string) ([]models.ExtPeersResponse, error) {
+
+	var peers []models.ExtPeersResponse
+	records, err := database.FetchRecords(database.EXT_CLIENT_TABLE_NAME)
+
+	if err != nil {
+		return peers, err
+	}
+
+	for _, value := range records {
+		var peer models.ExtPeersResponse
+		var extClient models.ExtClient
+		err = json.Unmarshal([]byte(value), &peer)
+		if err != nil {
+			functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal peer", 2)
+			continue
+		}
+		err = json.Unmarshal([]byte(value), &extClient)
+		if err != nil {
+			functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal ext client", 2)
+			continue
+		}
+		if extClient.Network == networkName && extClient.IngressGatewayID == macaddress {
+			peers = append(peers, peer)
+		}
+	}
+	return peers, err
+}

+ 88 - 0
logic/nodes.go

@@ -0,0 +1,88 @@
+package logic
+
+import (
+	"encoding/json"
+	"sort"
+	"time"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+)
+
+func GetNetworkNodes(network string) ([]models.Node, error) {
+	var nodes []models.Node
+	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return []models.Node{}, nil
+		}
+		return nodes, err
+	}
+	for _, value := range collection {
+
+		var node models.Node
+		err := json.Unmarshal([]byte(value), &node)
+		if err != nil {
+			continue
+		}
+		if node.Network == network {
+			nodes = append(nodes, node)
+		}
+	}
+	return nodes, nil
+}
+
+func GetSortedNetworkServerNodes(network string) ([]models.Node, error) {
+	var nodes []models.Node
+	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return []models.Node{}, nil
+		}
+		return nodes, err
+	}
+	for _, value := range collection {
+
+		var node models.Node
+		err := json.Unmarshal([]byte(value), &node)
+		if err != nil {
+			continue
+		}
+		if node.Network == network && node.IsServer == "yes" {
+			nodes = append(nodes, node)
+		}
+	}
+	sort.Sort(models.NodesArray(nodes))
+	return nodes, nil
+}
+
+func GetPeers(node models.Node) ([]models.Node, error) {
+	if node.IsServer == "yes" && IsLeader(&node) {
+		SetNetworkServerPeers(&node)
+	}
+	excludeIsRelayed := node.IsRelay != "yes"
+	var relayedNode string
+	if node.IsRelayed == "yes" {
+		relayedNode = node.Address
+	}
+	peers, err := GetPeersList(node.Network, excludeIsRelayed, relayedNode)
+	if err != nil {
+		return nil, err
+	}
+	return peers, nil
+}
+
+func IsLeader(node *models.Node) bool {
+	nodes, err := GetSortedNetworkServerNodes(node.Network)
+	if err != nil {
+		functions.PrintUserLog("[netmaker]", "ERROR: COULD NOT RETRIEVE SERVER NODES. THIS WILL BREAK HOLE PUNCHING.", 0)
+		return false
+	}
+	for _, n := range nodes {
+		if n.LastModified > time.Now().Add(-1*time.Minute).Unix() {
+			return n.Address == node.Address
+		}
+	}
+	return len(nodes) <= 1 || nodes[1].Address == node.Address
+}

+ 285 - 0
logic/util.go

@@ -0,0 +1,285 @@
+// package for logicing client and server code
+package logic
+
+import (
+	"encoding/json"
+	"strconv"
+	"strings"
+	"time"
+	"encoding/base64"
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/dnslogic"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/relay"
+	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/crypto/bcrypt"
+)
+
+//This is used to validate public keys (make sure they're base64 encoded like all public keys should be).
+func IsBase64(s string) bool {
+	_, err := base64.StdEncoding.DecodeString(s)
+	return err == nil
+}
+
+func CheckEndpoint(endpoint string) bool {
+	endpointarr := strings.Split(endpoint, ":")
+	return len(endpointarr) == 2
+}
+
+func SetNetworkServerPeers(node *models.Node) {
+	if currentPeersList, err := GetSystemPeers(node); err == nil {
+		if database.SetPeers(currentPeersList, node.Network) {
+			functions.PrintUserLog(models.NODE_SERVER_NAME, "set new peers on network "+node.Network, 1)
+		}
+	} else {
+		functions.PrintUserLog(models.NODE_SERVER_NAME, "could not set peers on network "+node.Network, 1)
+		functions.PrintUserLog(models.NODE_SERVER_NAME, err.Error(), 1)
+	}
+}
+
+
+func DeleteNode(key string, exterminate bool) error {
+	var err error
+	if !exterminate {
+		args := strings.Split(key, "###")
+		node, err := GetNode(args[0], args[1])
+		if err != nil {
+			return err
+		}
+		node.Action = models.NODE_DELETE
+		nodedata, err := json.Marshal(&node)
+		if err != nil {
+			return err
+		}
+		err = database.Insert(key, string(nodedata), database.DELETED_NODES_TABLE_NAME)
+		if err != nil {
+			return err
+		}
+	} else {
+		if err := database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, key); err != nil {
+			functions.PrintUserLog("", err.Error(), 2)
+		}
+	}
+	if err := database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
+		return err
+	}
+	if servercfg.IsDNSMode() {
+		err = dnslogic.SetDNS()
+	}
+	return err
+}
+
+
+func CreateNode(node models.Node, networkName string) (models.Node, error) {
+
+	//encrypt that password so we never see it
+	hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
+
+	if err != nil {
+		return node, err
+	}
+	//set password to encrypted password
+	node.Password = string(hash)
+
+	node.Network = networkName
+	if node.Name == models.NODE_SERVER_NAME {
+		node.IsServer = "yes"
+	}
+	if servercfg.IsDNSMode() && node.DNSOn == "" {
+		node.DNSOn = "yes"
+	}
+	node.SetDefaults()
+	node.Address, err = functions.UniqueAddress(networkName)
+	if err != nil {
+		return node, err
+	}
+	node.Address6, err = functions.UniqueAddress6(networkName)
+	if err != nil {
+		return node, err
+	}
+	//Create a JWT for the node
+	tokenString, _ := functions.CreateJWT(node.MacAddress, networkName)
+	if tokenString == "" {
+		//returnErrorResponse(w, r, errorResponse)
+		return node, err
+	}
+	err = node.Validate(false)
+	if err != nil {
+		return node, err
+	}
+	key, err := functions.GetRecordKey(node.MacAddress, node.Network)
+	if err != nil {
+		return node, err
+	}
+	nodebytes, err := json.Marshal(&node)
+	if err != nil {
+		return node, err
+	}
+	err = database.Insert(key, string(nodebytes), database.NODES_TABLE_NAME)
+	if err != nil {
+		return node, err
+	}
+	if node.IsPending != "yes" {
+		functions.DecrimentKey(node.Network, node.AccessKey)
+	}
+	SetNetworkNodesLastModified(node.Network)
+	if servercfg.IsDNSMode() {
+		err = dnslogic.SetDNS()
+	}
+	return node, err
+}
+
+func SetNetworkNodesLastModified(networkName string) error {
+
+	timestamp := time.Now().Unix()
+
+	network, err := functions.GetParentNetwork(networkName)
+	if err != nil {
+		return err
+	}
+	network.NodesLastModified = timestamp
+	data, err := json.Marshal(&network)
+	if err != nil {
+		return err
+	}
+	err = database.Insert(networkName, string(data), database.NETWORKS_TABLE_NAME)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func GetNode(macaddress string, network string) (models.Node, error) {
+	var node models.Node
+
+	key, err := functions.GetRecordKey(macaddress, network)
+	if err != nil {
+		return node, err
+	}
+	data, err := database.FetchRecord(database.NODES_TABLE_NAME, key)
+	if err != nil {
+		if data == "" {
+			data, err = database.FetchRecord(database.DELETED_NODES_TABLE_NAME, key)
+			err = json.Unmarshal([]byte(data), &node)
+		}
+		return node, err
+	}
+	if err = json.Unmarshal([]byte(data), &node); err != nil {
+		return node, err
+	}
+	node.SetDefaults()
+
+	return node, err
+}
+
+func GetNodePeers(networkName string, excludeRelayed bool) ([]models.Node, error) {
+	var peers []models.Node
+	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return peers, nil
+		}
+		functions.PrintUserLog("", err.Error(), 2)
+		return nil, err
+	}
+	udppeers, errN := database.GetPeers(networkName)
+	if errN != nil {
+		functions.PrintUserLog("", errN.Error(), 2)
+	}
+	for _, value := range collection {
+		var node models.Node
+		var peer models.Node
+		err := json.Unmarshal([]byte(value), &node)
+		if err != nil {
+			functions.PrintUserLog("", err.Error(), 2)
+			continue
+		}
+		if node.IsEgressGateway == "yes" { // handle egress stuff
+			peer.EgressGatewayRanges = node.EgressGatewayRanges
+			peer.IsEgressGateway = node.IsEgressGateway
+		}
+		allow := node.IsRelayed != "yes" || !excludeRelayed
+
+		if node.Network == networkName && node.IsPending != "yes" && allow {
+			peer = setPeerInfo(node)
+			if node.UDPHolePunch == "yes" && errN == nil && CheckEndpoint(udppeers[node.PublicKey]) {
+				endpointstring := udppeers[node.PublicKey]
+				endpointarr := strings.Split(endpointstring, ":")
+				if len(endpointarr) == 2 {
+					port, err := strconv.Atoi(endpointarr[1])
+					if err == nil {
+						peer.Endpoint = endpointarr[0]
+						peer.ListenPort = int32(port)
+					}
+				}
+			}
+			if node.IsRelay == "yes" {
+				network, err := models.GetNetwork(networkName)
+				if err == nil {
+					peer.AllowedIPs = append(peer.AllowedIPs, network.AddressRange)
+				} else {
+					peer.AllowedIPs = append(peer.AllowedIPs, node.RelayAddrs...)
+				}
+			}
+			peers = append(peers, peer)
+		}
+	}
+
+	return peers, err
+}
+
+func GetPeersList(networkName string, excludeRelayed bool, relayedNodeAddr string) ([]models.Node, error) {
+	var peers []models.Node
+	var relayNode models.Node
+	var err error
+	if relayedNodeAddr == "" {
+		peers, err = GetNodePeers(networkName, excludeRelayed)
+
+	} else {
+		relayNode, err = relay.GetNodeRelay(networkName, relayedNodeAddr)
+		if relayNode.Address != "" {
+			relayNode = setPeerInfo(relayNode)
+			network, err := models.GetNetwork(networkName)
+			if err == nil {
+				relayNode.AllowedIPs = append(relayNode.AllowedIPs, network.AddressRange)
+			} else {
+				relayNode.AllowedIPs = append(relayNode.AllowedIPs, relayNode.RelayAddrs...)
+			}
+			nodepeers, err := GetNodePeers(networkName, false)
+			if err == nil && relayNode.UDPHolePunch == "yes" {
+				for _, nodepeer := range nodepeers {
+					if nodepeer.Address == relayNode.Address {
+						relayNode.Endpoint = nodepeer.Endpoint
+						relayNode.ListenPort = nodepeer.ListenPort
+					}
+				}
+			}
+
+			peers = append(peers, relayNode)
+		}
+	}
+	return peers, err
+}
+
+func setPeerInfo(node models.Node) models.Node {
+	var peer models.Node
+	peer.RelayAddrs = node.RelayAddrs
+	peer.IsRelay = node.IsRelay
+	peer.IsServer = node.IsServer
+	peer.IsRelayed = node.IsRelayed
+	peer.PublicKey = node.PublicKey
+	peer.Endpoint = node.Endpoint
+	peer.LocalAddress = node.LocalAddress
+	peer.ListenPort = node.ListenPort
+	peer.AllowedIPs = node.AllowedIPs
+	peer.UDPHolePunch = node.UDPHolePunch
+	peer.Address = node.Address
+	peer.Address6 = node.Address6
+	peer.EgressGatewayRanges = node.EgressGatewayRanges
+	peer.IsEgressGateway = node.IsEgressGateway
+	peer.IngressGatewayRange = node.IngressGatewayRange
+	peer.IsIngressGateway = node.IsIngressGateway
+	peer.IsPending = node.IsPending
+	return peer
+}

+ 25 - 0
logic/wireguard.go

@@ -0,0 +1,25 @@
+package logic
+
+import (
+	"github.com/gravitl/netmaker/models"
+	"golang.zx2c4.com/wireguard/wgctrl"
+)
+
+func GetSystemPeers(node *models.Node) (map[string]string, error) {
+	peers := make(map[string]string)
+
+	client, err := wgctrl.New()
+	if err != nil {
+		return peers, err
+	}
+	device, err := client.Device(node.Interface)
+	if err != nil {
+		return nil, err
+	}
+	for _, peer := range device.Peers {
+		if IsBase64(peer.PublicKey.String()) && peer.Endpoint != nil && CheckEndpoint(peer.Endpoint.String()) {
+			peers[peer.PublicKey.String()] = peer.Endpoint.String()
+		}
+	}
+	return peers, nil
+}

+ 3 - 4
main.go

@@ -15,6 +15,7 @@ import (
 
 	controller "github.com/gravitl/netmaker/controllers"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/dnslogic"
 	"github.com/gravitl/netmaker/functions"
 	nodepb "github.com/gravitl/netmaker/grpc"
 	"github.com/gravitl/netmaker/models"
@@ -81,14 +82,13 @@ func startControllers() {
 		go runGRPC(&waitnetwork)
 	}
 
-	// Run the client in goroutine locally if CLIENT_MODE is "contained"
-	if servercfg.IsClientMode() == "contained" {
+	if servercfg.IsClientMode() == "on" {
 		waitnetwork.Add(1)
 		go runClient(&waitnetwork)
 	}
 
 	if servercfg.IsDNSMode() {
-		err := controller.SetDNS()
+		err := dnslogic.SetDNS()
 		if err != nil {
 			log.Println("error occurred initializing DNS:", err)
 		}
@@ -115,7 +115,6 @@ func startControllers() {
 
 func runClient(wg *sync.WaitGroup) {
 	defer wg.Done()
-	log.Println("CLIENT_MODE running as contained")
 	go func() {
 		for {
 			if err := serverctl.HandleContainedClient(); err != nil {

+ 22 - 7
models/node.go

@@ -7,7 +7,7 @@ import (
 	"net"
 	"strings"
 	"time"
-
+	"bytes"
 	"github.com/go-playground/validator/v10"
 	"github.com/gravitl/netmaker/database"
 	"golang.org/x/crypto/bcrypt"
@@ -76,6 +76,18 @@ type Node struct {
 	MTU                 int32    `json:"mtu" bson:"mtu" yaml:"mtu"`
 }
 
+type NodesArray []Node
+
+func (a NodesArray) Len() int           { return len(a) }
+func (a NodesArray) Less(i, j int) bool { return isLess(a[i].Address, a[j].Address) }
+func (a NodesArray) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+
+func isLess(ipA string, ipB string) bool {
+	ipNetA := net.ParseIP(ipA)
+	ipNetB := net.ParseIP(ipB)
+	return bytes.Compare(ipNetA, ipNetB) < 0
+}
+
 func (node *Node) SetDefaultMTU() {
 	if node.MTU == 0 {
 		node.MTU = 1280
@@ -263,9 +275,12 @@ func (node *Node) SetDefaults() {
 		}
 	}
 	// == Parent Network settings ==
-	node.CheckInInterval = parentNetwork.DefaultCheckInInterval
-	node.IsDualStack = parentNetwork.IsDualStack
-	node.MTU = parentNetwork.DefaultMTU
+	if node.IsDualStack == "" {
+		node.IsDualStack = parentNetwork.IsDualStack
+	}
+	if node.MTU == 0 {
+		node.MTU = parentNetwork.DefaultMTU
+	}
 	// == node defaults if not set by parent ==
 	node.SetIPForwardingDefault()
 	node.SetDNSOnDefault()
@@ -535,9 +550,9 @@ func GetAllNodes() ([]Node, error) {
 	return nodes, nil
 }
 
-func GetID(macaddress string, network string) (string, error) {
-	if macaddress == "" || network == "" {
+func (node *Node) GetID() (string, error) {
+	if node.MacAddress == "" || node.Network == "" {
 		return "", errors.New("unable to get record key")
 	}
-	return macaddress + "###" + network, nil
+	return node.MacAddress + "###" + node.Network, nil
 }

+ 5 - 4
netclient/command/commands.go

@@ -58,7 +58,7 @@ func Join(cfg config.ClientConfig, privateKey string) error {
 
 func getWindowsInterval() int {
 	interval := 15
-	networks, err := functions.GetNetworks()
+	networks, err := ncutils.GetSystemNetworks()
 	if err != nil {
 		return interval
 	}
@@ -90,12 +90,13 @@ func RunUserspaceDaemon() {
 
 func CheckIn(cfg config.ClientConfig) error {
 	var err error
+
 	if cfg.Network == "" {
 		ncutils.PrintLog("required, '-n', exiting", 0)
 		os.Exit(1)
 	} else if cfg.Network == "all" {
 		ncutils.PrintLog("running checkin for all networks", 1)
-		networks, err := functions.GetNetworks()
+		networks, err := ncutils.GetSystemNetworks()
 		if err != nil {
 			ncutils.PrintLog("error retrieving networks, exiting", 1)
 			return err
@@ -138,7 +139,7 @@ func Push(cfg config.ClientConfig) error {
 	var err error
 	if cfg.Network == "all" || ncutils.IsWindows() {
 		ncutils.PrintLog("pushing config to server for all networks.", 0)
-		networks, err := functions.GetNetworks()
+		networks, err := ncutils.GetSystemNetworks()
 		if err != nil {
 			ncutils.PrintLog("error retrieving networks, exiting.", 0)
 			return err
@@ -164,7 +165,7 @@ func Pull(cfg config.ClientConfig) error {
 	var err error
 	if cfg.Network == "all" {
 		ncutils.PrintLog("No network selected. Running Pull for all networks.", 0)
-		networks, err := functions.GetNetworks()
+		networks, err := ncutils.GetSystemNetworks()
 		if err != nil {
 			ncutils.PrintLog("Error retrieving networks. Exiting.", 1)
 			return err

+ 94 - 72
netclient/functions/checkin.go

@@ -1,12 +1,15 @@
 package functions
 
 import (
+	"context"
 	"encoding/json"
 	"errors"
 	"os"
 	"runtime"
 	"strings"
+
 	nodepb "github.com/gravitl/netmaker/grpc"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/auth"
 	"github.com/gravitl/netmaker/netclient/config"
@@ -136,7 +139,6 @@ func CheckConfig(cliconf config.ClientConfig) error {
 	if newNode.IsPending == "yes" {
 		return errors.New("node is pending")
 	}
-
 	actionCompleted := checkNodeActions(newNode, network, servercfg, &currentNode, cfg)
 	if actionCompleted == models.NODE_DELETE {
 		return errors.New("node has been removed")
@@ -164,32 +166,40 @@ func Pull(network string, manual bool) (*models.Node, error) {
 			return nil, err
 		}
 	}
-	conn, err := grpc.Dial(cfg.Server.GRPCAddress,
-		ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
-	if err != nil {
-		ncutils.PrintLog("Cant dial GRPC server: "+err.Error(), 1)
-		return nil, err
-	}
-	defer conn.Close()
-	wcclient := nodepb.NewNodeServiceClient(conn)
+	var resNode models.Node // just need to fill this with either server calls or client calls
+	var ctx context.Context
+	if cfg.Node.IsServer != "yes" {
+		conn, err := grpc.Dial(cfg.Server.GRPCAddress,
+			ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
+		if err != nil {
+			ncutils.PrintLog("Cant dial GRPC server: "+err.Error(), 1)
+			return nil, err
+		}
+		defer conn.Close()
+		wcclient := nodepb.NewNodeServiceClient(conn)
 
-	ctx, err := auth.SetJWT(wcclient, network)
-	if err != nil {
-		ncutils.PrintLog("Failed to authenticate: "+err.Error(), 1)
-		return nil, err
-	}
+		ctx, err := auth.SetJWT(wcclient, network)
+		if err != nil {
+			ncutils.PrintLog("Failed to authenticate: "+err.Error(), 1)
+			return nil, err
+		}
 
-	req := &nodepb.Object{
-		Data: node.MacAddress + "###" + node.Network,
-		Type: nodepb.STRING_TYPE,
-	}
-	readres, err := wcclient.ReadNode(ctx, req, grpc.Header(&header))
-	if err != nil {
-		return nil, err
-	}
-	var resNode models.Node
-	if err = json.Unmarshal([]byte(readres.Data), &resNode); err != nil {
-		return nil, err
+		req := &nodepb.Object{
+			Data: node.MacAddress + "###" + node.Network,
+			Type: nodepb.STRING_TYPE,
+		}
+		readres, err := wcclient.ReadNode(ctx, req, grpc.Header(&header))
+		if err != nil {
+			return nil, err
+		}
+		if err = json.Unmarshal([]byte(readres.Data), &resNode); err != nil {
+			return nil, err
+		}
+	} else { // handle server side read
+		resNode, err = logic.GetNode(node.MacAddress, node.Network)
+		if err != nil && !ncutils.IsEmptyRecord(err) {
+			return nil, err
+		}
 	}
 	// ensure that the OS never changes
 	resNode.OS = runtime.GOOS
@@ -211,14 +221,20 @@ func Pull(network string, manual bool) (*models.Node, error) {
 		if err != nil {
 			return &resNode, err
 		}
-		req := &nodepb.Object{
-			Data:     string(nodeData),
-			Type:     nodepb.NODE_TYPE,
-			Metadata: "",
-		}
-		_, err = wcclient.UpdateNode(ctx, req, grpc.Header(&header))
-		if err != nil {
-			return &resNode, err
+		if resNode.IsServer != "yes" {
+			req := &nodepb.Object{
+				Data:     string(nodeData),
+				Type:     nodepb.NODE_TYPE,
+				Metadata: "",
+			}
+			_, err = wcclient.UpdateNode(ctx, req, grpc.Header(&header))
+			if err != nil {
+				return &resNode, err
+			}
+		} else { // handle server side update
+			if err = resNode.Update(&resNode); err != nil {
+				return &resNode, err
+			}
 		}
 	} else {
 		if err = wireguard.SetWGConfig(network, true); err != nil {
@@ -244,54 +260,60 @@ func Push(network string) error {
 	postnode := cfg.Node
 	// always set the OS on client
 	postnode.OS = runtime.GOOS
-	var header metadata.MD
-
-	var wcclient nodepb.NodeServiceClient
-	conn, err := grpc.Dial(cfg.Server.GRPCAddress,
-		ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
-	if err != nil {
-		ncutils.PrintLog("Cant dial GRPC server: "+err.Error(), 1)
-		return err
-	}
-	defer conn.Close()
-	wcclient = nodepb.NewNodeServiceClient(conn)
+	postnode.SetLastCheckIn()
 
-	ctx, err := auth.SetJWT(wcclient, network)
-	if err != nil {
-		ncutils.PrintLog("Failed to authenticate with server: "+err.Error(), 1)
-		return err
-	}
-	if postnode.IsPending != "yes" {
-		privateKey, err := wireguard.RetrievePrivKey(network)
+	if postnode.IsServer != "yes" { // handle client side
+		var header metadata.MD
+		var wcclient nodepb.NodeServiceClient
+		conn, err := grpc.Dial(cfg.Server.GRPCAddress,
+			ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
 		if err != nil {
+			ncutils.PrintLog("Cant dial GRPC server: "+err.Error(), 1)
 			return err
 		}
-		privateKeyWG, err := wgtypes.ParseKey(privateKey)
+		defer conn.Close()
+		wcclient = nodepb.NewNodeServiceClient(conn)
+
+		ctx, err := auth.SetJWT(wcclient, network)
 		if err != nil {
+			ncutils.PrintLog("Failed to authenticate with server: "+err.Error(), 1)
 			return err
 		}
-		if postnode.PublicKey != privateKeyWG.PublicKey().String() {
-			postnode.PublicKey = privateKeyWG.PublicKey().String()
+		if postnode.IsPending != "yes" {
+			privateKey, err := wireguard.RetrievePrivKey(network)
+			if err != nil {
+				return err
+			}
+			privateKeyWG, err := wgtypes.ParseKey(privateKey)
+			if err != nil {
+				return err
+			}
+			if postnode.PublicKey != privateKeyWG.PublicKey().String() {
+				postnode.PublicKey = privateKeyWG.PublicKey().String()
+			}
+		}
+		nodeData, err := json.Marshal(&postnode)
+		if err != nil {
+			return err
 		}
-	}
-	postnode.SetLastCheckIn()
-	nodeData, err := json.Marshal(&postnode)
-	if err != nil {
-		return err
-	}
 
-	req := &nodepb.Object{
-		Data:     string(nodeData),
-		Type:     nodepb.NODE_TYPE,
-		Metadata: "",
-	}
-	data, err := wcclient.UpdateNode(ctx, req, grpc.Header(&header))
-	if err != nil {
-		return err
-	}
-	err = json.Unmarshal([]byte(data.Data), &postnode)
-	if err != nil {
-		return err
+		req := &nodepb.Object{
+			Data:     string(nodeData),
+			Type:     nodepb.NODE_TYPE,
+			Metadata: "",
+		}
+		data, err := wcclient.UpdateNode(ctx, req, grpc.Header(&header))
+		if err != nil {
+			return err
+		}
+		err = json.Unmarshal([]byte(data.Data), &postnode)
+		if err != nil {
+			return err
+		}
+	} else {
+		if err = postnode.Update(&postnode); err != nil {
+			return err
+		}
 	}
 	err = config.ModConfig(&postnode)
 	return err

+ 18 - 40
netclient/functions/common.go

@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"log"
 	"net"
 	"os"
@@ -13,6 +12,7 @@ import (
 	"strings"
 
 	nodepb "github.com/gravitl/netmaker/grpc"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/auth"
 	"github.com/gravitl/netmaker/netclient/config"
@@ -138,7 +138,7 @@ func GetNode(network string) models.Node {
 }
 
 func Uninstall() error {
-	networks, err := GetNetworks()
+	networks, err := ncutils.GetSystemNetworks()
 	if err != nil {
 		ncutils.PrintLog("unable to retrieve networks: "+err.Error(), 1)
 		ncutils.PrintLog("continuing uninstall without leaving networks", 1)
@@ -163,7 +163,6 @@ func Uninstall() error {
 }
 
 func LeaveNetwork(network string) error {
-	//need to  implement checkin on server side
 	cfg, err := config.ReadConfig(network)
 	if err != nil {
 		return err
@@ -171,19 +170,20 @@ func LeaveNetwork(network string) error {
 	servercfg := cfg.Server
 	node := cfg.Node
 
-	var wcclient nodepb.NodeServiceClient
-	conn, err := grpc.Dial(cfg.Server.GRPCAddress,
-		ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
-	if err != nil {
-		log.Printf("Unable to establish client connection to "+servercfg.GRPCAddress+": %v", err)
-	} else {
+	if node.IsServer != "yes" {
+		var wcclient nodepb.NodeServiceClient
+		conn, err := grpc.Dial(cfg.Server.GRPCAddress,
+			ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
+		if err != nil {
+			log.Printf("Unable to establish client connection to "+servercfg.GRPCAddress+": %v", err)
+		}
 		defer conn.Close()
 		wcclient = nodepb.NewNodeServiceClient(conn)
 
 		ctx, err := auth.SetJWT(wcclient, network)
 		if err != nil {
 			log.Printf("Failed to authenticate: %v", err)
-		} else {
+		} else { // handle client side
 			node.SetID()
 			var header metadata.MD
 			_, err = wcclient.DeleteNode(
@@ -200,6 +200,13 @@ func LeaveNetwork(network string) error {
 				ncutils.PrintLog("removed machine from "+node.Network+" network on remote server", 1)
 			}
 		}
+	} else { // handle server side
+		node.SetID()
+		if err = logic.DeleteNode(node.ID, true); err != nil {
+			ncutils.PrintLog("error removing server on network "+node.Network, 1)
+		} else {
+			ncutils.PrintLog("removed netmaker server instance on  "+node.Network, 1)
+		}
 	}
 	return RemoveLocalInstance(cfg, network)
 }
@@ -244,7 +251,7 @@ func DeleteInterface(ifacename string, postdown string) error {
 
 func List() error {
 
-	networks, err := GetNetworks()
+	networks, err := ncutils.GetSystemNetworks()
 	if err != nil {
 		return err
 	}
@@ -267,34 +274,6 @@ func List() error {
 	return nil
 }
 
-func GetNetworks() ([]string, error) {
-	var networks []string
-	files, err := ioutil.ReadDir(ncutils.GetNetclientPathSpecific())
-	if err != nil {
-		return networks, err
-	}
-	for _, f := range files {
-		if strings.Contains(f.Name(), "netconfig-") {
-			networkname := stringAfter(f.Name(), "netconfig-")
-			networks = append(networks, networkname)
-		}
-	}
-	return networks, err
-}
-
-func stringAfter(original string, substring string) string {
-	position := strings.LastIndex(original, substring)
-	if position == -1 {
-		return ""
-	}
-	adjustedPosition := position + len(substring)
-
-	if adjustedPosition >= len(original) {
-		return ""
-	}
-	return original[adjustedPosition:len(original)]
-}
-
 func WipeLocal(network string) error {
 	cfg, err := config.ReadConfig(network)
 	if err != nil {
@@ -302,7 +281,6 @@ func WipeLocal(network string) error {
 	}
 	nodecfg := cfg.Node
 	ifacename := nodecfg.Interface
-
 	if ifacename != "" {
 		if !ncutils.IsKernel() {
 			if err = wireguard.RemoveConf(ifacename, true); err == nil {

+ 55 - 40
netclient/functions/join.go

@@ -8,6 +8,7 @@ import (
 	"log"
 
 	nodepb "github.com/gravitl/netmaker/grpc"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/auth"
 	"github.com/gravitl/netmaker/netclient/config"
@@ -28,7 +29,6 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 		return err
 	}
 
-	ncutils.Log("joining " + cfg.Network + " at " + cfg.Server.GRPCAddress)
 	err := config.Write(&cfg, cfg.Network)
 	if err != nil {
 		return err
@@ -53,7 +53,6 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 			cfg.Node.Endpoint = cfg.Node.LocalAddress
 		} else {
 			cfg.Node.Endpoint, err = ncutils.GetPublicIP()
-
 		}
 		if err != nil || cfg.Node.Endpoint == "" {
 			ncutils.Log("Error setting cfg.Node.Endpoint.")
@@ -82,17 +81,8 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 		}
 	}
 
-	var wcclient nodepb.NodeServiceClient
-
-	conn, err := grpc.Dial(cfg.Server.GRPCAddress,
-		ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
-
-	if err != nil {
-		log.Fatalf("Unable to establish client connection to "+cfg.Server.GRPCAddress+": %v", err)
-	}
-	defer conn.Close()
-	wcclient = nodepb.NewNodeServiceClient(conn)
-
+	// differentiate between client/server here
+	var node models.Node // fill this node with appropriate calls
 	postnode := &models.Node{
 		Password:            cfg.Node.Password,
 		MacAddress:          cfg.Node.MacAddress,
@@ -105,37 +95,63 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 		LocalAddress:        cfg.Node.LocalAddress,
 		Interface:           cfg.Node.Interface,
 		PublicKey:           cfg.Node.PublicKey,
-		DNSOn:           	 cfg.Node.DNSOn,
+		DNSOn:               cfg.Node.DNSOn,
 		Name:                cfg.Node.Name,
 		Endpoint:            cfg.Node.Endpoint,
 		SaveConfig:          cfg.Node.SaveConfig,
 		UDPHolePunch:        cfg.Node.UDPHolePunch,
 	}
 
-	if err = config.ModConfig(postnode); err != nil {
-		return err
-	}
-	data, err := json.Marshal(postnode)
-	if err != nil {
-		return err
-	}
-	// Create node on server
-	res, err := wcclient.CreateNode(
-		context.TODO(),
-		&nodepb.Object{
-			Data: string(data),
-			Type: nodepb.NODE_TYPE,
-		},
-	)
-	if err != nil {
-		return err
-	}
-	ncutils.PrintLog("node created on remote server...updating configs", 1)
+	if cfg.Node.IsServer != "yes" {
+		ncutils.Log("joining " + cfg.Network + " at " + cfg.Server.GRPCAddress)
+		var wcclient nodepb.NodeServiceClient
 
-	nodeData := res.Data
-	var node models.Node
-	if err = json.Unmarshal([]byte(nodeData), &node); err != nil {
-		return err
+		conn, err := grpc.Dial(cfg.Server.GRPCAddress,
+			ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
+
+		if err != nil {
+			log.Fatalf("Unable to establish client connection to "+cfg.Server.GRPCAddress+": %v", err)
+		}
+		defer conn.Close()
+		wcclient = nodepb.NewNodeServiceClient(conn)
+
+		if err = config.ModConfig(postnode); err != nil {
+			return err
+		}
+		data, err := json.Marshal(postnode)
+		if err != nil {
+			return err
+		}
+		// Create node on server
+		res, err := wcclient.CreateNode(
+			context.TODO(),
+			&nodepb.Object{
+				Data: string(data),
+				Type: nodepb.NODE_TYPE,
+			},
+		)
+		if err != nil {
+			return err
+		}
+		ncutils.PrintLog("node created on remote server...updating configs", 1)
+
+		nodeData := res.Data
+		if err = json.Unmarshal([]byte(nodeData), &node); err != nil {
+			return err
+		}
+	} else { // handle server side node creation
+		ncutils.Log("adding a server instance on network " + postnode.Network)
+		if err = config.ModConfig(postnode); err != nil {
+			return err
+		}
+		node, err = logic.CreateNode(*postnode, cfg.Network)
+		if err != nil {
+			return err
+		}
+		err = logic.SetNetworkNodesLastModified(node.Network)
+		if err != nil {
+			return err
+		}
 	}
 
 	// get free port based on returned default listen port
@@ -177,9 +193,8 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
 		}
 	}
 
-	ncutils.Log("retrieving remote peers")
-	peers, hasGateway, gateways, err := server.GetPeers(node.MacAddress, cfg.Network, cfg.Server.GRPCAddress, node.IsDualStack == "yes", node.IsIngressGateway == "yes")
-
+	ncutils.Log("retrieving peers")
+	peers, hasGateway, gateways, err := server.GetPeers(node.MacAddress, cfg.Network, cfg.Server.GRPCAddress, node.IsDualStack == "yes", node.IsIngressGateway == "yes", node.IsServer == "yes")
 	if err != nil && !ncutils.IsEmptyRecord(err) {
 		ncutils.Log("failed to retrieve peers")
 		return err

+ 1 - 1
netclient/main.go

@@ -24,7 +24,7 @@ func main() {
 	app := cli.NewApp()
 	app.Name = "Netclient CLI"
 	app.Usage = "Netmaker's netclient agent and CLI. Used to perform interactions with Netmaker server and set local WireGuard config."
-	app.Version = "v0.8.1"
+	app.Version = "v0.8.3"
 
 	cliFlags := []cli.Flag{
 		&cli.StringFlag{

+ 28 - 0
netclient/ncutils/netclientutils.go

@@ -368,3 +368,31 @@ func PrintLog(message string, loglevel int) {
 		log.Println("[netclient]", message)
 	}
 }
+
+func GetSystemNetworks() ([]string, error) {
+	var networks []string
+	files, err := ioutil.ReadDir(GetNetclientPathSpecific())
+	if err != nil {
+		return networks, err
+	}
+	for _, f := range files {
+		if strings.Contains(f.Name(), "netconfig-") {
+			networkname := stringAfter(f.Name(), "netconfig-")
+			networks = append(networks, networkname)
+		}
+	}
+	return networks, err
+}
+
+func stringAfter(original string, substring string) string {
+	position := strings.LastIndex(original, substring)
+	if position == -1 {
+		return ""
+	}
+	adjustedPosition := position + len(substring)
+
+	if adjustedPosition >= len(original) {
+		return ""
+	}
+	return original[adjustedPosition:len(original)]
+}

+ 113 - 83
netclient/server/grpc.go

@@ -9,6 +9,7 @@ import (
 	"time"
 
 	nodepb "github.com/gravitl/netmaker/grpc"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/netclient/auth"
 	"github.com/gravitl/netmaker/netclient/config"
@@ -40,30 +41,32 @@ func CheckIn(network string) (*models.Node, error) {
 		return nil, err
 	}
 	node := cfg.Node
-	wcclient, err := getGrpcClient(cfg)
-	if err != nil {
-		return nil, err
-	}
-	// == run client action ==
-	var header metadata.MD
-	ctx, err := auth.SetJWT(wcclient, network)
-	nodeData, err := json.Marshal(&node)
-	if err != nil {
-		return nil, err
-	}
-	response, err := wcclient.ReadNode(
-		ctx,
-		&nodepb.Object{
-			Data: string(nodeData),
-			Type: nodepb.NODE_TYPE,
-		},
-		grpc.Header(&header),
-	)
-	if err != nil {
-		log.Printf("Encountered error checking in node: %v", err)
-	}
-	if err = json.Unmarshal([]byte(response.GetData()), &node); err != nil {
-		return nil, err
+	if cfg.Node.IsServer != "yes" {
+		wcclient, err := getGrpcClient(cfg)
+		if err != nil {
+			return nil, err
+		}
+		// == run client action ==
+		var header metadata.MD
+		ctx, err := auth.SetJWT(wcclient, network)
+		nodeData, err := json.Marshal(&node)
+		if err != nil {
+			return nil, err
+		}
+		response, err := wcclient.ReadNode(
+			ctx,
+			&nodepb.Object{
+				Data: string(nodeData),
+				Type: nodepb.NODE_TYPE,
+			},
+			grpc.Header(&header),
+		)
+		if err != nil {
+			log.Printf("Encountered error checking in node: %v", err)
+		}
+		if err = json.Unmarshal([]byte(response.GetData()), &node); err != nil {
+			return nil, err
+		}
 	}
 	return &node, err
 }
@@ -116,11 +119,11 @@ func RemoveNetwork(network string) error {
 	return err
 }
 */
-func GetPeers(macaddress string, network string, server string, dualstack bool, isIngressGateway bool) ([]wgtypes.PeerConfig, bool, []string, error) {
+
+func GetPeers(macaddress string, network string, server string, dualstack bool, isIngressGateway bool, isServer bool) ([]wgtypes.PeerConfig, bool, []string, error) {
 	hasGateway := false
 	var gateways []string
 	var peers []wgtypes.PeerConfig
-	var wcclient nodepb.NodeServiceClient
 	cfg, err := config.ReadConfig(network)
 	if err != nil {
 		log.Fatalf("Issue retrieving config for network: "+network+". Please investigate: %v", err)
@@ -132,40 +135,48 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
 	if err != nil {
 		log.Fatalf("Issue with format of keepalive value. Please update netconfig: %v", err)
 	}
+	var nodes []models.Node // fill this either from server or client
+	if !isServer {          // set peers client side
+		var wcclient nodepb.NodeServiceClient
+		conn, err := grpc.Dial(cfg.Server.GRPCAddress,
+			ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
 
-	conn, err := grpc.Dial(cfg.Server.GRPCAddress,
-		ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
+		if err != nil {
+			log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
+		}
+		defer conn.Close()
+		// Instantiate the BlogServiceClient with our client connection to the server
+		wcclient = nodepb.NewNodeServiceClient(conn)
 
-	if err != nil {
-		log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
-	}
-	defer conn.Close()
-	// Instantiate the BlogServiceClient with our client connection to the server
-	wcclient = nodepb.NewNodeServiceClient(conn)
+		req := &nodepb.Object{
+			Data: macaddress + "###" + network,
+			Type: nodepb.STRING_TYPE,
+		}
 
-	req := &nodepb.Object{
-		Data: macaddress + "###" + network,
-		Type: nodepb.STRING_TYPE,
-	}
+		ctx, err := auth.SetJWT(wcclient, network)
+		if err != nil {
+			log.Println("Failed to authenticate.")
+			return peers, hasGateway, gateways, err
+		}
+		var header metadata.MD
 
-	ctx, err := auth.SetJWT(wcclient, network)
-	if err != nil {
-		log.Println("Failed to authenticate.")
-		return peers, hasGateway, gateways, err
+		response, err := wcclient.GetPeers(ctx, req, grpc.Header(&header))
+		if err != nil {
+			log.Println("Error retrieving peers")
+			log.Println(err)
+			return nil, hasGateway, gateways, err
+		}
+		if err := json.Unmarshal([]byte(response.GetData()), &nodes); err != nil {
+			log.Println("Error unmarshaling data for peers")
+			return nil, hasGateway, gateways, err
+		}
+	} else { // set peers serverside
+		nodes, err = logic.GetPeers(nodecfg)
+		if err != nil {
+			return nil, hasGateway, gateways, err
+		}
 	}
-	var header metadata.MD
 
-	response, err := wcclient.GetPeers(ctx, req, grpc.Header(&header))
-	if err != nil {
-		log.Println("Error retrieving peers")
-		log.Println(err)
-		return nil, hasGateway, gateways, err
-	}
-	var nodes []models.Node
-	if err := json.Unmarshal([]byte(response.GetData()), &nodes); err != nil {
-		log.Println("Error unmarshaling data for peers")
-		return nil, hasGateway, gateways, err
-	}
 	for _, node := range nodes {
 		pubkey, err := wgtypes.ParseKey(node.PublicKey)
 		if err != nil {
@@ -240,7 +251,7 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
 			}
 			allowedips = append(allowedips, addr6)
 		}
-		if nodecfg.IsServer == "yes" {
+		if nodecfg.IsServer == "yes" && !(node.IsServer == "yes"){
 			peer = wgtypes.PeerConfig{
 				PublicKey:                   pubkey,
 				PersistentKeepaliveInterval: &keepaliveserver,
@@ -283,43 +294,62 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
 }
 func GetExtPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, error) {
 	var peers []wgtypes.PeerConfig
-	var wcclient nodepb.NodeServiceClient
+
 	cfg, err := config.ReadConfig(network)
 	if err != nil {
 		log.Fatalf("Issue retrieving config for network: "+network+". Please investigate: %v", err)
 	}
 	nodecfg := cfg.Node
+	var extPeers []models.Node
+	if nodecfg.IsServer != "yes" { // fill extPeers with client side logic
+		var wcclient nodepb.NodeServiceClient
 
-	conn, err := grpc.Dial(cfg.Server.GRPCAddress,
-		ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
-	if err != nil {
-		log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
-	}
-	defer conn.Close()
-	// Instantiate the BlogServiceClient with our client connection to the server
-	wcclient = nodepb.NewNodeServiceClient(conn)
+		conn, err := grpc.Dial(cfg.Server.GRPCAddress,
+			ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
+		if err != nil {
+			log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
+		}
+		defer conn.Close()
+		// Instantiate the BlogServiceClient with our client connection to the server
+		wcclient = nodepb.NewNodeServiceClient(conn)
 
-	req := &nodepb.Object{
-		Data: macaddress + "###" + network,
-		Type: nodepb.STRING_TYPE,
-	}
+		req := &nodepb.Object{
+			Data: macaddress + "###" + network,
+			Type: nodepb.STRING_TYPE,
+		}
 
-	ctx, err := auth.SetJWT(wcclient, network)
-	if err != nil {
-		log.Println("Failed to authenticate.")
-		return peers, err
-	}
-	var header metadata.MD
+		ctx, err := auth.SetJWT(wcclient, network)
+		if err != nil {
+			log.Println("Failed to authenticate.")
+			return peers, err
+		}
+		var header metadata.MD
 
-	responseObject, err := wcclient.GetExtPeers(ctx, req, grpc.Header(&header))
-	if err != nil {
-		log.Println("Error retrieving peers")
-		log.Println(err)
-		return nil, err
-	}
-	var extPeers []models.Node
-	if err = json.Unmarshal([]byte(responseObject.Data), &extPeers); err != nil {
-		return nil, err
+		responseObject, err := wcclient.GetExtPeers(ctx, req, grpc.Header(&header))
+		if err != nil {
+			log.Println("Error retrieving peers")
+			log.Println(err)
+			return nil, err
+		}
+		if err = json.Unmarshal([]byte(responseObject.Data), &extPeers); err != nil {
+			return nil, err
+		}
+	} else { // fill extPeers with server side logic
+		tempPeers, err := logic.GetExtPeersList(nodecfg.MacAddress, nodecfg.Network)
+		if err != nil {
+			return nil, err
+		}
+		for i := 0; i < len(tempPeers); i++ {
+			extPeers = append(extPeers, models.Node{
+				Address:             tempPeers[i].Address,
+				Address6:            tempPeers[i].Address6,
+				Endpoint:            tempPeers[i].Endpoint,
+				PublicKey:           tempPeers[i].PublicKey,
+				PersistentKeepalive: tempPeers[i].KeepAlive,
+				ListenPort:          tempPeers[i].ListenPort,
+				LocalAddress:        tempPeers[i].LocalAddress,
+			})
+		}
 	}
 	for _, extPeer := range extPeers {
 		pubkey, err := wgtypes.ParseKey(extPeer.PublicKey)

+ 1 - 1
netclient/wireguard/common.go

@@ -267,7 +267,7 @@ func SetWGConfig(network string, peerupdate bool) error {
 	servercfg := cfg.Server
 	nodecfg := cfg.Node
 
-	peers, hasGateway, gateways, err := server.GetPeers(nodecfg.MacAddress, nodecfg.Network, servercfg.GRPCAddress, nodecfg.IsDualStack == "yes", nodecfg.IsIngressGateway == "yes")
+	peers, hasGateway, gateways, err := server.GetPeers(nodecfg.MacAddress, nodecfg.Network, servercfg.GRPCAddress, nodecfg.IsDualStack == "yes", nodecfg.IsIngressGateway == "yes", nodecfg.IsServer == "yes")
 	if err != nil {
 		return err
 	}

+ 37 - 0
relay/relay.go

@@ -0,0 +1,37 @@
+package relay
+
+import (
+	"encoding/json"
+	"errors"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+)
+
+func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
+	collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
+	var relay models.Node
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return relay, nil
+		}
+		functions.PrintUserLog("", err.Error(), 2)
+		return relay, err
+	}
+	for _, value := range collection {
+		err := json.Unmarshal([]byte(value), &relay)
+		if err != nil {
+			functions.PrintUserLog("", err.Error(), 2)
+			continue
+		}
+		if relay.IsRelay == "yes" {
+			for _, addr := range relay.RelayAddrs {
+				if addr == relayedNodeAddr {
+					return relay, nil
+				}
+			}
+		}
+	}
+	return relay, errors.New("could not find relay for node " + relayedNodeAddr)
+}

+ 1 - 1
servercfg/serverconf.go

@@ -74,7 +74,7 @@ func GetAPIConnString() string {
 	return conn
 }
 func GetVersion() string {
-	version := "0.8.1"
+	version := "0.8.3"
 	if config.Config.Server.Version != "" {
 		version = config.Config.Server.Version
 	}

+ 69 - 72
serverctl/serverctl.go

@@ -6,11 +6,11 @@ import (
 	"io"
 	"log"
 	"os"
-	"os/exec"
 
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
+	nccommand "github.com/gravitl/netmaker/netclient/command"
+	"github.com/gravitl/netmaker/netclient/config"
 	"github.com/gravitl/netmaker/netclient/ncutils"
 	"github.com/gravitl/netmaker/servercfg"
 )
@@ -91,18 +91,8 @@ func copy(src, dst string) (int64, error) {
 }
 
 func RemoveNetwork(network string) (bool, error) {
-	netclientPath := ncutils.GetNetclientPath()
-	_, err := os.Stat(netclientPath + "/netclient")
-	if err != nil {
-		log.Println("could not find " + netclientPath + "/netclient")
-		return false, err
-	}
-	_, err = ncutils.RunCmd(netclientPath+"/netclient leave -n "+network, true)
-	if err == nil {
-		log.Println("Server removed from network " + network)
-	}
+	err := nccommand.Leave(config.ClientConfig{Network: network})
 	return true, err
-
 }
 
 func InitServerNetclient() error {
@@ -114,82 +104,89 @@ func InitServerNetclient() error {
 		log.Println("could not find or create", netclientDir)
 		return err
 	}
-	_, err = os.Stat(netclientDir + "/netclient")
-	if os.IsNotExist(err) {
-		err = InstallNetclient()
+	return nil
+}
+
+func HandleContainedClient() error {
+	servernets, err := models.GetNetworks()
+	if err != nil && !database.IsEmptyRecord(err) {
+		return err
+	}
+	if len(servernets) > 0 {
 		if err != nil {
 			return err
 		}
-	}
-	err = os.Chmod(netclientDir+"/netclient", 0755)
-	if err != nil {
-		log.Println("could not change netclient binary permissions")
-		return err
+		log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile))
+		err := SyncNetworks(servernets)
+		if err != nil && servercfg.GetVerbose() >= 1 {
+			log.Printf("[server netclient] error syncing networks %s \n", err)
+		}
+		err = nccommand.CheckIn(config.ClientConfig{Network: "all"})
+		if err != nil && servercfg.GetVerbose() >= 1 {
+			log.Printf("[server netclient] error occurred %s \n", err)
+		}
+		if servercfg.GetVerbose() >= 3 {
+			log.Println("[server netclient]", "completed a checkin call")
+		}
 	}
 	return nil
 }
 
-func HandleContainedClient() error {
-	log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile))
+func SyncNetworks(servernets []models.Network) error {
 
-	netclientPath := ncutils.GetNetclientPath()
-	checkinCMD := exec.Command(netclientPath+"/netclient", "checkin", "-n", "all")
-	if servercfg.GetVerbose() >= 2 {
-		checkinCMD.Stdout = os.Stdout
-	}
-	checkinCMD.Stderr = os.Stderr
-	err := checkinCMD.Start()
+	localnets, err := ncutils.GetSystemNetworks()
 	if err != nil {
-		if servercfg.GetVerbose() >= 2 {
-			log.Println(err)
-		}
+		return err
 	}
-	err = checkinCMD.Wait()
-	if err != nil {
-		if servercfg.GetVerbose() >= 2 {
-			log.Println(err)
+	// check networks to join
+	for _, servernet := range servernets {
+		exists := false
+		for _, localnet := range localnets {
+			if servernet.NetID == localnet {
+				exists = true
+			}
+		}
+		if !exists {
+			success, err := AddNetwork(servernet.NetID)
+			if err != nil || !success {
+				if err == nil {
+					err = errors.New("network add failed for " + servernet.NetID)
+				}
+				log.Printf("[server] error adding network %s during sync %s \n", servernet.NetID, err)
+			}
 		}
 	}
-	if servercfg.GetVerbose() >= 3 {
-		log.Println("[server netclient]", "completed a checkin call")
+	// check networks to leave
+	for _, localnet := range localnets {
+		exists := false
+		for _, servernet := range servernets {
+			if servernet.NetID == localnet {
+				exists = true
+			}
+		}
+		if !exists {
+			success, err := RemoveNetwork(localnet)
+			if err != nil || !success {
+				if err == nil {
+					err = errors.New("network delete failed for " + localnet)
+				}
+				log.Printf("[server] error removing network %s during sync %s \n", localnet, err)
+			}
+		}
 	}
 	return nil
 }
 
 func AddNetwork(network string) (bool, error) {
-	pubip, err := servercfg.GetPublicIP()
-	if err != nil {
-		log.Println("could not get public IP.")
-		return false, err
-	}
-	netclientPath := ncutils.GetNetclientPath()
-
-	token, err := functions.CreateServerToken(network)
-	if err != nil {
-		log.Println("could not create server token for " + network)
-		return false, err
-	}
-
-	functions.PrintUserLog(models.NODE_SERVER_NAME, "executing network join: "+netclientPath+"netclient "+"join "+"-t "+token+" -name "+models.NODE_SERVER_NAME+" -endpoint "+pubip, 0)
-	var joinCMD *exec.Cmd
-	if servercfg.IsClientMode() == "contained" {
-		joinCMD = exec.Command(netclientPath+"/netclient", "join", "-t", token, "-name", models.NODE_SERVER_NAME, "-endpoint", pubip, "-daemon", "off", "-dnson", "no")
-	} else {
-		joinCMD = exec.Command(netclientPath+"/netclient", "join", "-t", token, "-name", models.NODE_SERVER_NAME, "-endpoint", pubip)
-	}
-	joinCMD.Stdout = os.Stdout
-	joinCMD.Stderr = os.Stderr
-	err = joinCMD.Start()
-
-	if err != nil {
-		log.Println(err)
-	}
-	log.Println("Waiting for join command to finish...")
-	err = joinCMD.Wait()
-	if err != nil {
-		log.Printf("Command finished with error: %v", err)
-		return false, err
-	}
+	err := nccommand.Join(config.ClientConfig{
+		Network: network,
+		Daemon:  "off",
+		Node: models.Node{
+			Network:  network,
+			IsServer: "yes",
+			Name:     models.NODE_SERVER_NAME,
+		},
+	}, "")
 	log.Println("Server added to network " + network)
 	return true, err
 }

+ 0 - 30
serverctl/wireguard.go

@@ -1,30 +0,0 @@
-package serverctl
-
-import (
-	"github.com/gravitl/netmaker/functions"
-	"golang.zx2c4.com/wireguard/wgctrl"
-)
-
-func GetPeers(networkName string) (map[string]string, error) {
-	peers := make(map[string]string)
-	network, err := functions.GetParentNetwork(networkName)
-	if err != nil {
-		return peers, err
-	}
-	iface := network.DefaultInterface
-
-	client, err := wgctrl.New()
-	if err != nil {
-		return peers, err
-	}
-	device, err := client.Device(iface)
-	if err != nil {
-		return nil, err
-	}
-	for _, peer := range device.Peers {
-		if functions.IsBase64(peer.PublicKey.String()) && peer.Endpoint != nil && functions.CheckEndpoint(peer.Endpoint.String()) {
-			peers[peer.PublicKey.String()] = peer.Endpoint.String()
-		}
-	}
-	return peers, nil
-}

+ 1 - 1
test/user_test.go.bak

@@ -191,7 +191,7 @@ func TestAuthenticateUser(t *testing.T) {
 			password:      "xxxxxxx",
 			code:          http.StatusBadRequest,
 			tokenExpected: false,
-			errMessage:    "Wrong Password",
+			errMessage:    "Incorrect Credentials",
 		},
 		AuthorizeTestCase{
 			testname:      "Valid User",