Browse Source

refactor node deletion

Matthew R. Kasun 2 years ago
parent
commit
4df672de68
7 changed files with 100 additions and 67 deletions
  1. 30 40
      controllers/node.go
  2. 1 1
      logic/networks.go
  3. 64 23
      logic/nodes.go
  4. 2 2
      logic/server.go
  5. 1 1
      logic/zombie.go
  6. 1 0
      main.go
  7. 1 0
      models/node.go

+ 30 - 40
controllers/node.go

@@ -665,7 +665,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 		}
 		if !updatedUserNode { // user was found but not updated, so delete node
 			logger.Log(0, "failed to add node to user", keyName)
-			logic.DeleteNodeByID(&node, true)
+			logic.DeleteNode(&node, true)
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			return
 		}
@@ -1049,22 +1049,11 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	var nodeid = params["nodeid"]
 	fromNode := r.Header.Get("requestfrom") == "node"
-	var node, err = logic.GetNodeByID(nodeid)
+	node, err := logic.GetNodeByID(nodeid)
 	if err != nil {
-		if fromNode {
-			node, err = logic.GetDeletedNodeByID(nodeid)
-			if err != nil {
-				logger.Log(0, r.Header.Get("user"),
-					fmt.Sprintf("error fetching node from deleted nodes [ %s ] info: %v", nodeid, err))
-				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-				return
-			}
-		} else {
-			logger.Log(0, r.Header.Get("user"),
-				fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-			return
-		}
+		logger.Log(0, "error retrieving node to delete", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
 	}
 	if isServer(&node) {
 		err := fmt.Errorf("cannot delete server node")
@@ -1080,34 +1069,35 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
-	//send update to node to be deleted before deleting on server otherwise message cannot be sent
-	node.Action = models.NODE_DELETE
-
-	err = logic.DeleteNodeByID(&node, fromNode)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+	if err := logic.DeleteNode(&node, fromNode); err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
 		return
 	}
 	if fromNode {
-		// deletes node related role and client
-		event := mq.MqDynsecPayload{
-			Commands: []mq.MqDynSecCmd{
-				{
-					Command:  mq.DeleteClientCmd,
-					Username: nodeid,
-				},
-			},
-		}
-
-		if err := mq.PublishEventToDynSecTopic(event); err != nil {
-			logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
-				event.Commands, err.Error()))
+		//check if server should be removed from mq
+		found := false
+		// err is irrelevent
+		nodes, _ := logic.GetAllNodes()
+		for _, nodetocheck := range nodes {
+			if nodetocheck.HostID == node.HostID {
+				found = true
+				break
+			}
 		}
-	}
-
-	if servercfg.Is_EE {
-		if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
-			logger.Log(0, "failed to reset failover lists during node delete for node", node.Name, node.Network)
+		if !found {
+			// deletes node related role and client
+			event := mq.MqDynsecPayload{
+				Commands: []mq.MqDynSecCmd{
+					{
+						Command:  mq.DeleteClientCmd,
+						Username: node.HostID,
+					},
+				},
+			}
+			if err := mq.PublishEventToDynSecTopic(event); err != nil {
+				logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
+					event.Commands, err.Error()))
+			}
 		}
 	}
 

+ 1 - 1
logic/networks.go

@@ -54,7 +54,7 @@ func DeleteNetwork(network string) error {
 		servers, err := GetSortedNetworkServerNodes(network)
 		if err == nil {
 			for _, s := range servers {
-				if err = DeleteNodeByID(&s, true); err != nil {
+				if err = DeleteNode(&s, true); err != nil {
 					logger.Log(2, "could not removed server", s.Name, "before deleting network", network)
 				} else {
 					logger.Log(2, "removed server", s.Name, "before deleting network", network)

+ 64 - 23
logic/nodes.go

@@ -1,6 +1,7 @@
 package logic
 
 import (
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -22,8 +23,14 @@ import (
 	"golang.org/x/crypto/bcrypt"
 )
 
-// RELAY_NODE_ERR - error to return if relay node is unfound
-const RELAY_NODE_ERR = "could not find relay for node"
+const (
+	// RELAY_NODE_ERR - error to return if relay node is unfound
+	RELAY_NODE_ERR = "could not find relay for node"
+	// NodePurgeTime time to wait for node to response to a NODE_DELETE actions
+	NodePurgeTime = time.Second * 10
+	// NodePurgeCheckTime is how often to check nodes for Pending Delete
+	NodePurgeCheckTime = time.Second * 30
+)
 
 // GetNetworkNodes - gets the nodes of a network
 func GetNetworkNodes(network string) ([]models.Node, error) {
@@ -160,8 +167,31 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 	return fmt.Errorf("failed to update node " + currentNode.ID + ", cannot change ID.")
 }
 
-// DeleteNodeByID - deletes a node from database or moves into delete nodes table
-func DeleteNodeByID(node *models.Node, exterminate bool) error {
+// DeleteNode - marks node for deletion if called by UI or deletes node if called by node
+func DeleteNode(node *models.Node, purge bool) error {
+	if !purge {
+		newnode := node
+		newnode.PendingDelete = true
+		newnode.Action = models.NODE_DELETE
+		if err := UpdateNode(node, newnode); err != nil {
+			return err
+		}
+		return nil
+	}
+	if err := DeleteNodeByID(node); err != nil {
+		return err
+	}
+	if servercfg.Is_EE {
+		if err := EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
+			logger.Log(0, "failed to reset failover lists during node delete for node", node.Name, node.Network)
+		}
+	}
+
+	return nil
+}
+
+// DeleteNodeByID - deletes a node from database
+func DeleteNodeByID(node *models.Node) error {
 	var err error
 	var key = node.ID
 	//delete any ext clients as required
@@ -170,27 +200,11 @@ func DeleteNodeByID(node *models.Node, exterminate bool) error {
 			logger.Log(0, "failed to deleted ext clients", err.Error())
 		}
 	}
-	if !exterminate {
-		node.Action = models.NODE_DELETE
-		nodedata, err := json.Marshal(&node)
-		if err != nil {
-			return err
-		}
-		err = database.Insert(key, string(nodedata), database.DELETED_NODES_TABLE_NAME)
-		if err != nil {
-			return err
-		}
-	} else {
-		if err := database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, key); err != nil {
-			logger.Log(2, err.Error())
-		}
-	}
 	if err = database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
 		if !database.IsEmptyRecord(err) {
 			return err
 		}
 	}
-
 	if servercfg.IsDNSMode() {
 		SetDNS()
 	}
@@ -200,7 +214,6 @@ func DeleteNodeByID(node *models.Node, exterminate bool) error {
 			logger.Log(0, "failed to dissasociate", node.OwnerID, "from node", node.ID, ":", err.Error())
 		}
 	}
-
 	_, err = nodeacls.RemoveNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID))
 	if err != nil {
 		// ignoring for now, could hit a nil pointer if delete called twice
@@ -210,11 +223,9 @@ func DeleteNodeByID(node *models.Node, exterminate bool) error {
 	if err = DeleteMetrics(node.ID); err != nil {
 		logger.Log(1, "unable to remove metrics from DB for node", node.ID, err.Error())
 	}
-
 	if node.IsServer == "yes" {
 		return removeLocalServer(node)
 	}
-
 	return nil
 }
 
@@ -773,4 +784,34 @@ func updateProNodeACLS(node *models.Node) error {
 	return nil
 }
 
+func PurgePendingNodes(ctx context.Context) {
+	ticker := time.NewTicker(NodePurgeCheckTime)
+	defer ticker.Stop()
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-ticker.C:
+			nodes, err := GetAllNodes()
+			if err != nil {
+				logger.Log(0, "PurgePendingNodes failed to retrieve nodes", err.Error())
+				continue
+			}
+			for _, node := range nodes {
+				if node.PendingDelete {
+					modified := time.Unix(node.LastModified, 0)
+					if time.Since(modified) > NodePurgeTime {
+						if err := DeleteNode(&node, true); err != nil {
+							logger.Log(0, "failed to purge node", node.ID, err.Error())
+						} else {
+							logger.Log(0, "purged node ", node.ID)
+						}
+
+					}
+				}
+			}
+		}
+	}
+}
+
 // == END PRO ==

+ 2 - 2
logic/server.go

@@ -193,7 +193,7 @@ func ServerUpdate(serverNode *models.Node, ifaceDelta bool) error {
 
 	var err = ServerPull(serverNode, ifaceDelta)
 	if isDeleteError(err) {
-		return DeleteNodeByID(serverNode, true)
+		return DeleteNode(serverNode, true)
 	} else if err != nil && !ifaceDelta {
 		err = ServerPull(serverNode, true)
 		if err != nil {
@@ -224,7 +224,7 @@ func checkNodeActions(node *models.Node) string {
 		}
 	}
 	if node.Action == models.NODE_DELETE {
-		err := DeleteNodeByID(node, true)
+		err := DeleteNode(node, true)
 		if err != nil {
 			logger.Log(1, "error deleting locally:", err.Error())
 		}

+ 1 - 1
logic/zombie.go

@@ -74,7 +74,7 @@ func ManageZombies(ctx context.Context) {
 						continue
 					}
 					if time.Since(time.Unix(node.LastCheckIn, 0)) > time.Minute*ZOMBIE_DELETE_TIME {
-						if err := DeleteNodeByID(&node, true); err != nil {
+						if err := DeleteNode(&node, true); err != nil {
 							logger.Log(1, "error deleting zombie node", zombies[i], err.Error())
 							continue
 						}

+ 1 - 0
main.go

@@ -184,6 +184,7 @@ func runMessageQueue(wg *sync.WaitGroup) {
 	ctx, cancel := context.WithCancel(context.Background())
 	go mq.Keepalive(ctx)
 	go logic.ManageZombies(ctx)
+	go logic.PurgePendingNodes(ctx)
 	quit := make(chan os.Signal, 1)
 	signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
 	<-quit

+ 1 - 0
models/node.go

@@ -103,6 +103,7 @@ type Node struct {
 	FirewallInUse   string      `json:"firewallinuse" bson:"firewallinuse" yaml:"firewallinuse"`
 	InternetGateway string      `json:"internetgateway" bson:"internetgateway" yaml:"internetgateway"`
 	Connected       string      `json:"connected" bson:"connected" yaml:"connected" validate:"checkyesorno"`
+	PendingDelete   bool        `json:"pendingdelete" bson:"pendingdelete" yaml:"pendingdelete"`
 	// == PRO ==
 	DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
 	OwnerID    string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`