Browse Source

resolve merge conflicts

Abhishek Kondur 2 years ago
parent
commit
3d48d005d6

+ 0 - 4
cli/cmd/network/create.go

@@ -41,9 +41,6 @@ var networkCreateCmd = &cobra.Command{
 			if defaultACL {
 				network.DefaultACL = "yes"
 			}
-			if pointToSite {
-				network.IsPointToSite = "yes"
-			}
 			network.DefaultInterface = defaultInterface
 			network.DefaultListenPort = int32(defaultListenPort)
 			network.NodeLimit = int32(nodeLimit)
@@ -69,7 +66,6 @@ func init() {
 	networkCreateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
 	networkCreateCmd.Flags().BoolVar(&localNetwork, "local", false, "Is the network local (LAN) ?")
 	networkCreateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
-	networkCreateCmd.Flags().BoolVar(&pointToSite, "point_to_site", false, "Enforce all clients to have only 1 central peer ?")
 	networkCreateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
 	networkCreateCmd.Flags().StringVar(&defaultPostUp, "post_up", "", "Commands to run after server is up `;` separated")
 	networkCreateCmd.Flags().StringVar(&defaultPostDown, "post_down", "", "Commands to run after server is down `;` separated")

+ 0 - 1
cli/cmd/network/flags.go

@@ -8,7 +8,6 @@ var (
 	udpHolePunch              bool
 	localNetwork              bool
 	defaultACL                bool
-	pointToSite               bool
 	defaultInterface          string
 	defaultListenPort         int
 	nodeLimit                 int

+ 0 - 4
cli/cmd/network/update.go

@@ -44,9 +44,6 @@ var networkUpdateCmd = &cobra.Command{
 			if defaultACL {
 				network.DefaultACL = "yes"
 			}
-			if pointToSite {
-				network.IsPointToSite = "yes"
-			}
 			network.DefaultInterface = defaultInterface
 			network.DefaultListenPort = int32(defaultListenPort)
 			network.NodeLimit = int32(nodeLimit)
@@ -70,7 +67,6 @@ func init() {
 	networkUpdateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
 	networkUpdateCmd.Flags().BoolVar(&localNetwork, "local", false, "Is the network local (LAN) ?")
 	networkUpdateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
-	networkUpdateCmd.Flags().BoolVar(&pointToSite, "point_to_site", false, "Enforce all clients to have only 1 central peer ?")
 	networkUpdateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
 	networkUpdateCmd.Flags().StringVar(&defaultPostUp, "post_up", "", "Commands to run after server is up `;` separated")
 	networkUpdateCmd.Flags().StringVar(&defaultPostDown, "post_down", "", "Commands to run after server is down `;` separated")

+ 4 - 1
controllers/node.go

@@ -524,7 +524,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if !logic.IsVersionComptatible(data.Host.Version) {
-		err := errors.New("incomatible netclient version")
+		err := errors.New("incompatible netclient version")
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
@@ -600,6 +600,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 				return
 			}
+			data.Host = *host
 		} else {
 			logger.Log(0, "error creating host", err.Error())
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -652,6 +653,8 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
+	data.Host.HostPass = "" // client should not change password after join
+	// concealing hash
 	response := models.NodeJoinResponse{
 		Node:         data.Node,
 		ServerConfig: server,

+ 5 - 3
go.mod

@@ -20,7 +20,7 @@ require (
 	golang.org/x/oauth2 v0.3.0
 	golang.org/x/sys v0.3.0 // indirect
 	golang.org/x/text v0.5.0 // indirect
-	golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c
+	golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1
@@ -29,7 +29,7 @@ require (
 require (
 	filippo.io/edwards25519 v1.0.0
 	github.com/c-robinson/iplib v1.0.6
-	github.com/go-ping/ping v1.1.0 // indirect
+	github.com/go-ping/ping v1.1.0
 	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
 )
 
@@ -42,8 +42,8 @@ require (
 )
 
 require (
-	github.com/gravitl/netclient v0.0.0-20230114051017-65ecaeffca09
 	github.com/guumaster/tablewriter v0.0.10
+	github.com/kr/pretty v0.3.1
 	github.com/matryer/is v1.4.0
 	github.com/olekukonko/tablewriter v0.0.5
 	github.com/spf13/cobra v1.6.1
@@ -53,7 +53,9 @@ require (
 	cloud.google.com/go/compute/metadata v0.2.1 // indirect
 	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
 	github.com/inconshreveable/mousetrap v1.0.1 // indirect
+	github.com/kr/text v0.2.0 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
+	github.com/rogpeppe/go-internal v1.9.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 )
 

+ 2 - 2
go.sum

@@ -62,8 +62,6 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gravitl/netclient v0.0.0-20230114051017-65ecaeffca09 h1:T0gLl+i8whnrdwtW91R4u8x8bmqFVfPTU9WfBratkMc=
-github.com/gravitl/netclient v0.0.0-20230114051017-65ecaeffca09/go.mod h1:g3q+vhLySW/6smOsWsVy5LrxoW++f+kqiBAp9BM6sbY=
 github.com/guumaster/tablewriter v0.0.10 h1:A0HD94yMdt4usgxBjoEceNeE0XMJ027euoHAzsPqBQs=
 github.com/guumaster/tablewriter v0.0.10/go.mod h1:p4FRFhyfo0UD9ZLmMRbbJooTUsxo6b80qZTERVDWrH8=
 github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
@@ -78,6 +76,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -121,6 +120,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f h1:BSnJgAfHzEp7o8PYJ7YfwAVHhqu7BYUTggcn/LGlUWY=
 github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f/go.mod h1:UW/gxgQwSePTvL1KA8QEHsXeYHP4xkoXgbDdN781p34=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=

+ 1 - 1
logic/metrics/metrics.go

@@ -3,9 +3,9 @@ package metrics
 import (
 	"time"
 
-	proxy_metrics "github.com/gravitl/netclient/nmproxy/metrics"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
+	proxy_metrics "github.com/gravitl/netmaker/metrics"
 	"github.com/gravitl/netmaker/models"
 	"golang.zx2c4.com/wireguard/wgctrl"
 )

+ 0 - 26
logic/nodes.go

@@ -239,7 +239,6 @@ func SetNodeDefaults(node *models.Node) {
 	if err == nil {
 		node.NetworkRange6 = *cidr
 	}
-	node.ExpirationDateTime = time.Now().Add(models.TEN_YEARS_IN_SECONDS)
 
 	if node.DefaultACL == "" {
 		node.DefaultACL = parentNetwork.DefaultACL
@@ -260,7 +259,6 @@ func SetNodeDefaults(node *models.Node) {
 	node.SetLastCheckIn()
 	node.SetDefaultConnected()
 	node.SetExpirationDateTime()
-
 }
 
 // GetRecordKey - get record key
@@ -272,30 +270,6 @@ func GetRecordKey(id string, network string) (string, error) {
 	return id + "###" + network, nil
 }
 
-// GetNodeByMacAddress - gets a node by mac address
-func GetNodeByMacAddress(network string, macaddress string) (models.Node, error) {
-
-	var node models.Node
-
-	key, err := GetRecordKey(macaddress, network)
-	if err != nil {
-		return node, err
-	}
-
-	record, err := database.FetchRecord(database.NODES_TABLE_NAME, key)
-	if err != nil {
-		return models.Node{}, err
-	}
-
-	if err = json.Unmarshal([]byte(record), &node); err != nil {
-		return models.Node{}, err
-	}
-
-	SetNodeDefaults(&node)
-
-	return node, nil
-}
-
 // GetNodesByAddress - gets a node by mac address
 func GetNodesByAddress(network string, addresses []string) ([]models.Node, error) {
 	var nodes []models.Node

+ 20 - 21
logic/peers.go

@@ -11,7 +11,6 @@ import (
 	"strings"
 	"time"
 
-	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
@@ -29,10 +28,10 @@ import (
 // TODO ==========================
 // TODO ==========================
 // revisit this logic with new host/node models.
-func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyManagerPayload, error) {
-	proxyPayload := proxy_models.ProxyManagerPayload{}
+func GetPeersForProxy(node *models.Node, onlyPeers bool) (models.ProxyManagerPayload, error) {
+	proxyPayload := models.ProxyManagerPayload{}
 	var peers []wgtypes.PeerConfig
-	peerConfMap := make(map[string]proxy_models.PeerConf)
+	peerConfMap := make(map[string]models.PeerConf)
 	var err error
 	currentPeers, err := GetNetworkNodes(node.Network)
 	if err != nil {
@@ -71,7 +70,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
 				logger.Log(1, "failed to relayed nodes: ", node.ID.String(), err.Error())
 				proxyPayload.IsRelay = false
 			} else {
-				relayPeersMap := make(map[string]proxy_models.RelayedConf)
+				relayPeersMap := make(map[string]models.RelayedConf)
 				for _, relayedNode := range relayedNodes {
 					relayedNode := relayedNode
 					payload, err := GetPeersForProxy(&relayedNode, true)
@@ -82,7 +81,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
 						}
 						relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, host.ListenPort))
 						if udpErr == nil {
-							relayPeersMap[host.PublicKey.String()] = proxy_models.RelayedConf{
+							relayPeersMap[host.PublicKey.String()] = models.RelayedConf{
 								RelayedPeerEndpoint: relayedEndpoint,
 								RelayedPeerPubKey:   relayedHost.PublicKey.String(),
 								Peers:               payload.Peers,
@@ -112,7 +111,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
 		if proxyStatus {
 			listenPort = host.ProxyListenPort
 			if listenPort == 0 {
-				listenPort = proxy_models.NmProxyPort
+				listenPort = models.NmProxyPort
 			}
 		} else if listenPort == 0 {
 			listenPort = host.ListenPort
@@ -137,7 +136,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
 			PersistentKeepaliveInterval: &keepalive,
 			ReplaceAllowedIPs:           true,
 		})
-		peerConfMap[host.PublicKey.String()] = proxy_models.PeerConf{
+		peerConfMap[host.PublicKey.String()] = models.PeerConf{
 			Address:          net.ParseIP(peer.PrimaryAddress()),
 			Proxy:            proxyStatus,
 			PublicListenPort: int32(listenPort),
@@ -153,7 +152,7 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
 				}
 				relayTo, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, relayHost.ListenPort))
 				if err == nil {
-					peerConfMap[host.PublicKey.String()] = proxy_models.PeerConf{
+					peerConfMap[host.PublicKey.String()] = models.PeerConf{
 
 						IsRelayed:        true,
 						RelayedTo:        relayTo,
@@ -194,11 +193,11 @@ func GetPeersForProxy(node *models.Node, onlyPeers bool) (proxy_models.ProxyMana
 }
 
 // GetProxyUpdateForHost - gets the proxy update for host
-func GetProxyUpdateForHost(host *models.Host) (proxy_models.ProxyManagerPayload, error) {
-	proxyPayload := proxy_models.ProxyManagerPayload{
-		Action: proxy_models.ProxyUpdate,
+func GetProxyUpdateForHost(host *models.Host) (models.ProxyManagerPayload, error) {
+	proxyPayload := models.ProxyManagerPayload{
+		Action: models.ProxyUpdate,
 	}
-	peerConfMap := make(map[string]proxy_models.PeerConf)
+	peerConfMap := make(map[string]models.PeerConf)
 	if host.IsRelayed {
 		relayHost, err := GetHost(host.RelayedBy)
 		if err == nil {
@@ -215,14 +214,14 @@ func GetProxyUpdateForHost(host *models.Host) (proxy_models.ProxyManagerPayload,
 	}
 	if host.IsRelay {
 		relayedHosts := GetRelayedHosts(host)
-		relayPeersMap := make(map[string]proxy_models.RelayedConf)
+		relayPeersMap := make(map[string]models.RelayedConf)
 		for _, relayedHost := range relayedHosts {
 			relayedHost := relayedHost
 			payload, err := GetPeerUpdateForHost(&relayedHost)
 			if err == nil {
 				relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, getPeerListenPort(&relayedHost)))
 				if udpErr == nil {
-					relayPeersMap[relayedHost.PublicKey.String()] = proxy_models.RelayedConf{
+					relayPeersMap[relayedHost.PublicKey.String()] = models.RelayedConf{
 						RelayedPeerEndpoint: relayedEndpoint,
 						RelayedPeerPubKey:   relayedHost.PublicKey.String(),
 						Peers:               payload.Peers,
@@ -255,10 +254,10 @@ func GetProxyUpdateForHost(host *models.Host) (proxy_models.ProxyManagerPayload,
 			if err != nil {
 				continue
 			}
-			var currPeerConf proxy_models.PeerConf
+			var currPeerConf models.PeerConf
 			var found bool
 			if currPeerConf, found = peerConfMap[peerHost.PublicKey.String()]; !found {
-				currPeerConf = proxy_models.PeerConf{
+				currPeerConf = models.PeerConf{
 					Proxy:            peerHost.ProxyEnabled,
 					PublicListenPort: int32(getPeerListenPort(peerHost)),
 				}
@@ -776,7 +775,7 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 		}
 
 		if host.PublicKey.String() == extPeer.PublicKey ||
-			extPeer.IngressGatewayID != node.ID.String() {
+			extPeer.IngressGatewayID != node.ID.String() || !extPeer.Enabled {
 			continue
 		}
 
@@ -822,7 +821,7 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e
 
 }
 
-func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]proxy_models.PeerConf) ([]wgtypes.PeerConfig, map[string]proxy_models.PeerConf, error) {
+func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]models.PeerConf) ([]wgtypes.PeerConfig, map[string]models.PeerConf, error) {
 	var peers []wgtypes.PeerConfig
 	host, err := GetHost(node.HostID.String())
 	if err != nil {
@@ -841,7 +840,7 @@ func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]proxy_model
 		}
 
 		if host.PublicKey.String() == extPeer.PublicKey ||
-			extPeer.IngressGatewayID != node.ID.String() {
+			extPeer.IngressGatewayID != node.ID.String() || !extPeer.Enabled {
 			continue
 		}
 
@@ -872,7 +871,7 @@ func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]proxy_model
 			ReplaceAllowedIPs: true,
 			AllowedIPs:        allowedips,
 		}
-		extConf := proxy_models.PeerConf{
+		extConf := models.PeerConf{
 			IsExtClient: true,
 			Address:     net.ParseIP(extPeer.Address),
 		}

+ 111 - 0
metrics/metrics.go

@@ -0,0 +1,111 @@
+package metrics
+
+import (
+	"fmt"
+	"sync"
+	"time"
+
+	"github.com/go-ping/ping"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/models"
+)
+
+// lock for metrics map
+var metricsMapLock = &sync.RWMutex{}
+
+// metrics data map
+var metricsPeerMap = make(map[string]map[string]*models.ProxyMetric)
+
+// GetMetricByServer - get metric data of peers by server
+func GetMetricByServer(server string) map[string]*models.ProxyMetric {
+	metricsMapLock.RLock()
+	defer metricsMapLock.RUnlock()
+	if _, ok := metricsPeerMap[server]; !ok {
+		return nil
+	}
+	return metricsPeerMap[server]
+}
+
+// GetMetric - fetches the metric data for the peer
+func GetMetric(server, peerKey string) models.ProxyMetric {
+	metric := models.ProxyMetric{}
+	peerMetricMap := GetMetricByServer(server)
+	metricsMapLock.RLock()
+	defer metricsMapLock.RUnlock()
+	if peerMetricMap == nil {
+		return metric
+	}
+	if m, ok := peerMetricMap[peerKey]; ok && m != nil {
+		metric = *m
+	}
+	return metric
+}
+
+// UpdateMetric - updates metric data for the peer
+func UpdateMetric(server, peerKey string, metric *models.ProxyMetric) {
+	metricsMapLock.Lock()
+	defer metricsMapLock.Unlock()
+	if metricsPeerMap[server] == nil {
+		metricsPeerMap[server] = make(map[string]*models.ProxyMetric)
+	}
+	metricsPeerMap[server][peerKey] = metric
+}
+
+// UpdateMetricByPeer - updates metrics data by peer public key
+func UpdateMetricByPeer(peerKey string, metric *models.ProxyMetric, onlyTraffic bool) {
+	metricsMapLock.Lock()
+	defer metricsMapLock.Unlock()
+	for server, peerKeyMap := range metricsPeerMap {
+		if peerMetric, ok := peerKeyMap[peerKey]; ok {
+			peerMetric.TrafficRecieved += metric.TrafficRecieved
+			peerMetric.TrafficSent += metric.TrafficSent
+			if !onlyTraffic {
+				peerMetric.LastRecordedLatency = metric.LastRecordedLatency
+			}
+
+			metricsPeerMap[server][peerKey] = peerMetric
+		}
+	}
+}
+
+// ResetMetricsForPeer - reset metrics for peer
+func ResetMetricsForPeer(server, peerKey string) {
+	metricsMapLock.Lock()
+	defer metricsMapLock.Unlock()
+	delete(metricsPeerMap[server], peerKey)
+}
+
+// ResetMetricForNode - resets node level metrics
+func ResetMetricForNode(server, peerKey, peerID string) {
+	metric := GetMetric(server, peerKey)
+	delete(metric.NodeConnectionStatus, peerID)
+	UpdateMetric(server, peerKey, &metric)
+}
+
+// MetricCollectionInterval - collection interval for metrics
+const MetricCollectionInterval = time.Second * 25
+
+// PeerConnectionStatus - get peer connection status by pinging
+func PeerConnectionStatus(address string) (connected bool) {
+	fmt.Println("PINGER ADDR: ", address)
+	pinger, err := ping.NewPinger(address)
+	if err != nil {
+		logger.Log(0, "could not initiliaze ping peer address", address, err.Error())
+		connected = false
+	} else {
+		pinger.Timeout = time.Second * 2
+		err = pinger.Run()
+		if err != nil {
+			logger.Log(0, "failed to ping on peer address", address, err.Error())
+			return false
+		} else {
+			pingStats := pinger.Statistics()
+			if pingStats.PacketsRecv > 0 {
+				connected = true
+				return
+			}
+		}
+	}
+
+	return
+}

+ 16 - 17
models/mqtt.go

@@ -3,32 +3,31 @@ package models
 import (
 	"net"
 
-	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 
 // PeerUpdate - struct
 type PeerUpdate struct {
-	Network       string                           `json:"network" bson:"network" yaml:"network"`
-	ServerVersion string                           `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
-	ServerAddrs   []ServerAddr                     `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
-	Peers         []wgtypes.PeerConfig             `json:"peers" bson:"peers" yaml:"peers"`
-	DNS           string                           `json:"dns" bson:"dns" yaml:"dns"`
-	PeerIDs       PeerMap                          `json:"peerids" bson:"peerids" yaml:"peerids"`
-	ProxyUpdate   proxy_models.ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
+	Network       string               `json:"network" bson:"network" yaml:"network"`
+	ServerVersion string               `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
+	ServerAddrs   []ServerAddr         `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
+	Peers         []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
+	DNS           string               `json:"dns" bson:"dns" yaml:"dns"`
+	PeerIDs       PeerMap              `json:"peerids" bson:"peerids" yaml:"peerids"`
+	ProxyUpdate   ProxyManagerPayload  `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
 }
 
 // HostPeerUpdate - struct for host peer updates
 type HostPeerUpdate struct {
-	Host          Host                             `json:"host" bson:"host" yaml:"host"`
-	Server        string                           `json:"server" bson:"server" yaml:"server"`
-	ServerVersion string                           `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
-	ServerAddrs   []ServerAddr                     `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
-	Network       map[string]NetworkInfo           `json:"network" bson:"network" yaml:"network"`
-	Peers         []wgtypes.PeerConfig             `json:"peers" bson:"peers" yaml:"peers"`
-	PeerIDs       HostPeerMap                      `json:"peerids" bson:"peerids" yaml:"peerids"`
-	ProxyUpdate   proxy_models.ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
-	IngressInfo   IngressInfo                      `json:"ingress_info" bson:"ext_peers" yaml:"ext_peers"`
+	Host          Host                   `json:"host" bson:"host" yaml:"host"`
+	Server        string                 `json:"server" bson:"server" yaml:"server"`
+	ServerVersion string                 `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
+	ServerAddrs   []ServerAddr           `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
+	Network       map[string]NetworkInfo `json:"network" bson:"network" yaml:"network"`
+	Peers         []wgtypes.PeerConfig   `json:"peers" bson:"peers" yaml:"peers"`
+	PeerIDs       HostPeerMap            `json:"peerids" bson:"peerids" yaml:"peerids"`
+	ProxyUpdate   ProxyManagerPayload    `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"`
+	IngressInfo   IngressInfo            `json:"ingress_info" bson:"ext_peers" yaml:"ext_peers"`
 }
 
 // IngressInfo - struct for ingress info

+ 0 - 4
models/network.go

@@ -25,7 +25,6 @@ type Network struct {
 	IsLocal             string                `json:"islocal" bson:"islocal" validate:"checkyesorno"`
 	IsIPv4              string                `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
 	IsIPv6              string                `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
-	IsPointToSite       string                `json:"ispointtosite" bson:"ispointtosite" validate:"checkyesorno"`
 	DefaultUDPHolePunch string                `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
 	DefaultExtClientDNS string                `json:"defaultextclientdns" bson:"defaultextclientdns"`
 	DefaultMTU          int32                 `json:"defaultmtu" bson:"defaultmtu"`
@@ -56,9 +55,6 @@ func (network *Network) SetDefaults() {
 	if network.IsLocal == "" {
 		network.IsLocal = "no"
 	}
-	if network.IsPointToSite == "" {
-		network.IsPointToSite = "no"
-	}
 	if network.DefaultInterface == "" {
 		if len(network.NetID) < 13 {
 			network.DefaultInterface = "nm-" + network.NetID

+ 1 - 1
models/node.go

@@ -15,7 +15,7 @@ const (
 	// NODE_SERVER_NAME - the default server name
 	NODE_SERVER_NAME = "netmaker"
 	// TEN_YEARS_IN_SECONDS - ten years in seconds
-	TEN_YEARS_IN_SECONDS = 300000000
+	TEN_YEARS_IN_SECONDS = 315670000000000000
 	// MAX_NAME_LENGTH - max name length of node
 	MAX_NAME_LENGTH = 62
 	// == ACTIONS == (can only be set by server)

+ 68 - 0
models/proxy.go

@@ -0,0 +1,68 @@
+package models
+
+import (
+	"net"
+	"time"
+
+	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+)
+
+// ProxyAction - type for proxy action
+type ProxyAction string
+
+const (
+	// default proxy port
+	NmProxyPort = 51722
+	// PersistentKeepaliveInterval - default keepalive for wg peer
+	DefaultPersistentKeepaliveInterval = time.Duration(time.Second * 20)
+
+	// ProxyUpdate - constant for proxy update action
+	ProxyUpdate ProxyAction = "PROXY_UPDATE"
+	// ProxyDeletePeers - constant for proxy delete peers action
+	ProxyDeletePeers ProxyAction = "PROXY_DELETE"
+	// ProxyDeleteAllPeers - constant for proxy delete all peers action
+	ProxyDeleteAllPeers ProxyAction = "PROXY_DELETE_ALL"
+	// NoProxy - constant for no ProxyAction
+	NoProxy ProxyAction = "NO_PROXY"
+)
+
+// RelayedConf - struct relayed peers config
+type RelayedConf struct {
+	RelayedPeerEndpoint *net.UDPAddr         `json:"relayed_peer_endpoint"`
+	RelayedPeerPubKey   string               `json:"relayed_peer_pub_key"`
+	Peers               []wgtypes.PeerConfig `json:"relayed_peers"`
+}
+
+// PeerConf - struct for peer config in the network
+type PeerConf struct {
+	Proxy            bool         `json:"proxy"`
+	PublicListenPort int32        `json:"public_listen_port"`
+	IsExtClient      bool         `json:"is_ext_client"`
+	Address          net.IP       `json:"address"`
+	ExtInternalIp    net.IP       `json:"ext_internal_ip"`
+	IsRelayed        bool         `json:"is_relayed"`
+	RelayedTo        *net.UDPAddr `json:"relayed_to"`
+}
+
+// ProxyManagerPayload - struct for proxy manager payload
+type ProxyManagerPayload struct {
+	Action        ProxyAction `json:"action"`
+	InterfaceName string      `json:"interface_name"`
+	Server        string      `json:"server"`
+	//WgAddr          string                 `json:"wg_addr"`
+	Peers           []wgtypes.PeerConfig   `json:"peers"`
+	PeerMap         map[string]PeerConf    `json:"peer_map"`
+	IsIngress       bool                   `json:"is_ingress"`
+	IsRelayed       bool                   `json:"is_relayed"`
+	RelayedTo       *net.UDPAddr           `json:"relayed_to"`
+	IsRelay         bool                   `json:"is_relay"`
+	RelayedPeerConf map[string]RelayedConf `json:"relayed_conf"`
+}
+
+// Metric - struct for metric data
+type ProxyMetric struct {
+	NodeConnectionStatus map[string]bool `json:"node_connection_status"`
+	LastRecordedLatency  uint64          `json:"last_recorded_latency"`
+	TrafficSent          int64           `json:"traffic_sent"`     // stored in MB
+	TrafficRecieved      int64           `json:"traffic_recieved"` // stored in MB
+}

+ 1 - 2
mq/publishers.go

@@ -6,7 +6,6 @@ import (
 	"fmt"
 	"time"
 
-	proxy_models "github.com/gravitl/netclient/nmproxy/models"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
@@ -46,7 +45,7 @@ func PublishSingleHostUpdate(host *models.Host) error {
 		if err != nil {
 			return err
 		}
-		proxyUpdate.Action = proxy_models.ProxyUpdate
+		proxyUpdate.Action = models.ProxyUpdate
 		peerUpdate.ProxyUpdate = proxyUpdate
 	}
 

+ 509 - 0
scripts/nm-upgrade.sh

@@ -0,0 +1,509 @@
+#!/bin/bash
+
+# check_version - make sure current version is 0.17.1 before continuing
+check_version() {
+  IMG_TAG=$(yq -r '.services.netmaker.image' docker-compose.yml)
+
+  if [[ "$IMG_TAG" == *"v0.17.1"* ]]; then
+      echo "version is $IMG_TAG"
+  else
+      echo "error, current version is $IMG_TAG"
+      echo "please upgrade to v0.17.1 in order to use the upgrade script"
+      exit 1
+  fi
+}
+
+# wait_seconds - wait a number of seconds, print a log
+wait_seconds() {
+  for ((a=1; a <= $1; a++))
+  do
+    echo ". . ."
+    sleep 1
+  done
+}
+
+# confirm - confirm a choice, or exit script
+confirm() {
+  while true; do
+      read -p 'Does everything look right? [y/n]: ' yn
+      case $yn in
+          [Yy]* ) override="true"; break;;
+          [Nn]* ) echo "exiting..."; exit 1;;
+          * ) echo "Please answer yes or no.";;
+      esac
+  done
+}
+
+# install_dependencies - install system dependencies necessary for script to run
+install_dependencies() {
+  OS=$(uname)
+  is_ubuntu=$(sudo cat /etc/lsb-release | grep "Ubuntu")
+  if [ "${is_ubuntu}" != "" ]; then
+    dependencies="yq jq wireguard jq docker.io docker-compose"
+    update_cmd='apt update'
+    install_cmd='snap install'
+  elif [ -f /etc/debian_version ]; then
+    dependencies="yq jq wireguard jq docker.io docker-compose"
+    update_cmd='apt update'
+    install_cmd='apt install -y'
+  elif [ -f /etc/centos-release ]; then
+    dependencies="wireguard jq docker.io docker-compose"
+    update_cmd='yum update'
+    install_cmd='yum install -y'
+  elif [ -f /etc/fedora-release ]; then
+    dependencies="wireguard jq docker.io docker-compose"
+    update_cmd='dnf update'
+    install_cmd='dnf install -y'
+  elif [ -f /etc/redhat-release ]; then
+    dependencies="wireguard jq docker.io docker-compose"
+    update_cmd='yum update'
+    install_cmd='yum install -y'
+  elif [ -f /etc/arch-release ]; then
+        dependecies="wireguard-tools jq docker.io docker-compose netclient"
+    update_cmd='pacman -Sy'
+    install_cmd='pacman -S --noconfirm'
+  else
+    echo "OS not supported for automatic install"
+      exit 1
+  fi
+
+  set -- $dependencies
+
+  ${update_cmd}
+
+  set +e
+  while [ -n "$1" ]; do
+      is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
+      if [ "${is_installed}" != "" ]; then
+          echo "    " $1 is installed
+      else
+          echo "    " $1 is not installed. Attempting install.
+          ${install_cmd} $1
+          sleep 5
+          if [ "${OS}" = "OpenWRT" ] || [ "${OS}" = "TurrisOS" ]; then
+              is_installed=$(opkg list-installed $1 | grep $1)
+          else
+              is_installed=$(dpkg-query -W --showformat='${Status}\n' $1 | grep "install ok installed")
+          fi
+          if [ "${is_installed}" != "" ]; then
+              echo "    " $1 is installed
+          elif [ -x "$(command -v $1)" ]; then
+              echo "  " $1 is installed
+          else
+              echo "  " FAILED TO INSTALL $1
+              echo "  " This may break functionality.
+          fi
+      fi
+    shift
+  done
+  set -e
+  
+  echo "-----------------------------------------------------"
+  echo "dependency install complete"
+  echo "-----------------------------------------------------"
+}
+
+# collect_server_settings - retrieve server settings from existing compose file
+collect_server_settings() {
+  MASTER_KEY=$(yq -r .services.netmaker.environment.MASTER_KEY docker-compose.yml)
+  echo "-----------------------------------------------------"
+  echo "Is $MASTER_KEY the correct master key for your Netmaker installation?"
+  echo "-----------------------------------------------------"
+  select mkey_option in "yes" "no (enter manually)"; do
+    case $REPLY in
+      1)
+        echo "using $MASTER_KEY for master key"
+      break
+        ;;      
+      2)
+        read -p "Enter Master Key: " mkey
+        MASTER_KEY=$mkey
+        echo "using $MASTER_KEY"
+        break
+        ;;
+      *) echo "invalid option $REPLY, choose 1 or 2";;
+    esac
+  done
+
+  SERVER_HTTP_HOST=$(yq -r .services.netmaker.environment.SERVER_HTTP_HOST docker-compose.yml)
+  echo "-----------------------------------------------------"
+  echo "Is $SERVER_HTTP_HOST the correct api endpoint for your Netmaker installation?"
+  echo "-----------------------------------------------------"
+  select endpoint_option in "yes" "no (enter manually)"; do
+    case $REPLY in
+      1)
+        echo "using $SERVER_HTTP_HOST for api endpoint"
+      break
+        ;;      
+      2)
+        read -p "Enter API Endpoint: " endpoint
+        SERVER_HTTP_HOST=$endpoint
+        echo "using $SERVER_HTTP_HOST"
+        break
+        ;;
+      *) echo "invalid option $REPLY";;
+    esac
+  done
+
+  BROKER_NAME=$(yq -r .services.netmaker.environment.SERVER_NAME docker-compose.yml)
+  echo "-----------------------------------------------------"
+  echo "Is $BROKER_NAME the correct domain for your MQ broker?"
+  echo "-----------------------------------------------------"
+  select broker_option in "yes" "no (enter manually)"; do
+    case $REPLY in
+      1)
+        echo "using $BROKER_NAME for endpoint"
+      break
+        ;;      
+      2)
+        read -p "Enter Broker Domain: " broker
+        BROKER_NAME=$broker
+        echo "using $BROKER_NAME"
+        break
+        ;;
+      *) echo "invalid option $REPLY";;
+    esac
+  done
+
+  SERVER_NAME=${BROKER_NAME#"broker."}
+  echo "-----------------------------------------------------"
+  echo "Is $SERVER_NAME the correct base domain for your installation?"
+  echo "-----------------------------------------------------"
+  select domain_option in "yes" "no (enter manually)"; do
+    case $REPLY in
+      1)
+        echo "using $SERVER_NAME for domain"
+      break
+        ;;      
+      2)
+        read -p "Enter Server Domain: " broker
+        SERVER_NAME=$server
+        echo "using $SERVER_NAME"
+        break
+        ;;
+      *) echo "invalid option $REPLY";;
+    esac
+  done
+
+  STUN_NAME="stun.$SERVER_NAME"
+  echo "-----------------------------------------------------"
+  echo "Netmaker v0.18.0 requires a new DNS entry for $STUN_NAME."
+  echo "Please confirm this is added to your DNS provider before continuing"
+  echo "(note: this is not required if using an nip.io address)"
+  echo "-----------------------------------------------------"
+  confirm
+}
+
+# collect_node_settings - get existing server node configuration
+collect_node_settings() {
+  curl -s -H "Authorization: Bearer $MASTER_KEY" -H 'Content-Type: application/json' https://$SERVER_HTTP_HOST/api/nodes | jq -c '[ .[] | select(.isserver=="yes") ]' > nodejson.tmp
+  NODE_LEN=$(jq length nodejson.tmp)
+  HAS_INGRESS="no"
+  if [ "$NODE_LEN" -gt 0 ]; then
+      echo "===SERVER NODES==="
+      for i in $(seq 1 $NODE_LEN); do
+          NUM=$(($i-1))
+          echo "  SERVER NODE $NUM:"
+          echo "    network: $(jq -r ".[$NUM].network" ./nodejson.tmp)"
+          echo "      name: $(jq -r ".[$NUM].name" ./nodejson.tmp)"
+          echo "      private ipv4: $(jq -r ".[$NUM].address" ./nodejson.tmp)"
+          echo "      private ipv6: $(jq -r ".[$NUM].address6" ./nodejson.tmp)"
+          echo "      is egress: $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp)"
+          if [[ $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp) == "yes" ]]; then
+              echo "          egress range: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp)"
+          fi
+          echo "      is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
+          if [[ $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp) == "yes" ]]; then
+              HAS_INGRESS="yes"
+          fi
+          echo "      is relay: $(jq -r ".[$NUM].isrelay" ./nodejson.tmp)"
+          if [[ $(jq -r ".[$NUM].isrelay" ./nodejson.tmp) == "yes" ]]; then
+              HAS_RELAY="yes"
+              echo "          relay addrs: $(jq -r ".[$NUM].relayaddrs" ./nodejson.tmp | tr -d '[]\n"[:space:]')"
+          fi
+          echo "      is failover: $(jq -r ".[$NUM].failover" ./nodejson.tmp)"
+          echo "  ------------"
+      done
+      echo "=================="
+  else
+      echo "no nodes to parse"
+  fi
+
+  echo "Please confirm that the above output matches the server nodes in your Netmaker server."
+  confirm
+
+  if [[ $HAS_INGRESS == "yes" ]]; then
+      echo "WARNING: Your server contains an Ingress Gateway. After upgrading, existing Ext Clients will be lost and must be recreated. Please confirm that you would like to continue."
+      confirm
+  fi
+}
+
+# set_compose - set compose file with proper values
+set_compose() {
+
+  # DEV_TEMP - Temporary instructions for testing
+  sed -i "s/v0.17.1/testing/g" /root/docker-compose.yml
+
+  # RELEASE_REPLACE - Use this once release is ready
+  #sed -i "s/v0.17.1/v0.18.0/g" /root/docker-compose.yml
+  yq ".services.netmaker.environment.SERVER_NAME = \"$SERVER_NAME\"" -i /root/docker-compose.yml
+  yq ".services.netmaker.environment += {\"BROKER_NAME\": \"$BROKER_NAME\"}" -i /root/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"STUN_NAME\": \"$STUN_NAME\"}" -i /root/docker-compose.yml  
+  yq ".services.netmaker.environment += {\"STUN_PORT\": \"3478\"}" -i /root/docker-compose.yml  
+  yq ".services.netmaker.ports += \"3478:3478/udp\"" -i /root/docker-compose.yml
+}
+
+# start_containers - run docker-compose up -d
+start_containers() {
+  docker-compose -f /root/docker-compose.yml up -d
+}
+
+# test_caddy - make sure caddy is working
+test_caddy() {
+  echo "Testing Caddy setup (please be patient, this may take 1-2 minutes)"
+  for i in 1 2 3 4 5 6 7 8
+  do
+  curlresponse=$(curl -vIs https://${SERVER_HTTP_HOST} 2>&1)
+
+  if [[ "$i" == 8 ]]; then
+    echo "    Caddy is having an issue setting up certificates, please investigate (docker logs caddy)"
+    echo "    Exiting..."
+    exit 1
+  elif [[ "$curlresponse" == *"failed to verify the legitimacy of the server"* ]]; then
+    echo "    Certificates not yet configured, retrying..."
+
+  elif [[ "$curlresponse" == *"left intact"* ]]; then
+    echo "    Certificates ok"
+    break
+  else
+    secs=$(($i*5+10))
+    echo "    Issue establishing connection...retrying in $secs seconds..."       
+  fi
+  sleep $secs
+  done
+}
+
+# setup_netclient - installs netclient locally
+setup_netclient() {
+
+# DEV_TEMP - Temporary instructions for testing
+wget https://fileserver.netmaker.org/testing/netclient
+chmod +x netclient
+./netclient install
+
+# RELEASE_REPLACE - Use this once release is ready
+# if [ -f /etc/debian_version ]; then
+#     curl -sL 'https://apt.netmaker.org/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/netclient.asc
+#     curl -sL 'https://apt.netmaker.org/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/netclient.list
+#     sudo apt update
+#     sudo apt install netclient
+# elif [ -f /etc/centos-release ]; then
+#     curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
+#     curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
+#     sudo rpm --import /tmp/gpg.key
+#     sudo dnf check-update
+#     sudo dnf install netclient
+# elif [ -f /etc/fedora-release ]; then
+#     curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
+#     curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
+#     sudo rpm --import /tmp/gpg.key
+#     sudo dnf check-update
+#     sudo dnf install netclient
+# elif [ -f /etc/redhat-release ]; then
+#     curl -sL 'https://rpm.netmaker.org/gpg.key' | sudo tee /tmp/gpg.key
+#     curl -sL 'https://rpm.netmaker.org/netclient-repo' | sudo tee /etc/yum.repos.d/netclient.repo
+#     sudo rpm --import /tmp/gpg.key
+#     sudo dnf check-update(
+#     sudo dnf install netclient
+# elif [ -f /etc/arch-release ]; then
+#     yay -S netclient
+# else
+# 	echo "OS not supported for automatic install"
+#     exit 1
+# fi
+
+# if [ -z "${install_cmd}" ]; then
+#         echo "OS unsupported for automatic dependency install"
+# 	exit 1
+# fi
+}
+
+# setup_nmctl - pulls nmctl and makes it executable
+setup_nmctl() {
+
+  # DEV_TEMP - Temporary instructions for testing
+  wget https://fileserver.netmaker.org/testing/nmctl
+ 
+  # RELEASE_REPLACE - Use this once release is ready
+  # wget https://github.com/gravitl/netmaker/releases/download/v0.17.1/nmctl
+    chmod +x nmctl
+    echo "using server $SERVER_HTTP_HOST"
+    echo "using master key $MASTER_KEY"
+    ./nmctl context set default --endpoint="https://$SERVER_HTTP_HOST" --master_key="$MASTER_KEY"
+    ./nmctl context use default
+    RESP=$(./nmctl network list)
+    if [[ $RESP == *"unauthorized"* ]]; then
+        echo "Unable to properly configure NMCTL, exiting..."
+        exit 1
+    fi
+}
+
+# join_networks - joins netclient into the networks using old settings
+join_networks() {
+  NODE_LEN=$(jq length nodejson.tmp)
+  HAS_INGRESS="no"
+  if [ "$NODE_LEN" -gt 0 ]; then
+      for i in $(seq 1 $NODE_LEN); do
+          NUM=$(($i-1))
+          NETWORK=$(jq -r ".[$NUM].network" ./nodejson.tmp)
+          echo "  joining network $NETWORK with following settings. Please confirm:"
+          echo "         network: $(jq -r ".[$NUM].network" ./nodejson.tmp)"
+          echo "            name: $(jq -r ".[$NUM].name" ./nodejson.tmp)"
+          echo "    private ipv4: $(jq -r ".[$NUM].address" ./nodejson.tmp)"
+          echo "    private ipv6: $(jq -r ".[$NUM].address6" ./nodejson.tmp)"
+          echo "       is egress: $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp)"
+          if [[ $(jq -r ".[$NUM].isegressgateway" ./nodejson.tmp) == "yes" ]]; then
+              HAS_EGRESS="yes"
+              echo "          egress range: $(jq -r ".[$NUM].egressgatewayranges" ./nodejson.tmp)"
+          fi
+          echo "      is ingress: $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp)"
+          if [[ $(jq -r ".[$NUM].isingressgateway" ./nodejson.tmp) == "yes" ]]; then
+              HAS_INGRESS="yes"
+          fi
+          echo "        is relay: $(jq -r ".[$NUM].isrelay" ./nodejson.tmp)"
+          if [[ $(jq -r ".[$NUM].isrelay" ./nodejson.tmp) == "yes" ]]; then
+              HAS_RELAY="yes"
+              RELAY_ADDRS=$(jq -r ".[$NUM].relayaddrs" ./nodejson.tmp | tr -d '[]\n"[:space:]')
+          fi
+          echo "     is failover: $(jq -r ".[$NUM].failover" ./nodejson.tmp)"
+          if [[ $(jq -r ".[$NUM].failover" ./nodejson.tmp) == "yes" ]]; then
+              HAS_FAILOVER="yes"
+          fi
+          echo "  ------------"
+
+          confirm
+          echo "running command: ./nmctl keys create $NETWORK 1"
+          KEY_JSON=$(./nmctl keys create $NETWORK 1)          
+          KEY=$(echo $KEY_JSON | jq -r .accessstring)
+
+          echo "join key created: $KEY"
+
+          NAME=$(jq -r ".[$NUM].name" ./nodejson.tmp)
+          ADDRESS=$(jq -r ".[$NUM].address" ./nodejson.tmp)
+          ADDRESS6=$(jq -r ".[$NUM].address6" ./nodejson.tmp)
+ 
+
+          if [[ ! -z "$ADDRESS6" ]]; then
+            echo "joining with command: netclient join -t $KEY --name=$NAME --address=$ADDRESS --address6=$ADDRESS6
+"
+            confirm
+            netclient join -t $KEY --name=$NAME --address=$ADDRESS --address6=$ADDRESS6
+          else
+            echo "joining with command: netclient join -t $KEY --name=$NAME --address=$ADDRESS"          
+            confirm
+            netclient join -t $KEY --name=$NAME --address=$ADDRESS
+          fi
+          NODE_ID=$(sudo cat /etc/netclient/nodes.yml | yq -r .$NETWORK.commonnode.id)
+          echo "join complete. New node ID: $NODE_ID"
+          if [[ $NUM -eq 0 ]]; then
+            HOST_ID=$(sudo cat /etc/netclient/netclient.yml | yq -r .host.id)
+            echo "For first join, making host a default"
+            echo "Host ID: $HOST_ID"
+            # set as a default host
+            # TODO - this command is not working
+            ./nmctl host update $HOST_ID --default
+          fi
+
+          # create an egress if necessary
+          if [[ $HAS_EGRESS == "yes" ]]; then
+            echo "Egress is currently unimplemented. Wait for 0.18.1"
+          fi
+
+          echo "HAS INGRESS: $HAS_INGRESS"
+          # create an ingress if necessary
+          if [[ $HAS_INGRESS == "yes" ]]; then
+            if [[ $HAS_FAILOVER == "yes" ]]; then
+              echo "creating ingress and failover..."
+              ./nmctl node create_ingress $NETWORK $NODE_ID --failover
+            else
+              echo "creating ingress..."
+              ./nmctl node create_ingress $NETWORK $NODE_ID
+            fi
+          fi
+
+          # relay
+          if [[ $HAS_RELAY == "yes" ]]; then
+            echo "creating relay..."
+            ./nmctl node create_relay $NETWORK $NODE_ID $RELAY_ADDRS
+          fi
+
+      done
+      echo "=================="
+  else
+      echo "no networks to join"
+  fi
+}
+
+
+cat << "EOF"
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
+The Netmaker Upgrade Script: Upgrading to v0.18.0 so you don't have to!
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
+EOF
+
+set -e 
+
+if [ $(id -u) -ne 0 ]; then
+   echo "This script must be run as root"
+   exit 1
+fi
+
+echo "...installing dependencies for script"
+install_dependencies
+
+echo "...confirming version is correct"
+check_version
+
+echo "...collecting necessary server settings"
+collect_server_settings
+
+echo "...setup nmctl"
+setup_nmctl
+
+echo "...retrieving current server node settings"
+collect_node_settings
+
+echo "...backing up docker compose to docker-compose.yml.backup"
+cp /root/docker-compose.yml /root/docker-compose.yml.backup
+
+echo "...setting docker-compose values"
+set_compose
+
+echo "...starting containers"
+start_containers
+
+wait_seconds 3
+
+echo "..testing Caddy proxy"
+test_caddy
+
+echo "..testing Netmaker health"
+# TODO, implement health check
+# netmaker_health_check
+# wait_seconds 2
+
+echo "...setting up netclient (this may take a minute, be patient)"
+setup_netclient
+wait_seconds 2
+
+echo "...join networks"
+join_networks
+
+echo "-----------------------------------------------------------------"
+echo "-----------------------------------------------------------------"
+echo "Netmaker setup is now complete. You are ready to begin using Netmaker."
+echo "Visit dashboard.$SERVER_NAME to log in"
+echo "-----------------------------------------------------------------"
+echo "-----------------------------------------------------------------"