Allow resetting a node to maintenance mode.
This commit is contained in:
@@ -96,6 +96,7 @@ func (api *API) RegisterRoutes(r *mux.Router) {
|
|||||||
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}", api.NodeGet).Methods("GET")
|
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}", api.NodeGet).Methods("GET")
|
||||||
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}", api.NodeUpdate).Methods("PUT")
|
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}", api.NodeUpdate).Methods("PUT")
|
||||||
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}/apply", api.NodeApply).Methods("POST")
|
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}/apply", api.NodeApply).Methods("POST")
|
||||||
|
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}/reset", api.NodeReset).Methods("POST")
|
||||||
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}", api.NodeDelete).Methods("DELETE")
|
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}", api.NodeDelete).Methods("DELETE")
|
||||||
|
|
||||||
// PXE Asset management (schematic@version composite key)
|
// PXE Asset management (schematic@version composite key)
|
||||||
|
|||||||
@@ -371,3 +371,28 @@ func (api *API) NodeDiscoveryCancel(w http.ResponseWriter, r *http.Request) {
|
|||||||
"message": "Discovery cancelled successfully",
|
"message": "Discovery cancelled successfully",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NodeReset resets a node to maintenance mode
|
||||||
|
func (api *API) NodeReset(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
instanceName := vars["name"]
|
||||||
|
nodeIdentifier := vars["node"]
|
||||||
|
|
||||||
|
// Validate instance exists
|
||||||
|
if err := api.instance.ValidateInstance(instanceName); err != nil {
|
||||||
|
respondError(w, http.StatusNotFound, fmt.Sprintf("Instance not found: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset node
|
||||||
|
nodeMgr := node.NewManager(api.dataDir, instanceName)
|
||||||
|
if err := nodeMgr.Reset(instanceName, nodeIdentifier); err != nil {
|
||||||
|
respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to reset node: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respondJSON(w, http.StatusOK, map[string]string{
|
||||||
|
"message": "Node reset successfully - now in maintenance mode",
|
||||||
|
"node": nodeIdentifier,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -326,7 +326,6 @@ func (m *Manager) detectHardwareWithMode(nodeIP string, insecure bool) (*Hardwar
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Apply generates configuration and applies it to node
|
// Apply generates configuration and applies it to node
|
||||||
// This follows the wild-node-apply flow:
|
// This follows the wild-node-apply flow:
|
||||||
// 1. Auto-fetch templates if missing
|
// 1. Auto-fetch templates if missing
|
||||||
@@ -587,17 +586,21 @@ func (m *Manager) updateNodeStatus(instanceName string, node *Node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update configured flag
|
// Update configured flag
|
||||||
|
configuredValue := "false"
|
||||||
if node.Configured {
|
if node.Configured {
|
||||||
if err := yq.Set(configPath, basePath+".configured", "true"); err != nil {
|
configuredValue = "true"
|
||||||
return err
|
}
|
||||||
}
|
if err := yq.Set(configPath, basePath+".configured", configuredValue); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update applied flag
|
// Update applied flag
|
||||||
|
appliedValue := "false"
|
||||||
if node.Applied {
|
if node.Applied {
|
||||||
if err := yq.Set(configPath, basePath+".applied", "true"); err != nil {
|
appliedValue = "true"
|
||||||
return err
|
}
|
||||||
}
|
if err := yq.Set(configPath, basePath+".applied", appliedValue); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -677,3 +680,36 @@ func (m *Manager) FetchTemplates(instanceName string) error {
|
|||||||
destDir := filepath.Join(instancePath, "setup", "cluster-nodes", "patch.templates")
|
destDir := filepath.Join(instancePath, "setup", "cluster-nodes", "patch.templates")
|
||||||
return m.extractEmbeddedTemplates(destDir)
|
return m.extractEmbeddedTemplates(destDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset resets a node to maintenance mode
|
||||||
|
func (m *Manager) Reset(instanceName, nodeIdentifier string) error {
|
||||||
|
// Get node
|
||||||
|
node, err := m.Get(instanceName, nodeIdentifier)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("node not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine IP to reset
|
||||||
|
resetIP := node.CurrentIP
|
||||||
|
if resetIP == "" {
|
||||||
|
resetIP = node.TargetIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute reset command with graceful=false and reboot flags
|
||||||
|
talosconfigPath := tools.GetTalosconfigPath(m.dataDir, instanceName)
|
||||||
|
cmd := exec.Command("talosctl", "-n", resetIP, "--talosconfig", talosconfigPath, "reset", "--graceful=false", "--reboot")
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to reset node: %w\nOutput: %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update node status to maintenance mode
|
||||||
|
node.Maintenance = true
|
||||||
|
node.Configured = false
|
||||||
|
node.Applied = false
|
||||||
|
if err := m.updateNodeStatus(instanceName, node); err != nil {
|
||||||
|
return fmt.Errorf("failed to update node status: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user