Browse Source

Merge branch 'develop' into fix-tests

Alex 4 years ago
parent
commit
3144956aff

+ 0 - 0
licensing/LICENSE.txt → LICENSE.txt


+ 2 - 6
README.md

@@ -38,7 +38,7 @@ In future releases, we have plans to support other platforms such as Windows and
 ## Docs
 **For more information, please read the docs, or check out the Quick Start below:**
 
- - [Getting Started](docs/GETTING_STARTED.md)
+ - [General Usage](docs/USAGE.md)
  - [Troubleshooting](docs/TROUBLESHOOTING.md)
  - [API Documentation](docs/API.md)
  - [Product Roadmap](docs/ROADMAP.md)
@@ -70,14 +70,10 @@ Run the following: `curl -sfL https://raw.githubusercontent.com/gravitl/netmaker
 
 #### LICENSE
 
-Netmaker's source code and all artifacts in this repository are freely available. All versions are published under the Server Side Public License (SSPL), version 1, which can be found under the "licensing" directory: [LICENSE.txt](licensing/LICENSE.txt).
+Netmaker's source code and all artifacts in this repository are freely available. All versions are published under the Server Side Public License (SSPL), version 1, which can be found here: [LICENSE.txt](./LICENSE.txt).
 
 #### CONTACT
 
 Email: [email protected]  
 Discord: https://discord.gg/zRb9Vfhk8A
 
-#### SUPPORT
-
-BTC: 3JE5ejpwu9i4vwA4rePkEDBEPpFY1xzJuN  
-ETH: 0xB6c5D23F2bE2100A5a2D337911A7Ef7575B4f91A

+ 1 - 5
controllers/common.go

@@ -300,11 +300,7 @@ func CreateNode(node models.Node, networkName string) (models.Node, error) {
         //until one is open and then returns it
         node.Address, err = functions.UniqueAddress(networkName)
 
-        if err != nil {/*
-		errorResponse := models.ErrorResponse{
-                        Code: http.StatusInternalServerError, Message: "W1R3: Encountered an internal error! ",
-                }*/
-                //returnErrorResponse(w, r, errorResponse)
+        if err != nil {
                 return node, err
         }
 

+ 75 - 31
controllers/networkHttpController.go

@@ -104,7 +104,39 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
-func validateNetwork(operation string, network models.Network) error {
+func validateNetworkUpdate(network models.Network) error {
+
+        v := validator.New()
+
+        _ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
+                isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(fl.Field().String())
+                return isvalid
+        })
+
+        _ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
+                isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(fl.Field().String())
+                return isvalid
+        })
+
+        _ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
+                return true
+        })
+
+        _ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
+                return true
+        })
+
+        err := v.Struct(network)
+
+        if err != nil {
+                for _, e := range err.(validator.ValidationErrors) {
+                        fmt.Println(e)
+                }
+        }
+        return err
+}
+
+func validateNetworkCreate(network models.Network) error {
 
 	v := validator.New()
 
@@ -113,27 +145,21 @@ func validateNetwork(operation string, network models.Network) error {
 		return isvalid
 	})
 
-	_ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
-		isvalid := !*network.IsLocal || functions.IsIpv4CIDR(fl.Field().String())
-		return isvalid
-	})
+  _ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
+                isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(fl.Field().String())
+                return isvalid
+  })
 
-	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
-		isFieldUnique := false
-		inCharSet := false
-		if operation == "update" {
-			isFieldUnique = true
-		} else {
-			isFieldUnique, _ = functions.IsNetworkNameUnique(fl.Field().String())
-			inCharSet = functions.NameInNetworkCharSet(fl.Field().String())
-		}
+        _ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
+		isFieldUnique, _ := functions.IsNetworkNameUnique(fl.Field().String())
+		inCharSet        := functions.NameInNetworkCharSet(fl.Field().String())
 		return isFieldUnique && inCharSet
 	})
 
-	_ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
-		isFieldUnique, _ := functions.IsNetworkDisplayNameUnique(fl.Field().String())
-		return isFieldUnique || operation == "update"
-	})
+  _ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
+                isFieldUnique, _ := functions.IsNetworkDisplayNameUnique(fl.Field().String())
+                return isFieldUnique
+  })
 
 	err := v.Struct(network)
 
@@ -288,7 +314,13 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	//NOTE: Network.NetID is intentionally NOT editable. It acts as a static ID for the network.
+  err = validateNetworkUpdate(networkChange)
+  if err != nil {
+         returnErrorResponse(w,r,formatError(err, "badrequest"))
+         return
+  }
+
+	//NOTE: Network.NetID is intentionally NOT editable. It acts as a static ID for the network. 
 	//DisplayName can be changed instead, which is what shows on the front end
 	if networkChange.NetID != network.NetID {
 		returnErrorResponse(w, r, formatError(errors.New("NetID is not editable"), "badrequest"))
@@ -475,10 +507,21 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 
 	//TODO: Not really doing good validation here. Same as createNode, updateNode, and updateNetwork
 	//Need to implement some better validation across the board
-	if network.IsLocal == nil {
-		falsevar := false
-		network.IsLocal = &falsevar
-	}
+
+        if network.IsLocal == nil {
+                falsevar := false
+                network.IsLocal = &falsevar
+        }
+
+        err = validateNetworkCreate(network)
+        if err != nil {
+                returnErrorResponse(w,r,formatError(err, "badrequest"))
+                return
+        }
+	network.SetDefaults()
+        network.SetNodesLastModified()
+        network.SetNetworkLastModified()
+        network.KeyUpdateTimeStamp = time.Now().Unix()
 
 	err = validateNetwork("create", network)
 	if err != nil {
@@ -538,14 +581,15 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
 	if accesskey.Value == "" {
 		accesskey.Value = functions.GenKey()
 	}
-	if accesskey.Uses == 0 {
-		accesskey.Uses = 1
-	}
-	gconf, err := functions.GetGlobalConfig()
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
+
+        if accesskey.Uses == 0 {
+                accesskey.Uses = 1
+        }
+	_, gconf, err := functions.GetGlobalConfig()
+        if err != nil {
+                returnErrorResponse(w,r,formatError(err, "internal"))
+                return
+        }
 
 	privAddr := ""
 	if *network.IsLocal {

+ 2 - 0
controllers/nodeGrpcController.go

@@ -297,6 +297,8 @@ func (s *NodeServiceServer) GetPeers(req *nodepb.GetPeersReq, stream nodepb.Node
 			Peers: &nodepb.PeersResponse{
                             Address:  peers[i].Address,
                             Endpoint:  peers[i].Endpoint,
+                            Gatewayrange:  peers[i].GatewayRange,
+                            Isgateway:  peers[i].IsGateway,
                             Publickey:  peers[i].PublicKey,
                             Keepalive:  peers[i].KeepAlive,
                             Listenport:  peers[i].ListenPort,

+ 5 - 5
controllers/nodeHttpController.go

@@ -500,7 +500,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 
 	err =  ValidateNode("create", networkName, node)
         if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
+                returnErrorResponse(w,r,formatError(err, "badrequest"))
                 return
         }
 
@@ -592,12 +592,12 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
 	nodechange.IsGateway = true
 	nodechange.GatewayRange = gateway.RangeString
 	if gateway.PostUp == "" {
-		nodechange.PostUp = "iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
+		nodechange.PostUp = "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
 	} else {
 		nodechange.PostUp = gateway.PostUp
 	}
 	if gateway.PostDown == "" {
-		nodechange.PostDown = "iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
+		nodechange.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
 	} else {
 		nodechange.PostDown = gateway.PostDown
 	}
@@ -615,7 +615,7 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
         update := bson.D{
                 {"$set", bson.D{
                         {"postup", nodechange.PostUp},
-                        {"preup", nodechange.PostDown},
+                        {"postdown", nodechange.PostDown},
                         {"isgateway", nodechange.IsGateway},
                         {"gatewayrange", nodechange.GatewayRange},
 			{"lastmodified", nodechange.LastModified},
@@ -746,7 +746,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 
         err = ValidateNode("update", params["network"], nodechange)
         if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
+                returnErrorResponse(w,r,formatError(err, "badrequest"))
                 return
         }
 

+ 9 - 3
controllers/userHttpController.go

@@ -7,6 +7,7 @@ import (
     "github.com/gravitl/netmaker/mongoconn"
     "golang.org/x/crypto/bcrypt"
     "time"
+    "errors"
     "strings"
     "fmt"
     "context"
@@ -444,11 +445,16 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 
 	success, err := DeleteUser(params["username"])
 
-	if err != nil || !success {
-		http.Error(w, err.Error(), 400)
+	if err != nil {
+                returnErrorResponse(w, r, formatError(err, "internal"))
 		json.NewEncoder(w).Encode("Could not delete user " + params["username"])
 		return
-	}
+	} else if !success {
+                returnErrorResponse(w, r, formatError(errors.New("Delete unsuccessful."), "internal"))
+                json.NewEncoder(w).Encode("Could not delete user " + params["username"])
+                return
+        }
+
 
 	json.NewEncoder(w).Encode(params["username"] + " deleted.")
 }

+ 1 - 1
docs/API.md

@@ -6,7 +6,7 @@ Most actions that can be performed via API can be performed via UI. We recommend
  
 #### Authentication
  In general, API calls must be authenticated via a header of  the format  `-H "Authorization: Bearer <YOUR_SECRET_KEY>"` There are two methods of obtaining YOUR_SECRET_KEY:
-1. Using the masterkey. By default, this value is "secret key," but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [getting started](./GETTING_STARTED.md) documentation for more details.
+1. Using the masterkey. By default, this value is "secret key," but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [general usage](./USAGE.md) documentation for more details.
 2. Using a JWT recieved for a node. This  can be retrieved by calling the `/api/nodes/<network>/authenticate` endpoint, as documented below.
 
 #### Format 

+ 74 - 0
docs/CODE_OF_CONDUCT.md

@@ -0,0 +1,74 @@
+## Code of Conduct
+
+### Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of experience,
+nationality, personal appearance, race, religion, or sexual identity and
+orientation.
+
+### Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+  address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+  professional setting
+
+### Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+### Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+### Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at [email protected]. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+### Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/

+ 26 - 4
docs/CONTRIBUTING.md

@@ -1,6 +1,28 @@
-## Contributing
+# Contributing to Netmaker
+Welcome! If you're reading this, you might be wondering how to go about submitting some changes, whether those are features, bugfixes, or simple enhancements. You're in the right place. Please read below to learn more.
 
- 1. Create a feature / bugfix branch from develop
- 2.  Write your code
- 3. Submit PR to develop
+## Code of Conduct
 
+Please read through our [code of conduct](./CODE_OF_CONDUCT.md), and when making contributions to the community, stay true to that text.
+
+## Report bugs and requests [here](https://github.com/gravitl/netmaker/issues)
+We use GitHub issues to track bugs, feature requests, and enhancements. If you think there's something missing or wrong with Netmaker, let us know! Try to add the appropriate tags and describe your issue thoroughly. If it's a feature request and we like it, we'll add it to the [roadmap](ROADMAP.md)
+
+## Submitting a PR
+We actively welcome contributions, and the way to do that is with a PR:
+
+1. Fork the repo
+2. Create a branch from `develop` based on what you are developing. In general this will be a feature or a bugfix branch, and should have the format of feature_vX.X_mynewfeature or bugfix_vX.X_mybugfix. Check the releases to see what minor version we are currently developing.
+3. Do your thing
+4. Document thoroughly
+5. Issue a PR to `develop`
+6. Sign the CLA
+
+## Contributor License Agreement
+
+When submitting a PR, you will be asked to sign a CLA, defined [here](https://gist.github.com/afeiszli/2f9f8133929e7d5574a9d892959d58a7). We've tried to make this as non-annoying as possible. This is adapted from the text used by the Apache Foundation in their CLA.
+
+This project is evolving quickly and we may want to move to an MIT or GPL license at some point in the near future, which would be difficult without a CLA.
+
+## Licensing
+Any contributions you make will be under the SSPL Software License. When you submit code changes, you  understand that they will be under the same license that covers this project, defined [here](../LICENSE.txt). If you have any concerns around this, feel free to contact the maintainers.

+ 1 - 0
docs/SITE_2_SITE.md

@@ -0,0 +1 @@
+# This document will cover how to set up site to site connections using netmaker

+ 28 - 1
docs/TROUBLESHOOTING.md

@@ -1,2 +1,29 @@
-## Incorrect backend detected. Please specify correct URL and refresh. Given: http://localhost:8081
+# Netmaker Troubleshooting Help
+
+## Client (netclient)
+	### Problem: netclient-install script not working
+	### Problem: Hanging artifacts from previous install
+	### Problem: Need to change access token settings
+
+
+### Client fails to install
+
+### Cannot run install script
+
+### Issue with accesstoken created by UI
+
+
+## Server
+	### Server not added to default network
+	### Global config not found
+
+
+
+## MongoDB
+
+
+
+## UI
+
+### Incorrect backend detected. Please specify correct URL and refresh. Given: http://localhost:8081
 Solution: Front end expects a reachable address for the backend. Localhost is default. Check if server is up. If server is up, make sure you've got the right endpoint (endpoint of server. Will not be 'localhost' unless doing local testing). If server is up and endpoint is correct, check for port blockings.

+ 9 - 1
docs/GETTING_STARTED.md → docs/USAGE.md

@@ -1,4 +1,12 @@
-# Getting Started (Simple Setup)
+# Getting Started
+
+This guide covers the fundamentals of using Netmaker.
+
+## Quick Start
+
+
+## Non-Docker Setup
+
 ### Server Setup
  1. Get yourself a linux server and make sure it has a public IP.
  2. Deploy MongoDB `docker volume create mongovol && docker run -d --name mongodb -v mongovol:/data/db --network host -e MONGO_INITDB_ROOT_USERNAME=mongoadmin -e MONGO_INITDB_ROOT_PASSWORD=mongopass mongo --bind_ip 0.0.0.0 `

+ 29 - 8
functions/helpers.go

@@ -25,7 +25,7 @@ import (
 //node has that value for the same field within the network
 
 func CreateServerToken(netID string) (string, error) {
-
+	fmt.Println("Creating token.")
         var network models.Network
         var accesskey models.AccessKey
 
@@ -37,14 +37,29 @@ func CreateServerToken(netID string) (string, error) {
                 accesskey.Name = GenKeyName()
                 accesskey.Value = GenKey()
                 accesskey.Uses = 1
-        gconf, errG := GetGlobalConfig()
+        _, gconf, errG := GetGlobalConfig()
         if errG != nil {
                 return "", errG
         }
         address := "localhost" + gconf.PortGRPC
 
-        accessstringdec := address + "." + netID + "." + accesskey.Value
-        accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
+        privAddr := ""
+        if *network.IsLocal {
+                privAddr = network.LocalRange
+        }
+
+
+	fmt.Println("Token details:")
+	fmt.Println("    grpc address + port: " + address)
+	fmt.Println("                network: " + netID)
+	fmt.Println("          private range: " + privAddr)
+
+	accessstringdec := address + "|" + netID + "|" + accesskey.Value + "|" + privAddr
+
+	accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
+
+        fmt.Println("          access string: " + accesskey.AccessString)
+
 
         network.AccessKeys = append(network.AccessKeys, accesskey)
 
@@ -504,7 +519,9 @@ func UniqueAddress(networkName string) (string, error){
 }
 
 //pretty simple get
-func GetGlobalConfig() ( models.GlobalConfig, error) {
+func GetGlobalConfig() (bool, models.GlobalConfig, error) {
+
+	create := false
 
         filter := bson.M{}
 
@@ -518,12 +535,16 @@ func GetGlobalConfig() ( models.GlobalConfig, error) {
 
         defer cancel()
 
-        if err != nil {
+	if err == mongo.ErrNoDocuments {
+                fmt.Println("Global config does not exist. Need to create.")
+		create = true
+		return create, globalconf, err
+	} else if err != nil {
                 fmt.Println(err)
                 fmt.Println("Could not get global config")
-                return globalconf, err
+                return create, globalconf, err
         }
-	return globalconf, err
+	return create, globalconf, err
 }
 
 

+ 4 - 0
functions/jwt.go

@@ -50,6 +50,10 @@ func CreateUserJWT(username string, isadmin bool) (response string, err error) {
 func VerifyUserToken(tokenString string) (username string, isadmin bool, err error) {
     claims := &models.UserClaims{}
 
+    if tokenString == config.Config.Server.MasterKey {
+        return "masteradministrator", true, nil
+    }
+
     token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
         return jwtSecretKey, nil
     })

+ 20 - 7
main.go

@@ -16,6 +16,7 @@ import (
     "fmt"
     "time"
     "net/http"
+    "strings"
     "errors"
     "io/ioutil"
     "os"
@@ -25,6 +26,7 @@ import (
     "strconv"
     "sync"
     "os/signal"
+    "go.mongodb.org/mongo-driver/mongo"
     service "github.com/gravitl/netmaker/controllers"
     nodepb "github.com/gravitl/netmaker/grpc"
     "google.golang.org/grpc"
@@ -61,6 +63,7 @@ func main() {
 
 	log.Println("Server starting...")
 	mongoconn.ConnectDatabase()
+
 	installserver := false
 	if !(defaultnet == "off") {
 	if config.Config.Server.CreateDefault {
@@ -127,7 +130,7 @@ func runGRPC(wg *sync.WaitGroup, installserver bool) {
 	gconf.Name = "netmaker"
 	err := setGlobalConfig(gconf)
 
-	if err != nil {
+	if err != nil && err != mongo.ErrNoDocuments{
 	      log.Fatalf("Unable to set global config: %v", err)
 	}
 
@@ -159,11 +162,13 @@ func runGRPC(wg *sync.WaitGroup, installserver bool) {
         fmt.Println("Agent Server succesfully started on port " + grpcport + " (gRPC)")
 
 	if installserver {
-			fmt.Println("Adding server to default network")
+			fmt.Println("Adding server to " + config.Config.Server.DefaultNetName)
                         success, err := serverctl.AddNetwork(config.Config.Server.DefaultNetName)
                         if err != nil || !success {
                                 fmt.Printf("Error adding to default network: %v", err)
+				fmt.Println("")
 				fmt.Println("Unable to add server to network. Continuing.")
+				fmt.Println("Please investigate client installation on server.")
 			} else {
                                 fmt.Println("Server successfully added to default network.")
 			}
@@ -198,12 +203,16 @@ func setGlobalConfig(globalconf models.GlobalConfig) (error) {
         collection := mongoconn.Client.Database("netmaker").Collection("config")
         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-	_, err := functions.GetGlobalConfig()
-	if err != nil {
+	create, _, err := functions.GetGlobalConfig()
+	if create {
 		_, err := collection.InsertOne(ctx, globalconf)
 		defer cancel()
 		if err != nil {
-			return err
+			if err == mongo.ErrNoDocuments || strings.Contains(err.Error(), "no documents in result"){
+				return nil
+			} else {
+				return err
+			}
 		}
 	} else {
 		filter := bson.M{"name": "netmaker"}
@@ -213,9 +222,13 @@ func setGlobalConfig(globalconf models.GlobalConfig) (error) {
 				{"portgrpc", globalconf.PortGRPC},
 			}},
 		}
-		err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&globalconf)
+		err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&globalconf)
+                        if err == mongo.ErrNoDocuments {
+			//if err == mongo.ErrNoDocuments || strings.Contains(err.Error(), "no documents in result"){
+                                return nil
+                        }
 	}
-	return nil
+	return err
 }
 
 func createDefaultNetwork() (bool, error) {

+ 2 - 0
models/structs.go

@@ -95,6 +95,8 @@ type PeersResponse struct {
     Endpoint string `json:"endpoint" bson:"endpoint"`
     Address string `json:"address" bson:"address"`
     LocalAddress string `json:"localaddress" bson:"localaddress"`
+    IsGateway bool `json:"isgateway" bson:"isgateway"`
+    GatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
     ListenPort int32 `json:"listenport" bson:"listenport"`
     KeepAlive int32 `json:"persistentkeepalive" bson:"persistentkeepalive"`
 }

+ 1 - 1
netclient-install.sh

@@ -3,7 +3,7 @@ set -e
 
 [ -z "$KEY" ] && KEY=nokey;
 
-wget -O --backups=1 https://github.com/gravitl/netmaker/releases/download/latest/netclient
+wget -O netclient https://github.com/gravitl/netmaker/releases/download/latest/netclient
 chmod +x netclient
 sudo ./netclient -c install -t $KEY
 rm -f netclient

+ 79 - 5
netclient/functions/common.go

@@ -196,6 +196,8 @@ func Install(accesskey string, password string, server string, network string, n
 	var privatekey wgtypes.Key
 	var privkeystring string
 	var endpoint string
+	var postup string
+	var postdown string
 	var name string
 	var wginterface string
 
@@ -274,6 +276,17 @@ func Install(accesskey string, password string, server string, network string, n
         }
        fmt.Println("     Interface: " + wginterface)
 
+        if nodecfg.PostUp != "" {
+                postup = nodecfg.PostUp
+        }
+       fmt.Println("     PostUp: " + postup)
+
+       if nodecfg.PostDown!= "" {
+                postdown = nodecfg.PostDown
+        }
+       fmt.Println("     PostDown: " + postdown)
+
+
        if nodecfg.KeepAlive != 0 {
                 keepalive = nodecfg.KeepAlive
         }
@@ -347,6 +360,8 @@ func Install(accesskey string, password string, server string, network string, n
                 Accesskey: accesskey,
                 Nodenetwork:  network,
                 Listenport: listenport,
+                Postup: postup,
+                Postdown: postdown,
                 Keepalive: keepalive,
 		Localaddress: localaddress,
 		Interface: wginterface,
@@ -384,6 +399,8 @@ func Install(accesskey string, password string, server string, network string, n
        fmt.Println("     Local Address: " + node.Localaddress)
        fmt.Println("     Name: " + node.Name)
        fmt.Println("     Interface: " + node.Interface)
+       fmt.Println("     PostUp: " + node.Postup)
+       fmt.Println("     PostDown: " + node.Postdown)
        fmt.Println("     Port: " + strconv.FormatInt(int64(node.Listenport), 10))
        fmt.Println("     KeepAlive: " + strconv.FormatInt(int64(node.Keepalive), 10))
        fmt.Println("     Public Key: " + node.Publickey)
@@ -483,6 +500,12 @@ func modConfig(node *nodepb.Node) error{
         if node.Localaddress != ""{
 		nodecfg.LocalAddress = node.Localaddress
         }
+        if node.Postup != ""{
+                nodecfg.PostUp = node.Postup
+        }
+        if node.Postdown != ""{
+                nodecfg.PostDown = node.Postdown
+        }
         if node.Listenport != 0{
                 nodecfg.Port = node.Listenport
         }
@@ -655,12 +678,41 @@ func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
                 Stderr: os.Stdout,
         }
         err = cmdIPLinkDown.Run()
-        err = cmdIPLinkUp.Run()
-        if  err  !=  nil {
+        if nodecfg.PostDown != "" {
+		runcmds := strings.Split(nodecfg.PostDown, "; ")
+		err = runCmds(runcmds)
+		if err != nil {
+			fmt.Println("Error encountered running PostDown: " + err.Error())
+		}
+	}
+
+	err = cmdIPLinkUp.Run()
+        if nodecfg.PostUp != "" {
+                runcmds := strings.Split(nodecfg.PostUp, "; ")
+                err = runCmds(runcmds)
+                if err != nil {
+                        fmt.Println("Error encountered running PostUp: " + err.Error())
+                }
+        }
+	if  err  !=  nil {
                 return err
         }
 	return err
 }
+func runCmds(commands []string) error {
+	var err error
+	for _, command := range commands {
+		fmt.Println("Running command: " + command)
+		args := strings.Fields(command)
+		out, err := exec.Command(args[0], args[1:]...).Output()
+		fmt.Println(string(out))
+		if err != nil {
+			return err
+		}
+	}
+	return err
+}
+
 
 func setWGKeyConfig(network string, serveraddr string) error {
 
@@ -936,7 +988,7 @@ func CheckIn(network string) error {
                 if ifaceupdate {
 			fmt.Println("Interface update: " + currentiface +
 			" >>>> " + newinterface)
-                        err := DeleteInterface(currentiface)
+                        err := DeleteInterface(currentiface, nodecfg.PostDown)
                         if err != nil {
                                 fmt.Println("ERROR DELETING INTERFACE: " + currentiface)
                         }
@@ -1183,12 +1235,19 @@ func WipeLocal(network string) error{
         if  err  !=  nil {
                 fmt.Println(err)
         }
+        if nodecfg.PostDown != "" {
+                runcmds := strings.Split(nodecfg.PostDown, "; ")
+                err = runCmds(runcmds)
+                if err != nil {
+                        fmt.Println("Error encountered running PostDown: " + err.Error())
+                }
+        }
 	}
 	return err
 
 }
 
-func DeleteInterface(ifacename string) error{
+func DeleteInterface(ifacename string, postdown string) error{
         ipExec, err := exec.LookPath("ip")
 
         cmdIPLinkDel := &exec.Cmd {
@@ -1201,6 +1260,13 @@ func DeleteInterface(ifacename string) error{
         if  err  !=  nil {
                 fmt.Println(err)
         }
+        if postdown != "" {
+                runcmds := strings.Split(postdown, "; ")
+                err = runCmds(runcmds)
+                if err != nil {
+                        fmt.Println("Error encountered running PostDown: " + err.Error())
+                }
+        }
         return err
 }
 
@@ -1260,7 +1326,7 @@ func getPeers(macaddress string, network string, server string) ([]wgtypes.PeerC
                         fmt.Println(err)
 			break
                 }
-			spew.Dump(res)
+		spew.Dump(res)
 
                 // if err, return an error
                 if err != nil {
@@ -1272,6 +1338,13 @@ func getPeers(macaddress string, network string, server string) ([]wgtypes.PeerC
                         return peers, err
 			}
                 }
+		fmt.Println("Got Peer: "  + res.Peers.Publickey)
+		fmt.Println("    Address: " +res.Peers.Address)
+		fmt.Printf("    ListenPort: ",res.Peers.Listenport)
+		fmt.Println("")
+		fmt.Printf("    Gateway?: ",res.Peers.Isgateway)
+		fmt.Println("")
+                fmt.Println("    Gate Range: " + res.Peers.Gatewayrange)
 		pubkey, err := wgtypes.ParseKey(res.Peers.Publickey)
                 if err != nil {
 			fmt.Println("error parsing key")
@@ -1292,6 +1365,7 @@ func getPeers(macaddress string, network string, server string) ([]wgtypes.PeerC
 				fmt.Println("NOT SETTING GATEWAY")
 				fmt.Println(err)
 			} else {
+				fmt.Println("    Gateway Range: "  + res.Peers.Gatewayrange)
 				allowedips = append(allowedips, *ipnet)
 			}
 		}

+ 1 - 0
netclient/main.go

@@ -110,6 +110,7 @@ func main() {
 			fmt.Println("Beginning agent installation.")
 			err := functions.Install(*taccesskey, *tpassword, *tserver, *tnetwork, *tnoauto, *taccesstoken, *tname)
 			if err != nil {
+				fmt.Println("Error encountered while installing.")
 				if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {
 				fmt.Println("Error installing: ", err)
 				fmt.Println("Cleaning up (uninstall)")

+ 1 - 0
privatekey

@@ -0,0 +1 @@
+wMb6dxHPNJqQd8GbwfLN8HPLiJYEl1uJtEls5hRoD10=

+ 1 - 0
publickey

@@ -0,0 +1 @@
+/FdO9q+Bs3ee/NVbtKwMhSmFj4AyyjmlOrujzaBoenE=

+ 15 - 6
serverctl/serverctl.go

@@ -4,6 +4,7 @@ import (
         "fmt"
   "github.com/gravitl/netmaker/functions"
 	"io"
+	"errors"
 	"net/http"
         "os"
         "os/exec"
@@ -15,6 +16,7 @@ func DownloadNetclient() error {
 	// Get the data
 	resp, err := http.Get("https://github.com/gravitl/netmaker/releases/download/latest/netclient")
 	if err != nil {
+                fmt.Println("could not download netclient")
 		return err
 	}
 	defer resp.Body.Close()
@@ -22,6 +24,7 @@ func DownloadNetclient() error {
 	// Create the file
 	out, err := os.Create("/etc/netclient/netclient")
 	if err != nil {
+                fmt.Println("could not create /etc/netclient")
 		return err
 	}
 	defer out.Close()
@@ -33,6 +36,7 @@ func DownloadNetclient() error {
 func RemoveNetwork(network string) (bool, error) {
 	_, err := os.Stat("/etc/netclient/netclient")
         if err != nil {
+                fmt.Println("could not find /etc/netclient")
 		return false, err
 	}
         cmdoutput, err := exec.Command("/etc/netclient/netclient","-c","remove","-n",network).Output()
@@ -50,31 +54,36 @@ func AddNetwork(network string) (bool, error) {
         if os.IsNotExist(err) {
                 os.Mkdir("/etc/netclient", 744)
         } else if err != nil {
-                fmt.Println("couldnt find or create /etc/netclient")
+                fmt.Println("could not find or create /etc/netclient")
                 return false, err
         }
+	fmt.Println("Directory is ready.")
 	token, err := functions.CreateServerToken(network)
         if err != nil {
-                return false, err
+                fmt.Println("could not create server token for " + network)
+		return false, err
         }
+	fmt.Println("Token is ready.")
         _, err = os.Stat("/etc/netclient/netclient")
 	if os.IsNotExist(err) {
 		err = DownloadNetclient()
+                fmt.Println("could not download netclient")
 		if err != nil {
 			return false, err
 		}
 	}
         err = os.Chmod("/etc/netclient/netclient", 0755)
         if err != nil {
+                fmt.Println("could not change netclient directory permissions")
                 return false, err
         }
-	cmdoutput, err := exec.Command("/etc/netclient/netclient","-c","install","-t",token,"-name","netmaker").Output()
+	fmt.Println("Client is ready. Running install.")
+	out, err := exec.Command("/etc/netclient/netclient","-c","install","-t",token,"-name","netmaker").Output()
+        fmt.Println(string(out))
 	if err != nil {
-	        fmt.Println(string(cmdoutput))
-                return false, err
+                return false, errors.New(string(out) + err.Error())
         }
 	fmt.Println("Server added to network " + network)
 	return true, err
 }
 
-

+ 7 - 0
test/restartmongo.sh

@@ -0,0 +1,7 @@
+#!/bin/bash
+
+sudo docker kill mongodb
+sudo docker rm mongodb
+sudo docker volume rm mongovol
+
+docker volume create mongovol && docker run -d --name mongodb -v mongovol:/data/db --network host -e MONGO_INITDB_ROOT_USERNAME=mongoadmin -e MONGO_INITDB_ROOT_PASSWORD=mongopass mongo --bind_ip 0.0.0.0

+ 8 - 0
test/test.script

@@ -0,0 +1,8 @@
+#!/bin/bash
+docker run -d \
+            -e MONGO_INITDB_ROOT_USERNAME:='mongoadmin' \
+            -e MONGO_INITDB_ROOT_PASSWORD:='mongopass' \
+            -p 27017:27017 mongo:4.2 
+go run . --clientmode=off
+cd test
+go test . -v