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
This commit is contained in:
Abhishek K 2025-02-03 15:19:44 +04:00 committed by GitHub
parent cec48be354
commit 4431dc99a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 109 additions and 53 deletions

View file

@ -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",
),
)

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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