From 4431dc99a7c56c71511ecce111c75c9717fa52fc Mon Sep 17 00:00:00 2001 From: Abhishek K Date: Mon, 3 Feb 2025 15:19:44 +0400 Subject: [PATCH] NET-1933: option to force destroy network (#3311) * option to force destroy network * fix network tests * fix network defaults func * fix network destroy action * delete network if node count is zero * push peer update network deletion * send node update --- controllers/hosts.go | 4 +-- controllers/network.go | 30 +++++++++++++++-- controllers/network_test.go | 21 ++++++++++-- logic/networks.go | 64 ++++++++++++++++++++++++++----------- logic/nodes.go | 2 +- models/network.go | 13 +++++++- serverctl/serverctl.go | 28 ++-------------- 7 files changed, 109 insertions(+), 53 deletions(-) diff --git a/controllers/hosts.go b/controllers/hosts.go index 5458a179..ddc04104 100644 --- a/controllers/hosts.go +++ b/controllers/hosts.go @@ -594,7 +594,7 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) { w, r, logic.FormatError( - fmt.Errorf("failed to force delete daemon node: "+err.Error()), + fmt.Errorf("failed to force delete daemon node: %s", err.Error()), "internal", ), ) @@ -634,7 +634,7 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) { w, r, logic.FormatError( - fmt.Errorf("failed to force delete daemon node: "+err.Error()), + fmt.Errorf("failed to force delete daemon node: %s", err.Error()), "internal", ), ) diff --git a/controllers/network.go b/controllers/network.go index 86a6e340..b1a308cc 100644 --- a/controllers/network.go +++ b/controllers/network.go @@ -434,6 +434,7 @@ func getNetworkACL(w http.ResponseWriter, r *http.Request) { // @Tags Networks // @Security oauth // @Param networkname path string true "Network name" +// @Param force query bool false "Force Delete" // @Produce json // @Success 200 {object} models.SuccessResponse // @Failure 400 {object} models.ErrorResponse @@ -441,10 +442,18 @@ func getNetworkACL(w http.ResponseWriter, r *http.Request) { func deleteNetwork(w http.ResponseWriter, r *http.Request) { // Set header w.Header().Set("Content-Type", "application/json") - + force := r.URL.Query().Get("force") == "true" var params = mux.Vars(r) network := params["networkname"] - err := logic.DeleteNetwork(network) + doneCh := make(chan struct{}, 1) + networkNodes, err := logic.GetNetworkNodes(network) + if err != nil { + logger.Log(0, r.Header.Get("user"), + fmt.Sprintf("failed to get network nodes [%s]: %v", network, err)) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + err = logic.DeleteNetwork(network, force, doneCh) if err != nil { errtype := "badrequest" if strings.Contains(err.Error(), "Node check failed") { @@ -459,7 +468,22 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) { go logic.DeleteDefaultNetworkPolicies(models.NetworkID(network)) //delete network from allocated ip map go logic.RemoveNetworkFromAllocatedIpMap(network) - + go func() { + <-doneCh + mq.PublishPeerUpdate(true) + // send node update to clean up locally + for _, node := range networkNodes { + node := node + node.PendingDelete = true + node.Action = models.NODE_DELETE + if err := mq.NodeUpdate(&node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } + } + if servercfg.IsDNSMode() { + logic.SetDNS() + } + }() logger.Log(1, r.Header.Get("user"), "deleted network", network) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode("success") diff --git a/controllers/network_test.go b/controllers/network_test.go index 4320b6e3..87cb6ee3 100644 --- a/controllers/network_test.go +++ b/controllers/network_test.go @@ -75,11 +75,19 @@ func TestDeleteNetwork(t *testing.T) { t.Run("NetworkwithNodes", func(t *testing.T) { }) t.Run("DeleteExistingNetwork", func(t *testing.T) { - err := logic.DeleteNetwork("skynet") + doneCh := make(chan struct{}, 1) + err := logic.DeleteNetwork("skynet", false, doneCh) assert.Nil(t, err) }) t.Run("NonExistentNetwork", func(t *testing.T) { - err := logic.DeleteNetwork("skynet") + doneCh := make(chan struct{}, 1) + err := logic.DeleteNetwork("skynet", false, doneCh) + assert.Nil(t, err) + }) + createNetv1("test") + t.Run("ForceDeleteNetwork", func(t *testing.T) { + doneCh := make(chan struct{}, 1) + err := logic.DeleteNetwork("test", true, doneCh) assert.Nil(t, err) }) } @@ -214,6 +222,15 @@ func createNet() { logic.CreateNetwork(network) } } +func createNetv1(netId string) { + var network models.Network + network.NetID = netId + network.AddressRange = "100.0.0.1/24" + _, err := logic.GetNetwork(netId) + if err != nil { + logic.CreateNetwork(network) + } +} func createNetDualStack() { var network models.Network diff --git a/logic/networks.go b/logic/networks.go index 1617889d..04b6b573 100644 --- a/logic/networks.go +++ b/logic/networks.go @@ -102,7 +102,7 @@ func RemoveIpFromAllocatedIpMap(networkName string, ip string) { // AddNetworkToAllocatedIpMap - add network to allocated ip map when network is added func AddNetworkToAllocatedIpMap(networkName string) { networkCacheMutex.Lock() - allocatedIpMap[networkName] = map[string]net.IP{} + allocatedIpMap[networkName] = make(map[string]net.IP) networkCacheMutex.Unlock() } @@ -171,23 +171,8 @@ func GetNetworks() ([]models.Network, error) { } // DeleteNetwork - deletes a network -func DeleteNetwork(network string) error { - // remove ACL for network - err := nodeacls.DeleteACLContainer(nodeacls.NetworkID(network)) - if err != nil { - logger.Log(1, "failed to remove the node acls during network delete for network,", network) - } - // Delete default network enrollment key - keys, _ := GetAllEnrollmentKeys() - for _, key := range keys { - if key.Tags[0] == network { - if key.Default { - DeleteEnrollmentKey(key.Value, true) - break - } +func DeleteNetwork(network string, force bool, done chan struct{}) error { - } - } nodeCount, err := GetNetworkNonServerNodeCount(network) if nodeCount == 0 || database.IsEmptyRecord(err) { // delete server nodes first then db records @@ -200,7 +185,50 @@ func DeleteNetwork(network string) error { } return nil } - return errors.New("node check failed. All nodes must be deleted before deleting network") + + // Remove All Nodes + go func() { + nodes, err := GetNetworkNodes(network) + if err == nil { + for _, node := range nodes { + node := node + host, err := GetHost(node.HostID.String()) + if err != nil { + continue + } + DissasociateNodeFromHost(&node, host) + } + } + // remove ACL for network + err = nodeacls.DeleteACLContainer(nodeacls.NetworkID(network)) + if err != nil { + logger.Log(1, "failed to remove the node acls during network delete for network,", network) + } + // delete server nodes first then db records + err = database.DeleteRecord(database.NETWORKS_TABLE_NAME, network) + if err != nil { + return + } + if servercfg.CacheEnabled() { + deleteNetworkFromCache(network) + } + done <- struct{}{} + close(done) + }() + + // Delete default network enrollment key + keys, _ := GetAllEnrollmentKeys() + for _, key := range keys { + if key.Tags[0] == network { + if key.Default { + DeleteEnrollmentKey(key.Value, true) + break + } + + } + } + + return nil } // CreateNetwork - creates a network in database diff --git a/logic/nodes.go b/logic/nodes.go index 368d7d67..0b0c2495 100644 --- a/logic/nodes.go +++ b/logic/nodes.go @@ -239,7 +239,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error { } } - return fmt.Errorf("failed to update node " + currentNode.ID.String() + ", cannot change ID.") + return fmt.Errorf("failed to update node %s, cannot change ID", currentNode.ID.String()) } // DeleteNode - marks node for deletion (and adds to zombie list) if called by UI or deletes node if called by node diff --git a/models/network.go b/models/network.go index dbb2fc4a..32707933 100644 --- a/models/network.go +++ b/models/network.go @@ -42,9 +42,10 @@ func (network *Network) SetNetworkLastModified() { } // Network.SetDefaults - sets default values for a network struct -func (network *Network) SetDefaults() { +func (network *Network) SetDefaults() (upsert bool) { if network.DefaultUDPHolePunch == "" { network.DefaultUDPHolePunch = "no" + upsert = true } if network.DefaultInterface == "" { if len(network.NetID) < 33 { @@ -52,35 +53,45 @@ func (network *Network) SetDefaults() { } else { network.DefaultInterface = network.NetID } + upsert = true } if network.DefaultListenPort == 0 { network.DefaultListenPort = 51821 + upsert = true } if network.NodeLimit == 0 { network.NodeLimit = 999999999 + upsert = true } if network.DefaultKeepalive == 0 { network.DefaultKeepalive = 20 + upsert = true } if network.AllowManualSignUp == "" { network.AllowManualSignUp = "no" + upsert = true } if network.IsIPv4 == "" { network.IsIPv4 = "yes" + upsert = true } if network.IsIPv6 == "" { network.IsIPv6 = "no" + upsert = true } if network.DefaultMTU == 0 { network.DefaultMTU = 1280 + upsert = true } if network.DefaultACL == "" { network.DefaultACL = "yes" + upsert = true } + return } func (network *Network) GetNetworkNetworkCIDR4() *net.IPNet { diff --git a/serverctl/serverctl.go b/serverctl/serverctl.go index 3cedf72d..12fbc3e4 100644 --- a/serverctl/serverctl.go +++ b/serverctl/serverctl.go @@ -59,32 +59,8 @@ func setNetworkDefaults() error { return err } for _, network := range networks { - update := false - newNet := network - if strings.Contains(network.NetID, ".") { - newNet.NetID = strings.ReplaceAll(network.NetID, ".", "") - newNet.DefaultInterface = strings.ReplaceAll(network.DefaultInterface, ".", "") - update = true - } - if strings.ContainsAny(network.NetID, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") { - newNet.NetID = strings.ToLower(network.NetID) - newNet.DefaultInterface = strings.ToLower(network.DefaultInterface) - update = true - } - if update { - newNet.SetDefaults() - if err := logic.SaveNetwork(&newNet); err != nil { - logger.Log(0, "error saving networks during initial update:", err.Error()) - } - if err := logic.DeleteNetwork(network.NetID); err != nil { - logger.Log(0, "error deleting old network:", err.Error()) - } - } else { - network.SetDefaults() - _, _, _, err = logic.UpdateNetwork(&network, &network) - if err != nil { - logger.Log(0, "could not set defaults on network", network.NetID) - } + if network.SetDefaults() { + logic.SaveNetwork(&network) } } return nil