Browse Source

fixed network tests that failed with nodes

Matthew R Kasun 4 years ago
parent
commit
ae826002cd
6 changed files with 1177 additions and 733 deletions
  1. 293 297
      controllers/common.go
  2. 10 0
      controllers/nodeHttpController.go
  3. 300 302
      functions/helpers.go
  4. 28 18
      test/api_test.go
  5. 28 13
      test/group_test.go
  6. 518 103
      test/node_test.go

+ 293 - 297
controllers/common.go

@@ -1,226 +1,226 @@
 package controller
 
 import (
-    "gopkg.in/go-playground/validator.v9"
-    "log"
-    "fmt"
-    "golang.org/x/crypto/bcrypt"
-    "github.com/gravitl/netmaker/mongoconn"
-    "github.com/gravitl/netmaker/functions"
-    "context"
-    "go.mongodb.org/mongo-driver/bson"
-    "time"
-    "net"
-    "github.com/gravitl/netmaker/models"
-    "go.mongodb.org/mongo-driver/mongo/options"
-
+	"context"
+	"fmt"
+	"log"
+	"net"
+	"time"
+
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mongoconn"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"golang.org/x/crypto/bcrypt"
+	"gopkg.in/go-playground/validator.v9"
 )
 
 func GetPeersList(networkName string) ([]models.PeersResponse, error) {
 
-        var peers []models.PeersResponse
+	var peers []models.PeersResponse
 
-        //Connection mongoDB with mongoconn class
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	//Connection mongoDB with mongoconn class
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        //Get all nodes in the relevant network which are NOT in pending state
-        filter := bson.M{"network": networkName, "ispending": false}
-        cur, err := collection.Find(ctx, filter)
+	//Get all nodes in the relevant network which are NOT in pending state
+	filter := bson.M{"network": networkName, "ispending": false}
+	cur, err := collection.Find(ctx, filter)
 
-        if err != nil {
-                return peers, err
-        }
+	if err != nil {
+		return peers, err
+	}
 
-        // Close the cursor once finished and cancel if it takes too long
-        defer cancel()
+	// Close the cursor once finished and cancel if it takes too long
+	defer cancel()
 
-        for cur.Next(context.TODO()) {
+	for cur.Next(context.TODO()) {
 
-                var peer models.PeersResponse
-                err := cur.Decode(&peer)
-                if err != nil {
-                        log.Fatal(err)
-                }
+		var peer models.PeersResponse
+		err := cur.Decode(&peer)
+		if err != nil {
+			log.Fatal(err)
+		}
 
-                // add the node to our node array
-                //maybe better to just return this? But then that's just GetNodes...
-                peers = append(peers, peer)
-        }
+		// add the node to our node array
+		//maybe better to just return this? But then that's just GetNodes...
+		peers = append(peers, peer)
+	}
 
-        //Uh oh, fatal error! This needs some better error handling
-        //TODO: needs appropriate error handling so the server doesnt shut down.
-        if err := cur.Err(); err != nil {
-                log.Fatal(err)
-        }
+	//Uh oh, fatal error! This needs some better error handling
+	//TODO: needs appropriate error handling so the server doesnt shut down.
+	if err := cur.Err(); err != nil {
+		log.Fatal(err)
+	}
 
 	return peers, err
 }
 
-
 func ValidateNode(operation string, networkName string, node models.Node) error {
 
-        v := validator.New()
+	v := validator.New()
 
-        _ = v.RegisterValidation("endpoint_check", func(fl validator.FieldLevel) bool {
-                //var isFieldUnique bool = functions.IsFieldUnique(networkName, "endpoint", node.Endpoint)
+	_ = v.RegisterValidation("endpoint_check", func(fl validator.FieldLevel) bool {
+		//var isFieldUnique bool = functions.IsFieldUnique(networkName, "endpoint", node.Endpoint)
 		isIpv4 := functions.IsIpv4Net(node.Endpoint)
 		notEmptyCheck := node.Endpoint != ""
-                return (notEmptyCheck && isIpv4) || operation == "update"
-        })
-        _ = v.RegisterValidation("localaddress_check", func(fl validator.FieldLevel) bool {
-                //var isFieldUnique bool = functions.IsFieldUnique(networkName, "endpoint", node.Endpoint)
-                isIpv4 := functions.IsIpv4Net(node.LocalAddress)
-                notEmptyCheck := node.LocalAddress != ""
-                return (notEmptyCheck && isIpv4) || operation == "update"
-        })
-
-
-        _ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {
-                var isFieldUnique bool = functions.IsFieldUnique(networkName, "macaddress", node.MacAddress)
-                return isFieldUnique || operation == "update"
-        })
-
-        _ = v.RegisterValidation("macaddress_valid", func(fl validator.FieldLevel) bool {
-                _, err := net.ParseMAC(node.MacAddress)
-                return err == nil
-        })
-
-        _ = v.RegisterValidation("name_valid", func(fl validator.FieldLevel) bool {
-                isvalid := functions.NameInNodeCharSet(node.Name)
-                return isvalid
-        })
-
-        _ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
+		return (notEmptyCheck && isIpv4)
+	})
+	_ = v.RegisterValidation("localaddress_check", func(fl validator.FieldLevel) bool {
+		//var isFieldUnique bool = functions.IsFieldUnique(networkName, "endpoint", node.Endpoint)
+		isIpv4 := functions.IsIpv4Net(node.LocalAddress)
+		notEmptyCheck := node.LocalAddress != ""
+		return (notEmptyCheck && isIpv4)
+	})
+
+	_ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {
+		var isFieldUnique bool = functions.IsFieldUnique(networkName, "macaddress", node.MacAddress)
+		return isFieldUnique || operation == "update"
+	})
+
+	_ = v.RegisterValidation("macaddress_valid", func(fl validator.FieldLevel) bool {
+		_, err := net.ParseMAC(node.MacAddress)
+		return err == nil
+	})
+
+	_ = v.RegisterValidation("name_valid", func(fl validator.FieldLevel) bool {
+		isvalid := functions.NameInNodeCharSet(node.Name)
+		return isvalid
+	})
+
+	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
 		_, err := node.GetNetwork()
 		return err == nil
-        })
-        _ = v.RegisterValidation("pubkey_check", func(fl validator.FieldLevel) bool { 
-                notEmptyCheck := node.PublicKey != ""
+	})
+	_ = v.RegisterValidation("pubkey_check", func(fl validator.FieldLevel) bool {
+		notEmptyCheck := node.PublicKey != ""
 		isBase64 := functions.IsBase64(node.PublicKey)
-                return (notEmptyCheck  && isBase64) || operation == "update"
-        })
-        _ = v.RegisterValidation("password_check", func(fl validator.FieldLevel) bool { 
-                notEmptyCheck := node.Password != ""
+		return (notEmptyCheck && isBase64)
+	})
+	_ = v.RegisterValidation("password_check", func(fl validator.FieldLevel) bool {
+		notEmptyCheck := node.Password != ""
 		goodLength := len(node.Password) > 5
-                return (notEmptyCheck && goodLength) || operation == "update"
-        })
+		return (notEmptyCheck && goodLength)
+	})
 
-        err := v.Struct(node)
+	err := v.Struct(node)
 
-        if err != nil {
-                for _, e := range err.(validator.ValidationErrors) {
-                        fmt.Println(e)
-                }
-        }
-        return err
+	if err != nil {
+		for _, e := range err.(validator.ValidationErrors) {
+			fmt.Println(e)
+		}
+	}
+	return err
 }
 
-
 func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
-    //Question: Is there a better way  of doing  this than a bunch of "if" statements? probably...
-    //Eventually, lets have a better way to check if any of the fields are filled out...
-    queryMac := node.MacAddress
-    queryNetwork := node.Network
-    notifynetwork := false
-
-    if nodechange.Address != "" {
-        node.Address = nodechange.Address
-	notifynetwork = true
-    }
-    if nodechange.Name != "" {
-        node.Name = nodechange.Name
-    }
-    if nodechange.LocalAddress != "" {
-        node.LocalAddress = nodechange.LocalAddress
-    }
-    if nodechange.ListenPort != 0 {
-        node.ListenPort = nodechange.ListenPort
-    }
-    if nodechange.ExpirationDateTime != 0 {
-        node.ExpirationDateTime = nodechange.ExpirationDateTime
-    }
-    if nodechange.PostDown != "" {
-        node.PostDown = nodechange.PostDown
-    }
-    if nodechange.Interface != "" {
-        node.Interface = nodechange.Interface
-    }
-    if nodechange.PostUp != "" {
-        node.PostUp = nodechange.PostUp
-    }
-    if nodechange.AccessKey != "" {
-        node.AccessKey = nodechange.AccessKey
-    }
-    if nodechange.Endpoint != "" {
-        node.Endpoint = nodechange.Endpoint
-	notifynetwork = true
-}
-    if nodechange.SaveConfig != nil {
-        node.SaveConfig = nodechange.SaveConfig
-    }
-    if nodechange.PersistentKeepalive != 0 {
-        node.PersistentKeepalive = nodechange.PersistentKeepalive
-    }
-    if nodechange.Password != "" {
-	    err := bcrypt.CompareHashAndPassword([]byte(nodechange.Password), []byte(node.Password))
-           if err != nil && nodechange.Password != node.Password {
-		hash, err := bcrypt.GenerateFromPassword([]byte(nodechange.Password), 5)
-		if err != nil {
-			return node, err
+	//Question: Is there a better way  of doing  this than a bunch of "if" statements? probably...
+	//Eventually, lets have a better way to check if any of the fields are filled out...
+	queryMac := node.MacAddress
+	queryNetwork := node.Network
+	notifynetwork := false
+
+	if nodechange.Address != "" {
+		node.Address = nodechange.Address
+		notifynetwork = true
+	}
+	if nodechange.Name != "" {
+		node.Name = nodechange.Name
+	}
+	if nodechange.LocalAddress != "" {
+		node.LocalAddress = nodechange.LocalAddress
+	}
+	if nodechange.ListenPort != 0 {
+		node.ListenPort = nodechange.ListenPort
+	}
+	if nodechange.ExpirationDateTime != 0 {
+		node.ExpirationDateTime = nodechange.ExpirationDateTime
+	}
+	if nodechange.PostDown != "" {
+		node.PostDown = nodechange.PostDown
+	}
+	if nodechange.Interface != "" {
+		node.Interface = nodechange.Interface
+	}
+	if nodechange.PostUp != "" {
+		node.PostUp = nodechange.PostUp
+	}
+	if nodechange.AccessKey != "" {
+		node.AccessKey = nodechange.AccessKey
+	}
+	if nodechange.Endpoint != "" {
+		node.Endpoint = nodechange.Endpoint
+		notifynetwork = true
+	}
+	if nodechange.SaveConfig != nil {
+		node.SaveConfig = nodechange.SaveConfig
+	}
+	if nodechange.PersistentKeepalive != 0 {
+		node.PersistentKeepalive = nodechange.PersistentKeepalive
+	}
+	if nodechange.Password != "" {
+		err := bcrypt.CompareHashAndPassword([]byte(nodechange.Password), []byte(node.Password))
+		if err != nil && nodechange.Password != node.Password {
+			hash, err := bcrypt.GenerateFromPassword([]byte(nodechange.Password), 5)
+			if err != nil {
+				return node, err
+			}
+			nodechange.Password = string(hash)
+			node.Password = nodechange.Password
 		}
-		nodechange.Password = string(hash)
-		node.Password = nodechange.Password
-	}
-    }
-    if nodechange.MacAddress != "" {
-        node.MacAddress = nodechange.MacAddress
-    }
-    if nodechange.PublicKey != "" {
-        node.PublicKey = nodechange.PublicKey
-	node.KeyUpdateTimeStamp = time.Now().Unix()
-	notifynetwork = true
-    }
-
-        //collection := mongoconn.ConnectDB()
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-        // Create filter
-        filter := bson.M{"macaddress": queryMac, "network": queryNetwork}
-
-        node.SetLastModified()
-
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"name", node.Name},
-                        {"password", node.Password},
-                        {"listenport", node.ListenPort},
-                        {"publickey", node.PublicKey},
-                        {"keyupdatetimestamp", node.KeyUpdateTimeStamp},
-                        {"expdatetime", node.ExpirationDateTime},
-                        {"endpoint", node.Endpoint},
-                        {"postup", node.PostUp},
-                        {"postdown", node.PostDown},
-                        {"macaddress", node.MacAddress},
-                        {"localaddress", node.LocalAddress},
-                        {"persistentkeepalive", node.PersistentKeepalive},
-                        {"saveconfig", node.SaveConfig},
-                        {"accesskey", node.AccessKey},
-                        {"interface", node.Interface},
-                        {"lastmodified", node.LastModified},
-                }},
-        }
-        var nodeupdate models.Node
-
-        errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
+	}
+	if nodechange.MacAddress != "" {
+		node.MacAddress = nodechange.MacAddress
+	}
+	if nodechange.PublicKey != "" {
+		node.PublicKey = nodechange.PublicKey
+		node.KeyUpdateTimeStamp = time.Now().Unix()
+		notifynetwork = true
+	}
+
+	//collection := mongoconn.ConnectDB()
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+	// Create filter
+	filter := bson.M{"macaddress": queryMac, "network": queryNetwork}
+
+	node.SetLastModified()
+
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"name", node.Name},
+			{"password", node.Password},
+			{"listenport", node.ListenPort},
+			{"publickey", node.PublicKey},
+			{"keyupdatetimestamp", node.KeyUpdateTimeStamp},
+			{"expdatetime", node.ExpirationDateTime},
+			{"endpoint", node.Endpoint},
+			{"postup", node.PostUp},
+			{"postdown", node.PostDown},
+			{"macaddress", node.MacAddress},
+			{"localaddress", node.LocalAddress},
+			{"persistentkeepalive", node.PersistentKeepalive},
+			{"saveconfig", node.SaveConfig},
+			{"accesskey", node.AccessKey},
+			{"interface", node.Interface},
+			{"lastmodified", node.LastModified},
+		}},
+	}
+	var nodeupdate models.Node
+
+	errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
 	if errN != nil {
 		return nodeupdate, errN
 	}
 
+	if nodechange.MacAddress != "" {
+		queryMac = nodechange.MacAddress
+	}
 	returnnode, errN := GetNode(queryMac, queryNetwork)
 
 	defer cancel()
@@ -232,17 +232,17 @@ func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
 	return returnnode, errN
 }
 
-func DeleteNode(macaddress string, network string) (bool, error)  {
+func DeleteNode(macaddress string, network string) (bool, error) {
 
 	deleted := false
 
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 
 	filter := bson.M{"macaddress": macaddress, "network": network}
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        result, err := collection.DeleteOne(ctx, filter)
+	result, err := collection.DeleteOne(ctx, filter)
 
 	deletecount := result.DeletedCount
 
@@ -250,9 +250,9 @@ func DeleteNode(macaddress string, network string) (bool, error)  {
 		deleted = true
 	}
 
-        defer cancel()
+	defer cancel()
 
-        err = SetNetworkNodesLastModified(network)
+	err = SetNetworkNodesLastModified(network)
 	fmt.Println("Deleted node " + macaddress + " from network " + network)
 
 	return deleted, err
@@ -260,90 +260,88 @@ func DeleteNode(macaddress string, network string) (bool, error)  {
 
 func GetNode(macaddress string, network string) (models.Node, error) {
 
-        var node models.Node
+	var node models.Node
 
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"macaddress": macaddress, "network": network}
-        err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&node)
+	filter := bson.M{"macaddress": macaddress, "network": network}
+	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&node)
 
-        defer cancel()
+	defer cancel()
 
 	return node, err
 }
 
 func CreateNode(node models.Node, networkName string) (models.Node, error) {
 
-        //encrypt that password so we never see it again
-        hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
-
-        if err != nil {
-                return node, err
-        }
-        //set password to encrypted password
-        node.Password = string(hash)
+	//encrypt that password so we never see it again
+	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
+	node.Network = networkName
 
-        //node.SetDefaults()
-        //Umm, why am I doing this again?
-        //TODO: Why am I using a local function instead of the struct function? I really dont know.
-        //I think I thought it didn't work but uhhh...idk
-        //anyways, this sets some sensible variables for unset params.
-        node.SetDefaults()
+	//node.SetDefaults()
+	//Umm, why am I doing this again?
+	//TODO: Why am I using a local function instead of the struct function? I really dont know.
+	//I think I thought it didn't work but uhhh...idk
+	//anyways, this sets some sensible variables for unset params.
+	node.SetDefaults()
 
-        //Another DB call here...Inefficient
-        //Anyways, this scrolls through all the IP Addresses in the network range and checks against nodes
-        //until one is open and then returns it
-        node.Address, err = functions.UniqueAddress(networkName)
+	//Another DB call here...Inefficient
+	//Anyways, this scrolls through all the IP Addresses in the network range and checks against nodes
+	//until one is open and then returns it
+	node.Address, err = functions.UniqueAddress(networkName)
 
-        if err != nil {
-                return node, err
-        }
+	if err != nil {
+		return node, err
+	}
 
-        //IDK why these aren't a part of "set defaults. Pretty dumb.
-        //TODO: This is dumb. Consolidate and fix.
-        node.SetLastModified()
-        node.SetDefaultName()
+	//IDK why these aren't a part of "set defaults. Pretty dumb.
+	//TODO: This is dumb. Consolidate and fix.
+	node.SetLastModified()
+	node.SetDefaultName()
 	node.SetLastCheckIn()
 	node.SetLastPeerUpdate()
 	node.KeyUpdateTimeStamp = time.Now().Unix()
 
-        //Create a JWT for the node
-        tokenString, _ := functions.CreateJWT(node.MacAddress, networkName)
+	//Create a JWT for the node
+	tokenString, _ := functions.CreateJWT(node.MacAddress, networkName)
 
-        if tokenString == "" {
-                //returnErrorResponse(w, r, errorResponse)
+	if tokenString == "" {
+		//returnErrorResponse(w, r, errorResponse)
 		return node, err
-        }
-
-        // connect db
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	}
 
+	// connect db
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        // insert our node to the node db.
-        result, err := collection.InsertOne(ctx, node)
-        _ = result
+	// insert our node to the node db.
+	result, err := collection.InsertOne(ctx, node)
+	_ = result
 
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
-                return node, err
-        }
-        //return response for if node  is pending
-        if !node.IsPending {
+	if err != nil {
+		return node, err
+	}
+	//return response for if node  is pending
+	if !node.IsPending {
 
-        functions.DecrimentKey(node.Network, node.AccessKey)
+		functions.DecrimentKey(node.Network, node.AccessKey)
 
-        }
+	}
 
 	SetNetworkNodesLastModified(node.Network)
 
-        return node, err
+	return node, err
 }
 
 func NodeCheckIn(node models.Node, networkName string) (models.CheckInResponse, error) {
@@ -351,28 +349,28 @@ func NodeCheckIn(node models.Node, networkName string) (models.CheckInResponse,
 	var response models.CheckInResponse
 
 	parentnetwork, err := functions.GetParentNetwork(networkName)
-        if err != nil{
-		err = fmt.Errorf("%w; Couldnt retrieve Network " + networkName  + ": ", err)
-                return response, err
-        }
+	if err != nil {
+		err = fmt.Errorf("%w; Couldnt retrieve Network "+networkName+": ", err)
+		return response, err
+	}
 
 	parentnode, err := functions.GetNodeByMacAddress(networkName, node.MacAddress)
-        if err != nil{
-		err = fmt.Errorf("%w; Couldnt Get Node " + node.MacAddress, err)
-                return response, err
-        }
+	if err != nil {
+		err = fmt.Errorf("%w; Couldnt Get Node "+node.MacAddress, err)
+		return response, err
+	}
 	if parentnode.IsPending {
-                err = fmt.Errorf("%w; Node checking in is still pending: " + node.MacAddress, err)
+		err = fmt.Errorf("%w; Node checking in is still pending: "+node.MacAddress, err)
 		response.IsPending = true
 		return response, err
 	}
 
-        networklm := parentnetwork.NetworkLastModified
+	networklm := parentnetwork.NetworkLastModified
 	peerslm := parentnetwork.NodesLastModified
 	gkeyupdate := parentnetwork.KeyUpdateTimeStamp
 	nkeyupdate := parentnode.KeyUpdateTimeStamp
 	peerlistlm := parentnode.LastPeerUpdate
-        parentnodelm := parentnode.LastModified
+	parentnodelm := parentnode.LastModified
 	parentnodelastcheckin := parentnode.LastCheckIn
 
 	if parentnodelastcheckin < parentnodelm {
@@ -388,53 +386,53 @@ func NodeCheckIn(node models.Node, networkName string) (models.CheckInResponse,
 	if nkeyupdate < gkeyupdate {
 		response.NeedKeyUpdate = true
 	}
-        if time.Now().Unix() > parentnode.ExpirationDateTime {
-                response.NeedDelete = true
-		_, err =  DeleteNode(node.MacAddress, networkName)
-        } else {
-	err = TimestampNode(parentnode, true,  false, false)
+	if time.Now().Unix() > parentnode.ExpirationDateTime {
+		response.NeedDelete = true
+		_, err = DeleteNode(node.MacAddress, networkName)
+	} else {
+		err = TimestampNode(parentnode, true, false, false)
 
-	if err != nil{
-		err = fmt.Errorf("%w; Couldnt Timestamp Node: ", err)
-		return response, err
-	}
+		if err != nil {
+			err = fmt.Errorf("%w; Couldnt Timestamp Node: ", err)
+			return response, err
+		}
 	}
 	response.Success = true
 
-        return response, err
+	return response, err
 }
 
 func SetNetworkNodesLastModified(networkName string) error {
 
 	timestamp := time.Now().Unix()
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        // Create filter
-        filter := bson.M{"netid": networkName}
+	// Create filter
+	filter := bson.M{"netid": networkName}
 
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"nodeslastmodified", timestamp},
-                }},
-        }
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"nodeslastmodified", timestamp},
+		}},
+	}
 
-        result := collection.FindOneAndUpdate(ctx, filter, update)
+	result := collection.FindOneAndUpdate(ctx, filter, update)
 
-        defer cancel()
+	defer cancel()
 
 	if result.Err() != nil {
-                return result.Err()
-        }
+		return result.Err()
+	}
 
-        return nil
+	return nil
 }
 
-func TimestampNode(node models.Node, updatecheckin bool, updatepeers bool, updatelm bool) error{
-        if updatelm {
+func TimestampNode(node models.Node, updatecheckin bool, updatepeers bool, updatelm bool) error {
+	if updatelm {
 		node.SetLastModified()
 	}
 	if updatecheckin {
@@ -444,27 +442,25 @@ func TimestampNode(node models.Node, updatecheckin bool, updatepeers bool, updat
 		node.SetLastPeerUpdate()
 	}
 
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        // Create filter
-        filter := bson.M{"macaddress": node.MacAddress, "network": node.Network}
+	// Create filter
+	filter := bson.M{"macaddress": node.MacAddress, "network": node.Network}
 
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"lastmodified", node.LastModified},
-                        {"lastpeerupdate", node.LastPeerUpdate},
-                        {"lastcheckin", node.LastCheckIn},
-                }},
-        }
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"lastmodified", node.LastModified},
+			{"lastpeerupdate", node.LastPeerUpdate},
+			{"lastcheckin", node.LastCheckIn},
+		}},
+	}
 
 	var nodeupdate models.Node
-        err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
-        defer cancel()
+	err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
+	defer cancel()
 
 	return err
 }
-
-

+ 10 - 0
controllers/nodeHttpController.go

@@ -387,6 +387,12 @@ func checkIn(w http.ResponseWriter, r *http.Request) {
 	}
 
 	//TODO: check node last modified vs network last modified
+	//Get Updated node to return
+	node, err = GetNode(params["macaddress"], params["network"])
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(node)
 
@@ -660,6 +666,10 @@ func validateGateway(gateway models.GatewayRequest) error {
 	if empty || !isIpv4 {
 		err = errors.New("IP Range Not Valid")
 	}
+	empty = gateway.Interface == ""
+	if empty {
+		err = errors.New("Interface cannot be empty")
+	}
 	return err
 }
 

+ 300 - 302
functions/helpers.go

@@ -5,20 +5,21 @@
 package functions
 
 import (
-    "fmt"
-    "errors"
-    "math/rand"
-    "time"
-    "context"
-    "encoding/base64"
-    "strings"
-    "net"
-    "github.com/gravitl/netmaker/models"
-    "github.com/gravitl/netmaker/mongoconn"
-    "go.mongodb.org/mongo-driver/bson"
-    "go.mongodb.org/mongo-driver/bson/primitive"
-    "go.mongodb.org/mongo-driver/mongo/options"
-    "go.mongodb.org/mongo-driver/mongo"
+	"context"
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"math/rand"
+	"net"
+	"strings"
+	"time"
+
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mongoconn"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
 )
 
 //Takes in an arbitrary field and value for field and checks to see if any other
@@ -26,28 +27,27 @@ import (
 
 func CreateServerToken(netID string) (string, error) {
 	fmt.Println("Creating token.")
-        var network models.Network
-        var accesskey models.AccessKey
-
-        network, err := GetParentNetwork(netID)
-        if err != nil {
-                return "", err
-        }
+	var network models.Network
+	var accesskey models.AccessKey
 
-                accesskey.Name = GenKeyName()
-                accesskey.Value = GenKey()
-                accesskey.Uses = 1
-        _, gconf, errG := GetGlobalConfig()
-        if errG != nil {
-                return "", errG
-        }
-        address := "localhost" + gconf.PortGRPC
+	network, err := GetParentNetwork(netID)
+	if err != nil {
+		return "", err
+	}
 
-        privAddr := ""
-        if *network.IsLocal {
-                privAddr = network.LocalRange
-        }
+	accesskey.Name = GenKeyName()
+	accesskey.Value = GenKey()
+	accesskey.Uses = 1
+	_, gconf, errG := GetGlobalConfig()
+	if errG != nil {
+		return "", errG
+	}
+	address := "localhost" + gconf.PortGRPC
 
+	privAddr := ""
+	if *network.IsLocal {
+		privAddr = network.LocalRange
+	}
 
 	fmt.Println("Token details:")
 	fmt.Println("    grpc address + port: " + address)
@@ -58,73 +58,72 @@ func CreateServerToken(netID string) (string, error) {
 
 	accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
 
-        fmt.Println("          access string: " + accesskey.AccessString)
-
+	fmt.Println("          access string: " + accesskey.AccessString)
 
-        network.AccessKeys = append(network.AccessKeys, accesskey)
+	network.AccessKeys = append(network.AccessKeys, accesskey)
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        // Create filter
-        filter := bson.M{"netid": netID}
+	// Create filter
+	filter := bson.M{"netid": netID}
 
-        // Read update model from body request
-        fmt.Println("Adding key to " + network.NetID)
+	// Read update model from body request
+	fmt.Println("Adding key to " + network.NetID)
 
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"accesskeys", network.AccessKeys},
-                }},
-        }
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"accesskeys", network.AccessKeys},
+		}},
+	}
 
-        errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
+	errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
 
-        defer cancel()
+	defer cancel()
 
-        if errN != nil {
-                return "", errN
-        }
-        return accesskey.AccessString, nil
+	if errN != nil {
+		return "", errN
+	}
+	return accesskey.AccessString, nil
 }
 
-func IsFieldUnique(network string,  field string, value string) bool {
+func IsFieldUnique(network string, field string, value string) bool {
 
 	var node models.Node
 	isunique := true
 
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 	filter := bson.M{field: value, "network": network}
 
-        err := collection.FindOne(ctx, filter).Decode(&node)
+	err := collection.FindOne(ctx, filter).Decode(&node)
 
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
+	if err != nil {
 		return isunique
-        }
+	}
 
-        if (node.Name != "") {
-                isunique = false
-        }
+	if node.Name != "" {
+		isunique = false
+	}
 
-        return isunique
+	return isunique
 }
 
 func NetworkExists(name string) (bool, error) {
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"netid": name}
+	filter := bson.M{"netid": name}
 
 	var result bson.M
-        err := collection.FindOne(ctx, filter).Decode(&result)
+	err := collection.FindOne(ctx, filter).Decode(&result)
 
 	defer cancel()
 
@@ -143,100 +142,101 @@ func NetworkExists(name string) (bool, error) {
 //for each node, it gets a unique address. That requires checking against all other nodes once more
 func UpdateNetworkNodeAddresses(networkName string) error {
 
-        //Connection mongoDB with mongoconn class
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	//Connection mongoDB with mongoconn class
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"network": networkName}
-        cur, err := collection.Find(ctx, filter)
+	filter := bson.M{"network": networkName}
+	cur, err := collection.Find(ctx, filter)
 
-        if err != nil {
-                return err
-        }
+	if err != nil {
+		return err
+	}
 
-        defer cancel()
+	defer cancel()
 
-        for cur.Next(context.TODO()) {
+	for cur.Next(context.TODO()) {
 
-                var node models.Node
+		var node models.Node
 
-                err := cur.Decode(&node)
+		err := cur.Decode(&node)
 		if err != nil {
 			fmt.Println("error in node address assignment!")
-                        return err
-                }
+			return err
+		}
 		ipaddr, iperr := UniqueAddress(networkName)
-                if iperr != nil {
+		if iperr != nil {
 			fmt.Println("error in node  address assignment!")
-                        return iperr
-                }
+			return iperr
+		}
 
 		filter := bson.M{"macaddress": node.MacAddress}
-	        update := bson.D{{"$set", bson.D{{"address", ipaddr}}}}
+		update := bson.D{{"$set", bson.D{{"address", ipaddr}}}}
 
 		errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
 
 		defer cancel()
-	        if errN != nil {
+		if errN != nil {
 			return errN
 		}
-        }
+	}
 
-        return err
+	return err
 }
+
 //TODO TODO TODO!!!!!
 func UpdateNetworkPrivateAddresses(networkName string) error {
 
-        //Connection mongoDB with mongoconn class
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	//Connection mongoDB with mongoconn class
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"network": networkName}
-        cur, err := collection.Find(ctx, filter)
+	filter := bson.M{"network": networkName}
+	cur, err := collection.Find(ctx, filter)
 
-        if err != nil {
-                return err
-        }
+	if err != nil {
+		return err
+	}
 
-        defer cancel()
+	defer cancel()
 
-        for cur.Next(context.TODO()) {
+	for cur.Next(context.TODO()) {
 
-                var node models.Node
+		var node models.Node
 
-                err := cur.Decode(&node)
-                if err != nil {
-                        fmt.Println("error in node address assignment!")
-                        return err
-                }
-                ipaddr, iperr := UniqueAddress(networkName)
-                if iperr != nil {
-                        fmt.Println("error in node  address assignment!")
-                        return iperr
-                }
+		err := cur.Decode(&node)
+		if err != nil {
+			fmt.Println("error in node address assignment!")
+			return err
+		}
+		ipaddr, iperr := UniqueAddress(networkName)
+		if iperr != nil {
+			fmt.Println("error in node  address assignment!")
+			return iperr
+		}
 
-                filter := bson.M{"macaddress": node.MacAddress}
-                update := bson.D{{"$set", bson.D{{"address", ipaddr}}}}
+		filter := bson.M{"macaddress": node.MacAddress}
+		update := bson.D{{"$set", bson.D{{"address", ipaddr}}}}
 
-                errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
+		errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
 
-                defer cancel()
-                if errN != nil {
-                        return errN
-                }
-        }
+		defer cancel()
+		if errN != nil {
+			return errN
+		}
+	}
 
-        return err
+	return err
 }
 
 //Checks to see if any other networks have the same name (id)
-func IsNetworkNameUnique(name string) (bool, error ){
+func IsNetworkNameUnique(name string) (bool, error) {
 
-        isunique := true
+	isunique := true
 
-        dbs, err := ListNetworks()
+	dbs, err := ListNetworks()
 
 	if err != nil {
 		return false, err
@@ -249,92 +249,90 @@ func IsNetworkNameUnique(name string) (bool, error ){
 		}
 	}
 
-        return isunique, nil
+	return isunique, nil
 }
 
-func IsNetworkDisplayNameUnique(name string) (bool, error){
+func IsNetworkDisplayNameUnique(name string) (bool, error) {
 
-        isunique := true
-
-        dbs, err := ListNetworks()
-        if err != nil {
-                return false, err
-        }
+	isunique := true
 
+	dbs, err := ListNetworks()
+	if err != nil {
+		return false, err
+	}
 
-        for i := 0; i < len(dbs); i++ {
+	for i := 0; i < len(dbs); i++ {
 
-                if name == dbs[i].DisplayName {
-                        isunique = false
-                }
-        }
+		if name == dbs[i].DisplayName {
+			isunique = false
+		}
+	}
 
-        return isunique, nil
+	return isunique, nil
 }
 
-func GetNetworkNodeNumber(networkName string) (int,  error){
+func GetNetworkNodeNumber(networkName string) (int, error) {
 
-        collection := mongoconn.Client.Database("wirecat").Collection("nodes")
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"network": networkName}
-        count, err := collection.CountDocuments(ctx, filter)
+	filter := bson.M{"network": networkName}
+	count, err := collection.CountDocuments(ctx, filter)
 	returncount := int(count)
 
 	//not sure if this is the right way of handling this error...
-        if err != nil {
-                return 9999, err
-        }
+	if err != nil {
+		return 9999, err
+	}
 
-        defer cancel()
+	defer cancel()
 
-        return returncount, err
+	return returncount, err
 }
 
-
 //Kind  of a weird name. Should just be GetNetworks I think. Consider changing.
 //Anyway, returns all the networks
-func ListNetworks() ([]models.Network, error){
+func ListNetworks() ([]models.Network, error) {
 
-        var networks []models.Network
+	var networks []models.Network
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0}))
+	cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0}))
 
-        if err != nil {
-                return networks, err
-        }
+	if err != nil {
+		return networks, err
+	}
 
-        defer cancel()
+	defer cancel()
 
-        for cur.Next(context.TODO()) {
+	for cur.Next(context.TODO()) {
 
-                var network models.Network
-                err := cur.Decode(&network)
-                if err != nil {
+		var network models.Network
+		err := cur.Decode(&network)
+		if err != nil {
 			return networks, err
-                }
+		}
 
-                // add network our array
-                networks = append(networks, network)
-        }
+		// add network our array
+		networks = append(networks, network)
+	}
 
-        if err := cur.Err(); err != nil {
-                return networks, err
-        }
+	if err := cur.Err(); err != nil {
+		return networks, err
+	}
 
-        return networks, err
+	return networks, err
 }
 
 //Checks to see if access key is valid
 //Does so by checking against all keys and seeing if any have the same value
 //may want to hash values before comparing...consider this
 //TODO: No error handling!!!!
-func IsKeyValid(networkname string, keyvalue string) bool{
+func IsKeyValid(networkname string, keyvalue string) bool {
 
 	network, _ := GetParentNetwork(networkname)
 	var key models.AccessKey
@@ -342,48 +340,49 @@ func IsKeyValid(networkname string, keyvalue string) bool{
 	isvalid := false
 
 	for i := len(network.AccessKeys) - 1; i >= 0; i-- {
-              currentkey:= network.AccessKeys[i]
-              if currentkey.Value == keyvalue {
+		currentkey := network.AccessKeys[i]
+		if currentkey.Value == keyvalue {
 			key = currentkey
 			foundkey = true
 		}
 	}
 	if foundkey {
 		if key.Uses > 0 {
-			isvalid  = true
+			isvalid = true
 		}
 	}
 	return isvalid
 }
+
 //TODO: Contains a fatal error return. Need to change
 //This just gets a network object from a network name
-//Should probably just be GetNetwork. kind of a dumb name. 
+//Should probably just be GetNetwork. kind of a dumb name.
 //Used in contexts where it's not the Parent network.
 func GetParentNetwork(networkname string) (models.Network, error) {
 
-        var network models.Network
+	var network models.Network
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"netid": networkname}
-        err := collection.FindOne(ctx, filter).Decode(&network)
+	filter := bson.M{"netid": networkname}
+	err := collection.FindOne(ctx, filter).Decode(&network)
 
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
-                return network, err
-        }
+	if err != nil {
+		return network, err
+	}
 
-        return network, nil
+	return network, nil
 }
 
 //Check for valid IPv4 address
 //Note: We dont handle IPv6 AT ALL!!!!! This definitely is needed at some point
 //But for iteration 1, lets just stick to IPv4. Keep it simple stupid.
 func IsIpv4Net(host string) bool {
-   return net.ParseIP(host) != nil
+	return net.ParseIP(host) != nil
 }
 
 //Similar to above but checks if Cidr range is valid
@@ -391,15 +390,15 @@ func IsIpv4Net(host string) bool {
 //still not good error handling
 func IsIpv4CIDR(host string) bool {
 
-   ip, ipnet, err := net.ParseCIDR(host)
+	ip, ipnet, err := net.ParseCIDR(host)
 
 	if err != nil {
-                fmt.Println(err)
-                fmt.Println("Address Range is not valid!")
-                return false
-        }
+		fmt.Println(err)
+		fmt.Println("Address Range is not valid!")
+		return false
+	}
 
-   return ip != nil && ipnet != nil
+	return ip != nil && ipnet != nil
 }
 
 //This is used to validate public keys (make sure they're base64 encoded like all public keys should be).
@@ -414,28 +413,28 @@ func IsBase64(s string) bool {
 //TODO: Check references. This seems unnecessary.
 func GetNodeObj(id primitive.ObjectID) models.Node {
 
-        var node models.Node
+	var node models.Node
 
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"_id": id}
-        err := collection.FindOne(ctx, filter).Decode(&node)
+	filter := bson.M{"_id": id}
+	err := collection.FindOne(ctx, filter).Decode(&node)
 
 	defer cancel()
 
-        if err != nil {
-                fmt.Println(err)
+	if err != nil {
+		fmt.Println(err)
 		fmt.Println("Did not get the node...")
-                return node
-        }
+		return node
+	}
 	fmt.Println("Got node " + node.Name)
-        return node
+	return node
 }
 
 //This  checks to  make sure a network name is valid.
 //Switch to REGEX?
-func NameInNetworkCharSet(name string) bool{
+func NameInNetworkCharSet(name string) bool {
 
 	charset := "abcdefghijklmnopqrstuvwxyz1234567890-_"
 
@@ -447,26 +446,25 @@ func NameInNetworkCharSet(name string) bool{
 	return true
 }
 
-func NameInNodeCharSet(name string) bool{
+func NameInNodeCharSet(name string) bool {
 
-        charset := "abcdefghijklmnopqrstuvwxyz1234567890-"
+	charset := "abcdefghijklmnopqrstuvwxyz1234567890-"
 
-        for _, char := range name {
-                if !strings.Contains(charset, strings.ToLower(string(char))) {
-                        return false
-                }
-        }
-        return true
+	for _, char := range name {
+		if !strings.Contains(charset, strings.ToLower(string(char))) {
+			return false
+		}
+	}
+	return true
 }
 
-
 //This returns a node based on its mac address.
 //The mac address acts as the Unique ID for nodes.
 //Is this a dumb thing to do? I thought it was cool but maybe it's dumb.
 //It doesn't really provide a tangible benefit over a random ID
 func GetNodeByMacAddress(network string, macaddress string) (models.Node, error) {
 
-        var node models.Node
+	var node models.Node
 
 	filter := bson.M{"macaddress": macaddress, "network": network}
 
@@ -474,14 +472,14 @@ func GetNodeByMacAddress(network string, macaddress string) (models.Node, error)
 
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        err := collection.FindOne(ctx, filter).Decode(&node)
+	err := collection.FindOne(ctx, filter).Decode(&node)
 
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
-                return node, err
-        }
-        return node, nil
+	if err != nil {
+		return node, err
+	}
+	return node, nil
 }
 
 //This returns a unique address for a node to use
@@ -489,14 +487,14 @@ func GetNodeByMacAddress(network string, macaddress string) (models.Node, error)
 //and checks against all nodes to see if it's taken, until it finds one.
 //TODO: We do not handle a case where we run out of addresses.
 //We will need to handle that eventually
-func UniqueAddress(networkName string) (string, error){
+func UniqueAddress(networkName string) (string, error) {
 
 	var network models.Network
 	network, err := GetParentNetwork(networkName)
-        if err != nil {
-                fmt.Println("UniqueAddress encountered  an error")
-                return "666", err
-        }
+	if err != nil {
+		fmt.Println("UniqueAddress encountered  an error")
+		return "666", err
+	}
 
 	offset := true
 	ip, ipnet, err := net.ParseCIDR(network.AddressRange)
@@ -509,7 +507,7 @@ func UniqueAddress(networkName string) (string, error){
 			offset = false
 			continue
 		}
-		if IsIPUnique(networkName, ip.String()){
+		if IsIPUnique(networkName, ip.String()) {
 			return ip.String(), err
 		}
 	}
@@ -523,46 +521,44 @@ func GetGlobalConfig() (bool, models.GlobalConfig, error) {
 
 	create := false
 
-        filter := bson.M{}
+	filter := bson.M{}
 
-        var globalconf models.GlobalConfig
+	var globalconf models.GlobalConfig
 
-        collection := mongoconn.Client.Database("netmaker").Collection("config")
+	collection := mongoconn.Client.Database("netmaker").Collection("config")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        err := collection.FindOne(ctx, filter).Decode(&globalconf)
+	err := collection.FindOne(ctx, filter).Decode(&globalconf)
 
-        defer cancel()
+	defer cancel()
 
 	if err == mongo.ErrNoDocuments {
-                fmt.Println("Global config does not exist. Need to create.")
+		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 create, globalconf, err
-        }
+		fmt.Println(err)
+		fmt.Println("Could not get global config")
+		return create, globalconf, err
+	}
 	return create, globalconf, err
 }
 
-
-
 //generate an access key value
 func GenKey() string {
 
-  var seededRand *rand.Rand = rand.New(
-  rand.NewSource(time.Now().UnixNano()))
+	var seededRand *rand.Rand = rand.New(
+		rand.NewSource(time.Now().UnixNano()))
 
-  length := 16
-  charset := "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+	length := 16
+	charset := "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 
-  b := make([]byte, length)
-  for i := range b {
-    b[i] = charset[seededRand.Intn(len(charset))]
-  }
-  return string(b)
+	b := make([]byte, length)
+	for i := range b {
+		b[i] = charset[seededRand.Intn(len(charset))]
+	}
+	return string(b)
 }
 
 //generate a key value
@@ -571,17 +567,17 @@ func GenKey() string {
 //have a "base string" a "length" and a "charset"
 func GenKeyName() string {
 
-  var seededRand *rand.Rand = rand.New(
-  rand.NewSource(time.Now().UnixNano()))
+	var seededRand *rand.Rand = rand.New(
+		rand.NewSource(time.Now().UnixNano()))
 
-  length := 5
-  charset := "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+	length := 5
+	charset := "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 
-  b := make([]byte, length)
-  for i := range b {
-    b[i] = charset[seededRand.Intn(len(charset))]
-  }
-  return "key-" + string(b)
+	b := make([]byte, length)
+	for i := range b {
+		b[i] = charset[seededRand.Intn(len(charset))]
+	}
+	return "key-" + string(b)
 }
 
 //checks if IP is unique in the address range
@@ -592,21 +588,21 @@ func IsIPUnique(network string, ip string) bool {
 
 	isunique := true
 
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 	filter := bson.M{"address": ip, "network": network}
 
-        err := collection.FindOne(ctx, filter).Decode(&node)
+	err := collection.FindOne(ctx, filter).Decode(&node)
 
 	defer cancel()
 
-        if err != nil {
-                fmt.Println(err)
-                return isunique
-        }
+	if err != nil {
+		fmt.Println(err)
+		return isunique
+	}
 
-	if (node.Address == ip) {
+	if node.Address == ip {
 		isunique = false
 	}
 	return isunique
@@ -616,77 +612,79 @@ func IsIPUnique(network string, ip string) bool {
 //reduces value by one and deletes if necessary
 func DecrimentKey(networkName string, keyvalue string) {
 
-        var network models.Network
+	var network models.Network
 
 	network, err := GetParentNetwork(networkName)
-        if err != nil {
-                return
-        }
+	if err != nil {
+		return
+	}
 
-        for i := len(network.AccessKeys) - 1; i >= 0; i-- {
+	for i := len(network.AccessKeys) - 1; i >= 0; i-- {
 
-                currentkey := network.AccessKeys[i]
-                if currentkey.Value == keyvalue {
-                        network.AccessKeys[i].Uses--
+		currentkey := network.AccessKeys[i]
+		if currentkey.Value == keyvalue {
+			network.AccessKeys[i].Uses--
 			if network.AccessKeys[i].Uses < 1 {
 				//this is the part where it will call the delete
 				//not sure if there's edge cases I'm missing
 				DeleteKey(network, i)
 				return
 			}
-                }
-        }
+		}
+	}
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"netid": network.NetID}
+	filter := bson.M{"netid": network.NetID}
 
-        update := bson.D{
-                {"$set", bson.D{
-                        {"accesskeys", network.AccessKeys},
-                }},
-        }
-        errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
+	update := bson.D{
+		{"$set", bson.D{
+			{"accesskeys", network.AccessKeys},
+		}},
+	}
+	errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
 
-        defer cancel()
+	defer cancel()
 
-        if errN != nil {
-                return
-        }
+	if errN != nil {
+		return
+	}
 }
+
 //takes the logic from controllers.deleteKey
 func DeleteKey(network models.Network, i int) {
 
 	network.AccessKeys = append(network.AccessKeys[:i],
-                                network.AccessKeys[i+1:]...)
+		network.AccessKeys[i+1:]...)
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        // Create filter
-        filter := bson.M{"netid": network.NetID}
+	// Create filter
+	filter := bson.M{"netid": network.NetID}
 
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"accesskeys", network.AccessKeys},
-                }},
-        }
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"accesskeys", network.AccessKeys},
+		}},
+	}
 
-        errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
+	errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
 
-        defer cancel()
+	defer cancel()
 
-        if errN != nil {
-                return
-        }
+	if errN != nil {
+		return
+	}
 }
+
 //increments an IP over the previous
 func Inc(ip net.IP) {
-	for j := len(ip)-1; j>=0; j-- {
+	for j := len(ip) - 1; j >= 0; j-- {
 		ip[j]++
 		if ip[j] > 0 {
 			break

+ 28 - 18
test/api_test.go

@@ -216,22 +216,14 @@ func networkExists(t *testing.T) bool {
 	return false
 }
 
+func TestJunk(t *testing.T) {
+	deleteNetworks(t)
+}
+
 func deleteNetworks(t *testing.T) {
-	//delete all network nodes
-	var nodes []models.ReturnNode
-	response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/skynet", "secretkey")
-	assert.Nil(t, err, err)
-	if response.StatusCode == http.StatusOK {
-		err = json.NewDecoder(response.Body).Decode(&nodes)
-		response.Body.Close()
-		assert.Nil(t, err, err)
-		for _, node := range nodes {
-			resp, err := api(t, "", http.MethodDelete, baseURL+"/api/nodes/skynet/"+node.MacAddress, "secretkey")
-			assert.Nil(t, err, err)
-			assert.Equal(t, http.StatusOK, resp.StatusCode)
-		}
-	}
-	response, err = api(t, "", http.MethodGet, baseURL+"/api/networks", "secretkey")
+	//delete all node
+	deleteAllNodes(t)
+	response, err := api(t, "", http.MethodGet, baseURL+"/api/networks", "secretkey")
 	assert.Nil(t, err, err)
 	if response.StatusCode == http.StatusOK {
 		defer response.Body.Close()
@@ -260,12 +252,24 @@ func getNetworkNodes(t *testing.T) []models.ReturnNode {
 	return nodes
 }
 
-func deleteNode(t *testing.T, node models.ReturnNode) {
-	response, err := api(t, "", http.MethodDelete, baseURL+"/api/nodes/skynet/"+node.MacAddress, "secretkey")
+func deleteNode(t *testing.T) {
+	response, err := api(t, "", http.MethodDelete, baseURL+"/api/nodes/skynet/01:02:03:04:05:06", "secretkey")
 	assert.Nil(t, err, err)
 	assert.Equal(t, http.StatusOK, response.StatusCode)
 }
-
+func deleteAllNodes(t *testing.T) {
+	response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes", "secretkey")
+	assert.Nil(t, err, err)
+	assert.Equal(t, http.StatusOK, response.StatusCode)
+	var nodes []models.ReturnNode
+	defer response.Body.Close()
+	json.NewDecoder(response.Body).Decode(&nodes)
+	for _, node := range nodes {
+		resp, err := api(t, "", http.MethodDelete, baseURL+"/api/nodes/"+node.Network+"/"+node.MacAddress, "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, resp.StatusCode)
+	}
+}
 func createNode(t *testing.T) {
 	var node models.Node
 	key := createAccessKey(t)
@@ -291,3 +295,9 @@ func getNode(t *testing.T) models.Node {
 	assert.Nil(t, err, err)
 	return node
 }
+
+func setup(t *testing.T) {
+	deleteNetworks(t)
+	createNetwork(t)
+	createNode(t)
+}

+ 28 - 13
test/group_test.go

@@ -8,7 +8,6 @@ import (
 
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
-	"go.mongodb.org/mongo-driver/mongo"
 )
 
 func TestCreateNetwork(t *testing.T) {
@@ -118,6 +117,7 @@ func TestGetNetwork(t *testing.T) {
 func TestDeleteNetwork(t *testing.T) {
 
 	t.Run("InvalidKey", func(t *testing.T) {
+		setup(t)
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet", "badkey")
 		assert.Nil(t, err, err)
 		defer response.Body.Close()
@@ -128,17 +128,6 @@ func TestDeleteNetwork(t *testing.T) {
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
 	})
-	t.Run("ValidKey", func(t *testing.T) {
-		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message mongo.DeleteResult
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		assert.Equal(t, int64(1), message.DeletedCount)
-
-	})
 	t.Run("Badnetwork", func(t *testing.T) {
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/badnetwork", "secretkey")
 		assert.Nil(t, err, err)
@@ -150,7 +139,33 @@ func TestDeleteNetwork(t *testing.T) {
 		assert.Equal(t, http.StatusNotFound, response.StatusCode)
 	})
 	t.Run("NodesExist", func(t *testing.T) {
-
+		setup(t)
+		node := getNode(t)
+		t.Log(node)
+		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusForbidden, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Contains(t, message.Message, "Node check failed")
+		assert.Equal(t, http.StatusForbidden, message.Code)
+	})
+	t.Run("ValidKey", func(t *testing.T) {
+		type Message struct {
+			DeletedCount int64
+		}
+		setup(t)
+		deleteAllNodes(t)
+		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		var message Message
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		assert.Equal(t, int64(1), message.DeletedCount)
 	})
 }
 

+ 518 - 103
test/node_test.go

@@ -4,75 +4,214 @@ import (
 	"encoding/json"
 	"net/http"
 	"testing"
+	"time"
 
 	"github.com/gravitl/netmaker/models"
 	"github.com/stretchr/testify/assert"
+	"golang.org/x/crypto/bcrypt"
 )
 
-func setup(t *testing.T) {
-	deleteNetworks(t)
-	createNetwork(t)
-	createNode(t)
-}
-
-func TestJunk(t *testing.T) {
-	deleteNetworks(t)
+func TestRainyDay(t *testing.T) {
+	t.Run("badkey", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes", "badkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+	})
+	t.Run("badURL", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/adm/skynet/01:02:03:04:05:07", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
+	t.Run("NonExistentNetwork", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/badnet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
 }
 
 func TestGetAllNodes(t *testing.T) {
 	setup(t)
-	response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes", "secretkey")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	var nodes []models.ReturnNode
-	defer response.Body.Close()
-	err = json.NewDecoder(response.Body).Decode(&nodes)
-	assert.Nil(t, err, err)
-	for _, node := range nodes {
-		assert.NotNil(t, node, "empty node")
-	}
+	t.Run("NodesExist", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		var nodes []models.ReturnNode
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&nodes)
+		assert.Nil(t, err, err)
+		assert.NotEmpty(t, nodes)
+	})
+	t.Run("NodeDoesNotExist", func(t *testing.T) {
+		deleteNode(t)
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		var nodes []models.ReturnNode
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&nodes)
+		assert.Nil(t, err, err)
+		assert.Empty(t, nodes)
+	})
 }
 
 func TestGetNetworkNodes(t *testing.T) {
 	setup(t)
-	response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/skynet", "secretkey")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	var nodes []models.ReturnNode
-	defer response.Body.Close()
-	err = json.NewDecoder(response.Body).Decode(&nodes)
-	assert.Nil(t, err, err)
-	for _, node := range nodes {
-		assert.NotNil(t, node, "empty node")
-	}
+	t.Run("NodeExists", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		var nodes []models.ReturnNode
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&nodes)
+		assert.Nil(t, err, err)
+		assert.NotEmpty(t, nodes)
+	})
+	t.Run("NodeDoesNotExit", func(t *testing.T) {
+		deleteNode(t)
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		var nodes []models.ReturnNode
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&nodes)
+		assert.Nil(t, err, err)
+		assert.Empty(t, nodes)
+	})
 }
 
 func TestGetNode(t *testing.T) {
 	setup(t)
-	response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/skynet/01:02:03:04:05:06", "secretkey")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	var node models.Node
-	defer response.Body.Close()
-	err = json.NewDecoder(response.Body).Decode(&node)
-	assert.Nil(t, err, err)
-	assert.Equal(t, "01:02:03:04:05:06", node.MacAddress)
+	t.Run("NodeExists", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/skynet/01:02:03:04:05:06", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		var node models.Node
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&node)
+		assert.Nil(t, err, err)
+		assert.Equal(t, "01:02:03:04:05:06", node.MacAddress)
+	})
+	t.Run("NodeDoesNotExist", func(t *testing.T) {
+		deleteNode(t)
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/skynet/01:02:03:04:05:06", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusInternalServerError, response.StatusCode)
+	})
 }
 
 func TestUpdateNode(t *testing.T) {
+	var data models.Node
 	setup(t)
-	var data struct {
-		Name string
-	}
-	data.Name = "NewName"
-	response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:06", "secretkey")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	var node models.Node
-	defer response.Body.Close()
-	err = json.NewDecoder(response.Body).Decode(&node)
-	assert.Nil(t, err, err)
-	assert.Equal(t, data.Name, node.Name)
+
+	t.Run("UpdateMulti", func(t *testing.T) {
+		data.MacAddress = "01:02:03:04:05:05"
+		data.Name = "NewName"
+		data.PublicKey = "DM5qhLAE20PG9BbfBCgfr+Ac9D2NDOwCtY1rbYDLf34="
+		data.Password = "newpass"
+		data.LocalAddress = "192.168.0.2"
+		data.Endpoint = "10.100.100.5"
+		response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:06", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		var node models.Node
+		t.Log(response.Body)
+		err = json.NewDecoder(response.Body).Decode(&node)
+		assert.Nil(t, err, err)
+		assert.Equal(t, data.Name, node.Name)
+		assert.Equal(t, data.PublicKey, node.PublicKey)
+		err = bcrypt.CompareHashAndPassword([]byte(node.Password), []byte(data.Password))
+		assert.Nil(t, err, err)
+		assert.Equal(t, data.LocalAddress, node.LocalAddress)
+		assert.Equal(t, data.Endpoint, node.Endpoint)
+	})
+	t.Run("InvalidMacAddress", func(t *testing.T) {
+		data.MacAddress = "10:11:12:13:14:15:16"
+		response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:05", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'MacAddress' failed")
+	})
+	t.Run("InvalidEndpoint", func(t *testing.T) {
+		data.Endpoint = "10.10.10.300"
+		response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:05", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'Endpoint' failed")
+	})
+	t.Run("InvalidLocalAddress", func(t *testing.T) {
+		data.LocalAddress = "10.10.10.300"
+		response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:05", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'LocalAddress' failed")
+	})
+	t.Run("InvalidName", func(t *testing.T) {
+		var data struct {
+			Name string
+		}
+		data.Name = "New*Name"
+		response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:05", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'Name' failed")
+	})
+	t.Run("InvalidPublicKey", func(t *testing.T) {
+		data.PublicKey = "xxx"
+		response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:05", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'PublicKey' failed")
+	})
+	t.Run("InvalidPassword", func(t *testing.T) {
+		data.Password = "1234"
+		response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:05", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'Password' failed")
+	})
+	t.Run("EmptyPassword", func(t *testing.T) {
+		data.Password = ""
+		response, err := api(t, data, http.MethodPut, baseURL+"/api/nodes/skynet/01:02:03:04:05:05", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'Password' failed")
+	})
 }
 
 func TestDeleteNode(t *testing.T) {
@@ -103,33 +242,75 @@ func TestDeleteNode(t *testing.T) {
 }
 
 func TestCheckIn(t *testing.T) {
-	//get node
-	//oldNode := getNode(t)
 	setup(t)
-	response, err := api(t, "", http.MethodPost, baseURL+"/api/nodes/skynet/01:02:03:04:05:06/checkin", "secretkey")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	var node models.Node
-	defer response.Body.Close()
-	err = json.NewDecoder(response.Body).Decode(&node)
-	assert.Nil(t, err, err)
-	//assert.Greater(t, node.LastCheckIn, oldNode.LastCheckin)
+	oldNode := getNode(t)
+	time.Sleep(1 * time.Second)
+	t.Run("Valid", func(t *testing.T) {
+		response, err := api(t, "", http.MethodPost, baseURL+"/api/nodes/skynet/01:02:03:04:05:06/checkin", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		var node models.Node
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&node)
+		assert.Nil(t, err, err)
+		assert.Greater(t, node.LastCheckIn, oldNode.LastCheckIn)
+	})
+	t.Run("NodeDoesNotExist", func(t *testing.T) {
+		deleteNode(t)
+		response, err := api(t, "", http.MethodPost, baseURL+"/api/nodes/skynet/01:02:03:04:05:06/checkin", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusInternalServerError, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusInternalServerError, message.Code)
+		assert.Equal(t, "mongo: no documents in result", message.Message)
+	})
 }
 
 func TestCreateGateway(t *testing.T) {
 	setup(t)
 	//assert.False(t, node.IsGateway)
 	var gateway models.GatewayRequest
-	gateway.RangeString = "0.0.0.0/0"
-	gateway.Interface = "eth0"
-	response, err := api(t, gateway, http.MethodPost, baseURL+"/api/nodes/skynet/01:02:03:04:05:06/creategateway", "secretkey")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	defer response.Body.Close()
-	var message models.Node
-	err = json.NewDecoder(response.Body).Decode(&message)
-	assert.Nil(t, err, err)
-	assert.True(t, message.IsGateway)
+	t.Run("Valid", func(t *testing.T) {
+		gateway.RangeString = "0.0.0.0/0"
+		gateway.Interface = "eth0"
+		response, err := api(t, gateway, http.MethodPost, baseURL+"/api/nodes/skynet/01:02:03:04:05:06/creategateway", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		var message models.Node
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.True(t, message.IsGateway)
+	})
+	t.Run("BadRange", func(t *testing.T) {
+		gateway.RangeString = "0.0.0.0/36"
+		gateway.Interface = "eth0"
+		response, err := api(t, gateway, http.MethodPost, baseURL+"/api/nodes/skynet/01:02:03:04:05:06/creategateway", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusInternalServerError, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusInternalServerError, message.Code)
+		assert.Equal(t, "IP Range Not Valid", message.Message)
+	})
+	t.Run("BadInterface", func(t *testing.T) {
+		gateway.RangeString = "0.0.0.0/0"
+		gateway.Interface = ""
+		response, err := api(t, gateway, http.MethodPost, baseURL+"/api/nodes/skynet/01:02:03:04:05:06/creategateway", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusInternalServerError, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusInternalServerError, message.Code)
+		assert.Equal(t, "Interface cannot be empty", message.Message)
+	})
 }
 
 func TestDeleteGateway(t *testing.T) {
@@ -158,49 +339,283 @@ func TestUncordonNode(t *testing.T) {
 }
 
 func TestCreateNode(t *testing.T) {
-	deleteNetworks(t)
-	createNetwork(t)
+	setup(t)
 	key := createAccessKey(t)
+	t.Run("NodeExists", func(t *testing.T) {
+		var node models.Node
+		node.AccessKey = key.Value
+		node.MacAddress = "01:02:03:04:05:06"
+		node.Name = "myNode"
+		node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
+		node.Password = "tobedetermined"
+		node.LocalAddress = "192.168.0.1"
+		node.Endpoint = "10.100.100.4"
 
-	var node models.Node
-	node.AccessKey = key.Value
-	node.MacAddress = "01:02:03:04:05:06"
-	node.Name = "myNode"
-	node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
-	node.Password = "tobedetermined"
-	node.LocalAddress = "192.168.0.1"
-	node.Endpoint = "10.100.100.4"
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		assert.Contains(t, message.Message, "Field validation for 'MacAddress' failed on the 'macaddress_unique' tag")
+	})
+	t.Run("BadKey", func(t *testing.T) {
+		deleteNode(t)
+		var node models.Node
+		node.AccessKey = "badkey"
+		node.MacAddress = "01:02:03:04:05:06"
+		node.Name = "myNode"
+		node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
+		node.Password = "tobedetermined"
+		node.LocalAddress = "192.168.0.1"
+		node.Endpoint = "10.100.100.4"
+
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
+		assert.Contains(t, message.Message, "W1R3: Key invalid, or none provided")
+	})
+	t.Run("BadMac", func(t *testing.T) {
+		var node models.Node
+		node.AccessKey = key.Value
+		node.MacAddress = "badmac"
+		node.Name = "myNode"
+		node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
+		node.Password = "tobedetermined"
+		node.LocalAddress = "192.168.0.1"
+		node.Endpoint = "10.100.100.4"
+
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'MacAddress' failed on the 'macaddress_valid' tag")
+	})
+	t.Run("BadPublicKey", func(t *testing.T) {
+		var node models.Node
+		node.AccessKey = key.Value
+		node.MacAddress = "01:02:03:04:05:06"
+		node.Name = "myNode"
+		node.PublicKey = "xxx"
+		node.Password = "tobedetermined"
+		node.LocalAddress = "192.168.0.1"
+		node.Endpoint = "10.100.100.4"
+
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'PublicKey' failed")
+	})
+	t.Run("BadPass", func(t *testing.T) {
+		var node models.Node
+		node.AccessKey = key.Value
+		node.MacAddress = "01:02:03:04:05:06"
+		node.Name = "myNode"
+		node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
+		node.Password = ""
+		node.LocalAddress = "192.168.0.1"
+		node.Endpoint = "10.100.100.4"
+
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Error:Field validation for 'Password' failed")
+	})
+	t.Run("BadLocalAddress", func(t *testing.T) {
+		var node models.Node
+		node.AccessKey = key.Value
+		node.MacAddress = "01:02:03:04:05:06"
+		node.Name = "myNode"
+		node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
+		node.Password = "tobedetermined"
+		node.LocalAddress = "192.168.300.1"
+		node.Endpoint = "10.100.100.4"
+
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'LocalAddress' failed")
+	})
+	t.Run("BadEndpoint", func(t *testing.T) {
+		var node models.Node
+		node.AccessKey = key.Value
+		node.MacAddress = "01:02:03:04:05:06"
+		node.Name = "myNode"
+		node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
+		node.Password = "tobedetermined"
+		node.LocalAddress = "192.168.0.1"
+		node.Endpoint = "10.100.300.4"
+
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'Endpoint' failed")
+	})
+	t.Run("NetworkDoesNotExist", func(t *testing.T) {
+		deleteNetworks(t)
+		var node models.Node
+		node.AccessKey = key.Value
+		node.MacAddress = "01:02:03:04:05:06"
+		node.Name = "myNode"
+		node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
+		node.Password = "tobedetermined"
+		node.LocalAddress = "192.168.0.1"
+		node.Endpoint = "10.100.100.4"
+
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusNotFound, message.Code)
+		assert.Contains(t, "W1R3: Network does not exist! ", message.Message)
+	})
+	t.Run("Valid", func(t *testing.T) {
+		setup(t)
+		deleteNode(t)
+		key := createAccessKey(t)
+
+		var node models.Node
+		node.AccessKey = key.Value
+		node.MacAddress = "01:02:03:04:05:06"
+		node.Name = "myNode"
+		node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
+		node.Password = "tobedetermined"
+		node.LocalAddress = "192.168.0.1"
+		node.Endpoint = "10.100.100.4"
+
+		response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		var message models.Node
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, node.Name, message.Name)
+		t.Log(message)
+	})
 
-	response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	defer response.Body.Close()
-	var message models.Node
-	err = json.NewDecoder(response.Body).Decode(&message)
-	assert.Nil(t, err, err)
-	assert.Equal(t, node.Name, message.Name)
-	t.Log(message.Password)
 }
 
 func TestGetLastModified(t *testing.T) {
-	response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/adm/skynet/lastmodified", "secretkey")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	assert.NotNil(t, response.Body, "no time returned")
+	deleteNetworks(t)
+	createNetwork(t)
+	t.Run("Valid", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/adm/skynet/lastmodified", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+	})
+	deleteNetworks(t)
+	t.Run("NoNetwork", func(t *testing.T) {
+		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/adm/skynet/lastmodified", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+	})
 }
 
 func TestNodeAuthenticate(t *testing.T) {
 	setup(t)
-	var authRequest models.AuthParams
-	authRequest.MacAddress = "01:02:03:04:05:06"
-	authRequest.Password = "tobedetermined"
-	response, err := api(t, authRequest, http.MethodPost, "http://localhost:8081:/api/nodes/adm/skynet/authenticate", "")
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, response.StatusCode)
-	defer response.Body.Close()
-	var message models.SuccessResponse
-	err = json.NewDecoder(response.Body).Decode(&message)
-	assert.Nil(t, err, err)
-	assert.Equal(t, http.StatusOK, message.Code)
-	assert.Equal(t, "W1R3: Device 01:02:03:04:05:06 Authorized", message.Message)
+	t.Run("Valid", func(t *testing.T) {
+		var authRequest models.AuthParams
+		authRequest.MacAddress = "01:02:03:04:05:06"
+		authRequest.Password = "tobedetermined"
+		response, err := api(t, authRequest, http.MethodPost, "http://localhost:8081:/api/nodes/adm/skynet/authenticate", "")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+		defer response.Body.Close()
+		var message models.SuccessResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, message.Code)
+		assert.Equal(t, "W1R3: Device 01:02:03:04:05:06 Authorized", message.Message)
+	})
+	t.Run("MacEmpty", func(t *testing.T) {
+		var authRequest models.AuthParams
+		authRequest.MacAddress = ""
+		authRequest.Password = "tobedetermined"
+		response, err := api(t, authRequest, http.MethodPost, "http://localhost:8081:/api/nodes/adm/skynet/authenticate", "")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.SuccessResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Equal(t, "W1R3: MacAddress can't be empty", message.Message)
+	})
+	t.Run("EmptyPass", func(t *testing.T) {
+		var authRequest models.AuthParams
+		authRequest.MacAddress = "01:02:03:04:05:06"
+		authRequest.Password = ""
+		response, err := api(t, authRequest, http.MethodPost, "http://localhost:8081:/api/nodes/adm/skynet/authenticate", "")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.SuccessResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Equal(t, "W1R3: Password can't be empty", message.Message)
+	})
+	t.Run("BadPass", func(t *testing.T) {
+		var authRequest models.AuthParams
+		authRequest.MacAddress = "01:02:03:04:05:06"
+		authRequest.Password = "badpass"
+		response, err := api(t, authRequest, http.MethodPost, "http://localhost:8081:/api/nodes/adm/skynet/authenticate", "")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.SuccessResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Equal(t, "crypto/bcrypt: hashedPassword is not the hash of the given password", message.Message)
+	})
+	t.Run("BadMac", func(t *testing.T) {
+		var authRequest models.AuthParams
+		authRequest.MacAddress = "01:02:03:04:05:07"
+		authRequest.Password = "tobedetermined"
+		response, err := api(t, authRequest, http.MethodPost, "http://localhost:8081:/api/nodes/adm/skynet/authenticate", "")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.SuccessResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Equal(t, "mongo: no documents in result", message.Message)
+	})
 }