Files
wild-cloud/cli/cmd/instance.go
Paul Payne 77c393bf44 Streamline CLI: remove Wild Central commands, merge overlapping features
Remove commands that belong to Wild Central (dns, proxy, firewall,
ddns, crowdsec, secret central) since those APIs live on Central now.

Merge service into app (install, logs, config), merge iso into asset
(delete), move orphan commands (health, node-ip) under cluster.

Clean up redundant commands (backup start, node cancel-discovery)
and standardize deletion confirmation on --yes/-y across all commands.
2026-08-01 20:34:49 +00:00

278 lines
7.0 KiB
Go

package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/wild-cloud/wild-central/wild/internal/config"
)
var instanceCmd = &cobra.Command{
Use: "instance",
Short: "Manage instances",
Long: `Create, list, and manage Wild Cloud instances.`,
}
var instanceCreateCmd = &cobra.Command{
Use: "create <name>",
Short: "Create a new instance",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
resp, err := apiClient.Post("/api/v1/instances", map[string]string{
"name": name,
})
if err != nil {
return err
}
fmt.Printf("Instance '%s' created successfully\n", name)
if msg, ok := resp.Data["message"].(string); ok && msg != "" {
fmt.Println(msg)
}
return nil
},
}
var instanceListCmd = &cobra.Command{
Use: "list",
Short: "List all instances",
RunE: func(cmd *cobra.Command, args []string) error {
resp, err := apiClient.Get("/api/v1/instances")
if err != nil {
return err
}
if outputFormat == "json" {
return printJSON(resp.Data)
}
if outputFormat == "yaml" {
return printYAML(resp.Data)
}
instances := resp.GetArray("instances")
if len(instances) == 0 {
fmt.Println("No instances found")
return nil
}
fmt.Println("INSTANCES:")
for _, inst := range instances {
if name, ok := inst.(string); ok {
fmt.Printf(" - %s\n", name)
}
}
return nil
},
}
var instanceShowCmd = &cobra.Command{
Use: "show <name>",
Short: "Show instance details",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s", name))
if err != nil {
return err
}
if outputFormat == "json" {
return printJSON(resp.Data)
}
if outputFormat == "yaml" {
return printYAML(resp.Data)
}
// Pretty print instance info
instanceName := resp.GetString("name")
if instanceName != "" {
fmt.Printf("Instance: %s\n", instanceName)
}
// Show key config values
if config := resp.GetMap("config"); config != nil {
// Check for cloud config (test-cloud structure)
if cloud, ok := config["cloud"].(map[string]any); ok {
if domain, ok := cloud["domain"].(string); ok && domain != "" {
fmt.Printf("Domain: %s\n", domain)
}
if baseDomain, ok := cloud["baseDomain"].(string); ok && baseDomain != "" {
fmt.Printf("Base Domain: %s\n", baseDomain)
}
} else {
// Check for direct config fields (test-cli structure)
if domain, ok := config["domain"].(string); ok && domain != "" {
fmt.Printf("Domain: %s\n", domain)
}
if baseDomain, ok := config["baseDomain"].(string); ok && baseDomain != "" {
fmt.Printf("Base Domain: %s\n", baseDomain)
}
}
// Show cluster info if available
if cluster, ok := config["cluster"].(map[string]any); ok {
if clusterName, ok := cluster["name"].(string); ok && clusterName != "" {
fmt.Printf("Cluster Name: %s\n", clusterName)
}
}
}
fmt.Println("\nUse -o json or -o yaml for full configuration")
return nil
},
}
var instanceDeleteConfirm bool
var instanceDeleteCmd = &cobra.Command{
Use: "delete <name>",
Short: "Delete an instance",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
name := args[0]
if !instanceDeleteConfirm {
fmt.Printf("Are you sure you want to delete instance '%s'? (y/N): ", name)
var confirm string
fmt.Scanln(&confirm)
if confirm != "y" && confirm != "Y" {
fmt.Println("Deletion cancelled")
return nil
}
}
resp, err := apiClient.Delete(fmt.Sprintf("/api/v1/instances/%s", name))
if err != nil {
return err
}
fmt.Printf("Instance '%s' deleted successfully\n", name)
if msg, ok := resp.Data["message"].(string); ok && msg != "" {
fmt.Println(msg)
}
return nil
},
}
var instanceCurrentCmd = &cobra.Command{
Use: "current",
Short: "Show current instance",
Long: `Display the instance that would be used by commands.
Resolution order:
1. --instance flag
2. ~/.wildcloud/current_instance file
3. Auto-select first available instance`,
Run: func(cmd *cobra.Command, args []string) {
inst, err := getInstanceName()
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Println(inst)
},
}
var instanceUseCmd = &cobra.Command{
Use: "use <name>",
Short: "Set the default instance",
Long: `Set the default instance to use for all commands.
This persists the instance selection to ~/.wildcloud/current_instance.
The instance can still be overridden with the --instance flag.`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
instanceToSet := args[0]
// Validate instance exists by calling API
resp, err := apiClient.Get(fmt.Sprintf("/api/v1/instances/%s", instanceToSet))
if err != nil {
return fmt.Errorf("instance '%s' not found: %w", instanceToSet, err)
}
// Verify we got a valid response
if name := resp.GetString("name"); name != instanceToSet {
return fmt.Errorf("instance '%s' not found", instanceToSet)
}
// Persist the selection
if err := config.SetCurrentInstance(instanceToSet); err != nil {
return fmt.Errorf("failed to set current instance: %w", err)
}
fmt.Printf("Switched to instance: %s\n", instanceToSet)
// Check for config files and provide hint
dataDir := config.GetWildCLIDataDir()
var hasConfigs bool
if _, err := os.Stat(config.GetTalosconfigPath(dataDir, instanceToSet)); err == nil {
hasConfigs = true
}
if _, err := os.Stat(config.GetKubeconfigPath(dataDir, instanceToSet)); err == nil {
hasConfigs = true
}
if hasConfigs {
fmt.Println("\nTo configure your environment, run:")
fmt.Println(" source <(wild instance env)")
}
return nil
},
}
var instanceEnvCmd = &cobra.Command{
Use: "env",
Short: "Output environment variables for current instance",
Long: `Output export commands for TALOSCONFIG and KUBECONFIG.
Usage:
source <(wild instance env)
This will set environment variables for the current instance's talosconfig and kubeconfig files if they exist.`,
RunE: func(cmd *cobra.Command, args []string) error {
// Get current instance
inst, err := getInstanceName()
if err != nil {
return err
}
// Check for talosconfig and kubeconfig files
dataDir := config.GetWildCLIDataDir()
// Check for talosconfig
talosconfigPath := config.GetTalosconfigPath(dataDir, inst)
if _, err := os.Stat(talosconfigPath); err == nil {
fmt.Printf("export TALOSCONFIG=%s\n", talosconfigPath)
}
// Check for kubeconfig
kubeconfigPath := config.GetKubeconfigPath(dataDir, inst)
if _, err := os.Stat(kubeconfigPath); err == nil {
fmt.Printf("export KUBECONFIG=%s\n", kubeconfigPath)
}
return nil
},
}
func init() {
instanceCmd.AddCommand(instanceCreateCmd)
instanceCmd.AddCommand(instanceListCmd)
instanceCmd.AddCommand(instanceShowCmd)
instanceCmd.AddCommand(instanceDeleteCmd)
instanceDeleteCmd.Flags().BoolVarP(&instanceDeleteConfirm, "yes", "y", false, "Skip confirmation prompt")
instanceCmd.AddCommand(instanceCurrentCmd)
instanceCmd.AddCommand(instanceUseCmd)
instanceCmd.AddCommand(instanceEnvCmd)
}