Allow cancelling of node discovery.

This commit is contained in:
2025-11-04 16:45:39 +00:00
parent 393306de12
commit 3f546053f7

View File

@@ -1,7 +1,11 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"os"
"os/signal"
"syscall"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -24,6 +28,19 @@ var nodeDiscoverCmd = &cobra.Command{
return err return err
} }
// Check if --cancel flag is set
shouldCancel, _ := cmd.Flags().GetBool("cancel")
if shouldCancel {
// Cancel any running discovery first
_, err := apiClient.Post(fmt.Sprintf("/api/v1/instances/%s/discovery/cancel", inst), nil)
if err != nil {
// Ignore error if no discovery is running
fmt.Println("No active discovery to cancel, starting new discovery...")
} else {
fmt.Println("Cancelled previous discovery, starting new discovery...")
}
}
fmt.Printf("Starting discovery for %d IP(s)...\n", len(args)) fmt.Printf("Starting discovery for %d IP(s)...\n", len(args))
_, err = apiClient.Post(fmt.Sprintf("/api/v1/instances/%s/nodes/discover", inst), map[string]interface{}{ _, err = apiClient.Post(fmt.Sprintf("/api/v1/instances/%s/nodes/discover", inst), map[string]interface{}{
"ip_list": args, "ip_list": args,
@@ -32,38 +49,76 @@ var nodeDiscoverCmd = &cobra.Command{
return err return err
} }
// Poll for completion // Set up signal handling for Ctrl-C
fmt.Println("Scanning nodes...") ctx, cancel := context.WithCancel(context.Background())
for { defer cancel()
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/discovery", inst))
if err != nil {
return err
}
active, _ := resp.Data["active"].(bool) sigChan := make(chan os.Signal, 1)
if !active { signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
// Discovery complete
nodesFound := resp.GetArray("nodes_found") // Handle signals in a goroutine
if len(nodesFound) == 0 { go func() {
fmt.Println("\nNo Talos nodes found") <-sigChan
fmt.Println("\n\nCancelling discovery...")
cancel()
// Call cancel API
_, err := apiClient.Post(fmt.Sprintf("/api/v1/instances/%s/discovery/cancel", inst), nil)
if err != nil {
fmt.Printf("Failed to cancel discovery: %v\n", err)
} else {
fmt.Println("Discovery cancelled successfully")
}
os.Exit(0)
}()
// Poll for completion
fmt.Println("Scanning nodes... (Press Ctrl-C to cancel)")
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return nil
case <-ticker.C:
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s/discovery", inst))
if err != nil {
return err
}
active, _ := resp.Data["active"].(bool)
if !active {
// Discovery complete
nodesFound := resp.GetArray("nodes_found")
if len(nodesFound) == 0 {
fmt.Println("\nNo Talos nodes found")
return nil
}
fmt.Printf("\nFound %d node(s) in maintenance mode:\n\n", len(nodesFound))
fmt.Printf("%-15s %-15s %-15s\n", "IP", "VERSION", "HOSTNAME")
fmt.Println("-----------------------------------------------------")
for _, node := range nodesFound {
if m, ok := node.(map[string]interface{}); ok {
version := m["version"]
if version == nil {
version = ""
}
hostname := m["hostname"]
if hostname == nil {
hostname = ""
}
fmt.Printf("%-15s %-15s %-15s\n",
m["ip"], version, hostname)
}
}
return nil return nil
} }
fmt.Printf("\nFound %d node(s):\n\n", len(nodesFound)) // Still running, show progress
fmt.Printf("%-15s %-12s %-10s\n", "IP", "INTERFACE", "VERSION") fmt.Print(".")
fmt.Println("-----------------------------------------------")
for _, node := range nodesFound {
if m, ok := node.(map[string]interface{}); ok {
fmt.Printf("%-15s %-12s %-10s\n",
m["ip"], m["interface"], m["version"])
}
}
return nil
} }
// Still running, wait a bit
fmt.Print(".")
time.Sleep(500 * time.Millisecond)
} }
}, },
} }
@@ -431,6 +486,31 @@ You can use it manually to update templates.`,
}, },
} }
var nodeCancelDiscoveryCmd = &cobra.Command{
Use: "cancel-discovery",
Short: "Cancel active node discovery",
Long: `Cancel an active node discovery operation.
Use this if discovery gets stuck or you want to stop a running scan.
Example:
wild node cancel-discovery`,
RunE: func(cmd *cobra.Command, args []string) error {
inst, err := getInstanceName()
if err != nil {
return err
}
_, err = apiClient.Post(fmt.Sprintf("/api/v1/instances/%s/discovery/cancel", inst), nil)
if err != nil {
return err
}
fmt.Println("Discovery cancelled successfully")
return nil
},
}
var nodeDeleteCmd = &cobra.Command{ var nodeDeleteCmd = &cobra.Command{
Use: "delete <hostname>", Use: "delete <hostname>",
Short: "Delete a node", Short: "Delete a node",
@@ -453,6 +533,7 @@ var nodeDeleteCmd = &cobra.Command{
func init() { func init() {
nodeCmd.AddCommand(nodeDiscoverCmd) nodeCmd.AddCommand(nodeDiscoverCmd)
nodeCmd.AddCommand(nodeCancelDiscoveryCmd)
nodeCmd.AddCommand(nodeDetectCmd) nodeCmd.AddCommand(nodeDetectCmd)
nodeCmd.AddCommand(nodeListCmd) nodeCmd.AddCommand(nodeListCmd)
nodeCmd.AddCommand(nodeShowCmd) nodeCmd.AddCommand(nodeShowCmd)
@@ -462,6 +543,9 @@ func init() {
nodeCmd.AddCommand(nodeFetchTemplatesCmd) nodeCmd.AddCommand(nodeFetchTemplatesCmd)
nodeCmd.AddCommand(nodeDeleteCmd) nodeCmd.AddCommand(nodeDeleteCmd)
// Add flags to node discover command
nodeDiscoverCmd.Flags().Bool("cancel", false, "Cancel any running discovery before starting")
// Add flags to node add command // Add flags to node add command
nodeAddCmd.Flags().String("target-ip", "", "Target IP address for production") nodeAddCmd.Flags().String("target-ip", "", "Target IP address for production")
nodeAddCmd.Flags().String("current-ip", "", "Current IP address (for maintenance mode)") nodeAddCmd.Flags().String("current-ip", "", "Current IP address (for maintenance mode)")