diff --git a/.gitignore b/.gitignore index aeaf773..7a81c3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Binaries ry +/cli *.exe *.exe~ *.dll diff --git a/cmd/cli/commands/billing/billing.go b/cmd/cli/commands/billing/billing.go new file mode 100644 index 0000000..be5c044 --- /dev/null +++ b/cmd/cli/commands/billing/billing.go @@ -0,0 +1,37 @@ +package billing + +import ( + "fmt" + + "git.yoresee.cc/nightstar/ry/internal/output" + "git.yoresee.cc/nightstar/ry/internal/provider" + + "github.com/spf13/cobra" +) + +func Cmd(prov *provider.Provider, out **output.Printer) *cobra.Command { + billingCmd := &cobra.Command{ + Use: "billing", + Short: "Billing and orders", + } + + ordersCmd := &cobra.Command{ + Use: "orders", + Short: "List orders", + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Billing() + if svc == nil { + return fmt.Errorf("billing not supported by current provider") + } + orders, err := svc.Orders(cmd.Context()) + if err != nil { + return err + } + return (*out).Print(orders) + }, + } + + billingCmd.AddCommand(ordersCmd) + + return billingCmd +} diff --git a/cmd/cli/commands/configcmd/config.go b/cmd/cli/commands/configcmd/config.go new file mode 100644 index 0000000..acbb9ea --- /dev/null +++ b/cmd/cli/commands/configcmd/config.go @@ -0,0 +1,75 @@ +package configcmd + +import ( + "fmt" + "strings" + + "git.yoresee.cc/nightstar/ry/internal/config" + + "github.com/spf13/cobra" +) + +func Cmd(cfg *config.Config, cfgPath string) *cobra.Command { + configCmd := &cobra.Command{ + Use: "config", + Short: "Manage ry configuration", + } + + configShowCmd := &cobra.Command{ + Use: "show", + Short: "Show current configuration", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Printf("Config file: %s\n", cfgPath) + fmt.Printf("Current provider: %s\n", cfg.CurrentProvider) + fmt.Printf("Output format: %s\n", cfg.Output) + fmt.Println("Providers:") + for name, pc := range cfg.Providers { + masked := pc.APIKey + if len(masked) > 4 { + masked = masked[:2] + strings.Repeat("*", len(masked)-4) + masked[len(masked)-2:] + } + marker := " " + if name == cfg.CurrentProvider { + marker = "*" + } + fmt.Printf(" %s %s: api_key=%s\n", marker, name, masked) + } + return nil + }, + } + + configSetCmd := &cobra.Command{ + Use: "set ", + Short: "Set a config value", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + key, value := args[0], args[1] + switch key { + case "output": + cfg.Output = value + default: + return fmt.Errorf("unknown config key: %s", key) + } + if err := config.Save(cfgPath, cfg); err != nil { + return fmt.Errorf("save config: %w", err) + } + fmt.Printf("Set %s = %s\n", key, value) + return nil + }, + } + + configPathCmd := &cobra.Command{ + Use: "path", + Short: "Print config file path", + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println(cfgPath) + return nil + }, + } + + configCmd.AddCommand(configShowCmd) + configCmd.AddCommand(configSetCmd) + configCmd.AddCommand(configPathCmd) + + return configCmd +} diff --git a/cmd/cli/commands/domain/domain.go b/cmd/cli/commands/domain/domain.go new file mode 100644 index 0000000..719075c --- /dev/null +++ b/cmd/cli/commands/domain/domain.go @@ -0,0 +1,99 @@ +package domain + +import ( + "fmt" + + "git.yoresee.cc/nightstar/ry/internal/output" + "git.yoresee.cc/nightstar/ry/internal/provider" + + "github.com/spf13/cobra" +) + +func Cmd(prov *provider.Provider, out **output.Printer) *cobra.Command { + domainCmd := &cobra.Command{ + Use: "domain", + Short: "Manage domains", + } + + domainListCmd := &cobra.Command{ + Use: "list", + Short: "List domains", + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Domain() + if svc == nil { + return fmt.Errorf("domain management not supported by current provider") + } + domains, err := svc.List(cmd.Context()) + if err != nil { + return err + } + return (*out).Print(domains) + }, + } + + dnsCmd := &cobra.Command{ + Use: "dns ", + Short: "Manage DNS records", + Args: cobra.MinimumNArgs(1), + } + + dnsListCmd := &cobra.Command{ + Use: "list", + Short: "List DNS records", + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Domain() + if svc == nil { + return fmt.Errorf("domain management not supported") + } + records, err := svc.DNSList(cmd.Context(), args[0]) + if err != nil { + return err + } + return (*out).Print(records) + }, + } + + dnsAddCmd := &cobra.Command{ + Use: "add", + Short: "Add a DNS record", + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Domain() + if svc == nil { + return fmt.Errorf("domain management not supported") + } + recType, _ := cmd.Flags().GetString("type") + name, _ := cmd.Flags().GetString("name") + value, _ := cmd.Flags().GetString("value") + ttl, _ := cmd.Flags().GetInt("ttl") + return svc.DNSAdd(cmd.Context(), args[0], + provider.DNSRecord{Type: recType, Name: name, Value: value, TTL: ttl}) + }, + } + + dnsDeleteCmd := &cobra.Command{ + Use: "delete ", + Short: "Delete a DNS record", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Domain() + if svc == nil { + return fmt.Errorf("domain management not supported") + } + return svc.DNSDelete(cmd.Context(), args[0], args[1]) + }, + } + + dnsAddCmd.Flags().String("type", "A", "Record type (A, CNAME, TXT)") + dnsAddCmd.Flags().String("name", "@", "Record name") + dnsAddCmd.Flags().String("value", "", "Record value") + dnsAddCmd.Flags().Int("ttl", 600, "TTL in seconds") + dnsAddCmd.MarkFlagRequired("value") + + dnsCmd.AddCommand(dnsListCmd) + dnsCmd.AddCommand(dnsAddCmd) + dnsCmd.AddCommand(dnsDeleteCmd) + domainCmd.AddCommand(domainListCmd) + domainCmd.AddCommand(dnsCmd) + + return domainCmd +} diff --git a/cmd/cli/commands/providercmd/provider.go b/cmd/cli/commands/providercmd/provider.go new file mode 100644 index 0000000..a191d99 --- /dev/null +++ b/cmd/cli/commands/providercmd/provider.go @@ -0,0 +1,94 @@ +package providercmd + +import ( + "fmt" + + "git.yoresee.cc/nightstar/ry/internal/config" + "git.yoresee.cc/nightstar/ry/internal/provider" + + "github.com/spf13/cobra" +) + +func Cmd(cfg *config.Config, cfgPath string) *cobra.Command { + providerCmd := &cobra.Command{ + Use: "provider", + Short: "Manage cloud providers", + } + + providerListCmd := &cobra.Command{ + Use: "list", + Short: "List configured providers", + Annotations: map[string]string{"skipProvider": "true"}, + RunE: func(cmd *cobra.Command, args []string) error { + current := "" + if cfg != nil { + current = cfg.CurrentProvider + } + for _, name := range provider.List() { + marker := " " + if name == current { + marker = "*" + } + fmt.Printf("%s %s\n", marker, name) + } + return nil + }, + } + + providerUseCmd := &cobra.Command{ + Use: "use ", + Short: "Switch the current cloud provider", + Annotations: map[string]string{"skipProvider": "true"}, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + name := args[0] + if _, ok := cfg.Providers[name]; !ok { + return fmt.Errorf("provider %q not configured — run 'ry provider add %s' first", name, name) + } + cfg.CurrentProvider = name + if err := config.Save(cfgPath, cfg); err != nil { + return fmt.Errorf("save config: %w", err) + } + fmt.Printf("Switched to provider %q\n", name) + return nil + }, + } + + providerAddCmd := &cobra.Command{ + Use: "add ", + Short: "Add a new cloud provider", + Annotations: map[string]string{"skipProvider": "true"}, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + name := args[0] + fmt.Printf("Adding provider %q...\n", name) + fmt.Print("Enter API Key: ") + var apiKey string + fmt.Scanln(&apiKey) + if apiKey == "" { + return fmt.Errorf("API Key is required") + } + if cfg.Providers == nil { + cfg.Providers = make(map[string]config.ProviderConfig) + } + cfg.Providers[name] = config.ProviderConfig{APIKey: apiKey} + if cfg.CurrentProvider == "" { + cfg.CurrentProvider = name + } + if err := config.Save(cfgPath, cfg); err != nil { + return fmt.Errorf("save config: %w", err) + } + fmt.Printf("Provider %q added successfully\n", name) + if cfg.CurrentProvider == name { + fmt.Printf("Set as current provider\n") + } + return nil + }, + } + + providerCmd.AddCommand(providerListCmd) + providerCmd.AddCommand(providerUseCmd) + providerCmd.AddCommand(providerAddCmd) + + return providerCmd +} diff --git a/cmd/cli/commands/server/server.go b/cmd/cli/commands/server/server.go new file mode 100644 index 0000000..1bfbaeb --- /dev/null +++ b/cmd/cli/commands/server/server.go @@ -0,0 +1,181 @@ +package server + +import ( + "fmt" + + "git.yoresee.cc/nightstar/ry/internal/output" + "git.yoresee.cc/nightstar/ry/internal/provider" + + "github.com/spf13/cobra" +) + +func Cmd(prov *provider.Provider, out **output.Printer, raw *bool) *cobra.Command { + serverCmd := &cobra.Command{ + Use: "server", + Short: "Manage cloud servers", + } + + serverListCmd := &cobra.Command{ + Use: "list", + Short: "List servers", + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Server() + if svc == nil { + return fmt.Errorf("server management not supported by current provider") + } + servers, err := svc.List(cmd.Context()) + if err != nil { + return err + } + if *raw { + var r []any + for _, s := range servers { + r = append(r, s.Raw) + } + return (*out).Print(r) + } + return (*out).Print(servers) + }, + } + + serverGetCmd := &cobra.Command{ + Use: "get ", + Short: "Get server details", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Server() + if svc == nil { + return fmt.Errorf("server management not supported") + } + s, err := svc.Get(cmd.Context(), args[0]) + if err != nil { + return err + } + if *raw { + return (*out).Print(s.Raw) + } + return (*out).Print(s) + }, + } + + serverStartCmd := &cobra.Command{ + Use: "start ", + Short: "Start a server", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Server() + if svc == nil { + return fmt.Errorf("server management not supported") + } + if err := svc.Start(cmd.Context(), args[0]); err != nil { + return err + } + fmt.Printf("Server %s started\n", args[0]) + return nil + }, + } + + serverStopCmd := &cobra.Command{ + Use: "stop ", + Short: "Stop a server", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Server() + if svc == nil { + return fmt.Errorf("server management not supported") + } + if err := svc.Stop(cmd.Context(), args[0]); err != nil { + return err + } + fmt.Printf("Server %s stopped\n", args[0]) + return nil + }, + } + + serverRebootCmd := &cobra.Command{ + Use: "reboot ", + Short: "Reboot a server", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Server() + if svc == nil { + return fmt.Errorf("server management not supported") + } + if err := svc.Reboot(cmd.Context(), args[0]); err != nil { + return err + } + fmt.Printf("Server %s rebooted\n", args[0]) + return nil + }, + } + + serverReinstallCmd := &cobra.Command{ + Use: "reinstall ", + Short: "Reinstall server OS", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Server() + if svc == nil { + return fmt.Errorf("server management not supported") + } + osTemplate, _ := cmd.Flags().GetString("os") + if osTemplate == "" { + return fmt.Errorf("--os flag is required (e.g. --os ubuntu-22.04)") + } + if err := svc.Reinstall(cmd.Context(), args[0], osTemplate); err != nil { + return err + } + fmt.Printf("Server %s reinstalling with %s\n", args[0], osTemplate) + return nil + }, + } + + serverResetPasswordCmd := &cobra.Command{ + Use: "reset-password ", + Short: "Reset server password", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Server() + if svc == nil { + return fmt.Errorf("server management not supported") + } + if err := svc.ResetPassword(cmd.Context(), args[0]); err != nil { + return err + } + fmt.Printf("Password reset for server %s\n", args[0]) + return nil + }, + } + + serverVNCCmd := &cobra.Command{ + Use: "vnc ", + Short: "Get VNC connection URL", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Server() + if svc == nil { + return fmt.Errorf("server management not supported") + } + url, err := svc.GetVNC(cmd.Context(), args[0]) + if err != nil { + return err + } + fmt.Println(url) + return nil + }, + } + + serverReinstallCmd.Flags().String("os", "", "OS template name (required)") + serverReinstallCmd.MarkFlagRequired("os") + + serverCmd.AddCommand(serverListCmd) + serverCmd.AddCommand(serverGetCmd) + serverCmd.AddCommand(serverStartCmd) + serverCmd.AddCommand(serverStopCmd) + serverCmd.AddCommand(serverRebootCmd) + serverCmd.AddCommand(serverReinstallCmd) + serverCmd.AddCommand(serverResetPasswordCmd) + serverCmd.AddCommand(serverVNCCmd) + + return serverCmd +} diff --git a/cmd/cli/commands/storage/storage.go b/cmd/cli/commands/storage/storage.go new file mode 100644 index 0000000..bccf910 --- /dev/null +++ b/cmd/cli/commands/storage/storage.go @@ -0,0 +1,79 @@ +package storage + +import ( + "fmt" + + "git.yoresee.cc/nightstar/ry/internal/output" + "git.yoresee.cc/nightstar/ry/internal/provider" + + "github.com/spf13/cobra" +) + +func Cmd(prov *provider.Provider, out **output.Printer) *cobra.Command { + storageCmd := &cobra.Command{ + Use: "storage", + Short: "Manage object storage", + } + + storageListCmd := &cobra.Command{ + Use: "list", + Short: "List storage instances", + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Storage() + if svc == nil { + return fmt.Errorf("storage management not supported by current provider") + } + instances, err := svc.List(cmd.Context()) + if err != nil { + return err + } + return (*out).Print(instances) + }, + } + + bucketCmd := &cobra.Command{ + Use: "bucket ", + Short: "Manage buckets", + Args: cobra.ExactArgs(1), + } + + bucketListCmd := &cobra.Command{ + Use: "list", + Short: "List buckets", + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Storage() + if svc == nil { + return fmt.Errorf("storage management not supported") + } + buckets, err := svc.BucketList(cmd.Context(), args[0]) + if err != nil { + return err + } + return (*out).Print(buckets) + }, + } + + bucketCreateCmd := &cobra.Command{ + Use: "create ", + Short: "Create a bucket", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + svc := (*prov).Storage() + if svc == nil { + return fmt.Errorf("storage management not supported") + } + if err := svc.BucketCreate(cmd.Context(), args[0], args[1]); err != nil { + return err + } + fmt.Printf("Bucket %q created\n", args[1]) + return nil + }, + } + + bucketCmd.AddCommand(bucketListCmd) + bucketCmd.AddCommand(bucketCreateCmd) + storageCmd.AddCommand(storageListCmd) + storageCmd.AddCommand(bucketCmd) + + return storageCmd +} diff --git a/cmd/ry/main.go b/cmd/cli/main.go similarity index 66% rename from cmd/ry/main.go rename to cmd/cli/main.go index 7b998ac..2479638 100644 --- a/cmd/ry/main.go +++ b/cmd/cli/main.go @@ -4,12 +4,11 @@ import ( "fmt" "os" - "git.yoresee.cc/nightstar/ry/internal/cmd" _ "git.yoresee.cc/nightstar/ry/internal/provider/rainyun" ) func main() { - if err := cmd.Execute(); err != nil { + if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } diff --git a/internal/cmd/root.go b/cmd/cli/root.go similarity index 78% rename from internal/cmd/root.go rename to cmd/cli/root.go index 09c1511..35bf575 100644 --- a/internal/cmd/root.go +++ b/cmd/cli/root.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" @@ -8,14 +8,21 @@ import ( "git.yoresee.cc/nightstar/ry/internal/output" "git.yoresee.cc/nightstar/ry/internal/provider" + "git.yoresee.cc/nightstar/ry/cmd/cli/commands/billing" + "git.yoresee.cc/nightstar/ry/cmd/cli/commands/configcmd" + "git.yoresee.cc/nightstar/ry/cmd/cli/commands/domain" + "git.yoresee.cc/nightstar/ry/cmd/cli/commands/providercmd" + "git.yoresee.cc/nightstar/ry/cmd/cli/commands/server" + "git.yoresee.cc/nightstar/ry/cmd/cli/commands/storage" + "github.com/spf13/cobra" ) var ( - cfg *config.Config - cfgPath string - prov provider.Provider - out *output.Printer + cfg *config.Config + cfgPath string + prov provider.Provider + out *output.Printer flagProvider string flagOutput string flagRaw bool @@ -38,7 +45,6 @@ Use "ry [command] --help" for more information about a command.`, return fmt.Errorf("load config: %w", err) } - // provider subcommands don't need an initialized provider if cmd.Annotations != nil && cmd.Annotations["skipProvider"] == "true" { return nil } @@ -78,10 +84,6 @@ Use "ry [command] --help" for more information about a command.`, }, } -func Execute() error { - return rootCmd.Execute() -} - func init() { cfgPath = config.DefaultPath() rootCmd.PersistentFlags().StringVarP(&flagProvider, "provider", "p", "", "Cloud provider to use") @@ -108,4 +110,11 @@ func init() { }, } rootCmd.AddCommand(completionCmd) + + rootCmd.AddCommand(configcmd.Cmd(cfg, cfgPath)) + rootCmd.AddCommand(providercmd.Cmd(cfg, cfgPath)) + rootCmd.AddCommand(server.Cmd(&prov, &out, &flagRaw)) + rootCmd.AddCommand(storage.Cmd(&prov, &out)) + rootCmd.AddCommand(domain.Cmd(&prov, &out)) + rootCmd.AddCommand(billing.Cmd(&prov, &out)) } diff --git a/internal/cmd/billing.go b/internal/cmd/billing.go deleted file mode 100644 index 7c17c30..0000000 --- a/internal/cmd/billing.go +++ /dev/null @@ -1,33 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -var billingCmd = &cobra.Command{ - Use: "billing", - Short: "Billing and orders", -} - -var ordersCmd = &cobra.Command{ - Use: "orders", - Short: "List orders", - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Billing() - if svc == nil { - return fmt.Errorf("billing not supported by current provider") - } - orders, err := svc.Orders(cmd.Context()) - if err != nil { - return err - } - return out.Print(orders) - }, -} - -func init() { - billingCmd.AddCommand(ordersCmd) - rootCmd.AddCommand(billingCmd) -} diff --git a/internal/cmd/config_cmd.go b/internal/cmd/config_cmd.go deleted file mode 100644 index b27d07f..0000000 --- a/internal/cmd/config_cmd.go +++ /dev/null @@ -1,74 +0,0 @@ -package cmd - -import ( - "fmt" - "strings" - - "git.yoresee.cc/nightstar/ry/internal/config" - - "github.com/spf13/cobra" -) - -var configCmd = &cobra.Command{ - Use: "config", - Short: "Manage ry configuration", -} - -var configShowCmd = &cobra.Command{ - Use: "show", - Short: "Show current configuration", - RunE: func(cmd *cobra.Command, args []string) error { - fmt.Printf("Config file: %s\n", cfgPath) - fmt.Printf("Current provider: %s\n", cfg.CurrentProvider) - fmt.Printf("Output format: %s\n", cfg.Output) - fmt.Println("Providers:") - for name, pc := range cfg.Providers { - masked := pc.APIKey - if len(masked) > 4 { - masked = masked[:2] + strings.Repeat("*", len(masked)-4) + masked[len(masked)-2:] - } - marker := " " - if name == cfg.CurrentProvider { - marker = "*" - } - fmt.Printf(" %s %s: api_key=%s\n", marker, name, masked) - } - return nil - }, -} - -var configSetCmd = &cobra.Command{ - Use: "set ", - Short: "Set a config value", - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - key, value := args[0], args[1] - switch key { - case "output": - cfg.Output = value - default: - return fmt.Errorf("unknown config key: %s", key) - } - if err := config.Save(cfgPath, cfg); err != nil { - return fmt.Errorf("save config: %w", err) - } - fmt.Printf("Set %s = %s\n", key, value) - return nil - }, -} - -var configPathCmd = &cobra.Command{ - Use: "path", - Short: "Print config file path", - RunE: func(cmd *cobra.Command, args []string) error { - fmt.Println(cfgPath) - return nil - }, -} - -func init() { - configCmd.AddCommand(configShowCmd) - configCmd.AddCommand(configSetCmd) - configCmd.AddCommand(configPathCmd) - rootCmd.AddCommand(configCmd) -} diff --git a/internal/cmd/domain.go b/internal/cmd/domain.go deleted file mode 100644 index 9c61323..0000000 --- a/internal/cmd/domain.go +++ /dev/null @@ -1,97 +0,0 @@ -package cmd - -import ( - "fmt" - - "git.yoresee.cc/nightstar/ry/internal/provider" - - "github.com/spf13/cobra" -) - -var domainCmd = &cobra.Command{ - Use: "domain", - Short: "Manage domains", -} - -var domainListCmd = &cobra.Command{ - Use: "list", - Short: "List domains", - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Domain() - if svc == nil { - return fmt.Errorf("domain management not supported by current provider") - } - domains, err := svc.List(cmd.Context()) - if err != nil { - return err - } - return out.Print(domains) - }, -} - -var dnsCmd = &cobra.Command{ - Use: "dns ", - Short: "Manage DNS records", - Args: cobra.MinimumNArgs(1), -} - -var dnsListCmd = &cobra.Command{ - Use: "list", - Short: "List DNS records", - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Domain() - if svc == nil { - return fmt.Errorf("domain management not supported") - } - records, err := svc.DNSList(cmd.Context(), args[0]) - if err != nil { - return err - } - return out.Print(records) - }, -} - -var dnsAddCmd = &cobra.Command{ - Use: "add", - Short: "Add a DNS record", - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Domain() - if svc == nil { - return fmt.Errorf("domain management not supported") - } - recType, _ := cmd.Flags().GetString("type") - name, _ := cmd.Flags().GetString("name") - value, _ := cmd.Flags().GetString("value") - ttl, _ := cmd.Flags().GetInt("ttl") - return svc.DNSAdd(cmd.Context(), args[0], - provider.DNSRecord{Type: recType, Name: name, Value: value, TTL: ttl}) - }, -} - -var dnsDeleteCmd = &cobra.Command{ - Use: "delete ", - Short: "Delete a DNS record", - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Domain() - if svc == nil { - return fmt.Errorf("domain management not supported") - } - return svc.DNSDelete(cmd.Context(), args[0], args[1]) - }, -} - -func init() { - dnsAddCmd.Flags().String("type", "A", "Record type (A, CNAME, TXT)") - dnsAddCmd.Flags().String("name", "@", "Record name") - dnsAddCmd.Flags().String("value", "", "Record value") - dnsAddCmd.Flags().Int("ttl", 600, "TTL in seconds") - dnsAddCmd.MarkFlagRequired("value") - - dnsCmd.AddCommand(dnsListCmd) - dnsCmd.AddCommand(dnsAddCmd) - dnsCmd.AddCommand(dnsDeleteCmd) - domainCmd.AddCommand(domainListCmd) - domainCmd.AddCommand(dnsCmd) - rootCmd.AddCommand(domainCmd) -} diff --git a/internal/cmd/provider.go b/internal/cmd/provider.go deleted file mode 100644 index 0e7118b..0000000 --- a/internal/cmd/provider.go +++ /dev/null @@ -1,94 +0,0 @@ -package cmd - -import ( - "fmt" - - "git.yoresee.cc/nightstar/ry/internal/config" - "git.yoresee.cc/nightstar/ry/internal/provider" - - "github.com/spf13/cobra" -) - -var providerCmd = &cobra.Command{ - Use: "provider", - Short: "Manage cloud providers", -} - -var providerListCmd = &cobra.Command{ - Use: "list", - Short: "List configured providers", - Annotations: map[string]string{"skipProvider": "true"}, - RunE: func(cmd *cobra.Command, args []string) error { - // show all registered providers, mark current with * - current := "" - if cfg != nil { - current = cfg.CurrentProvider - } - for _, name := range provider.List() { - marker := " " - if name == current { - marker = "*" - } - fmt.Printf("%s %s\n", marker, name) - } - return nil - }, -} - -var providerUseCmd = &cobra.Command{ - Use: "use ", - Short: "Switch the current cloud provider", - Annotations: map[string]string{"skipProvider": "true"}, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - name := args[0] - if _, ok := cfg.Providers[name]; !ok { - return fmt.Errorf("provider %q not configured — run 'ry provider add %s' first", name, name) - } - cfg.CurrentProvider = name - if err := config.Save(cfgPath, cfg); err != nil { - return fmt.Errorf("save config: %w", err) - } - fmt.Printf("Switched to provider %q\n", name) - return nil - }, -} - -var providerAddCmd = &cobra.Command{ - Use: "add ", - Short: "Add a new cloud provider", - Annotations: map[string]string{"skipProvider": "true"}, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - name := args[0] - fmt.Printf("Adding provider %q...\n", name) - fmt.Print("Enter API Key: ") - var apiKey string - fmt.Scanln(&apiKey) - if apiKey == "" { - return fmt.Errorf("API Key is required") - } - if cfg.Providers == nil { - cfg.Providers = make(map[string]config.ProviderConfig) - } - cfg.Providers[name] = config.ProviderConfig{APIKey: apiKey} - if cfg.CurrentProvider == "" { - cfg.CurrentProvider = name - } - if err := config.Save(cfgPath, cfg); err != nil { - return fmt.Errorf("save config: %w", err) - } - fmt.Printf("Provider %q added successfully\n", name) - if cfg.CurrentProvider == name { - fmt.Printf("Set as current provider\n") - } - return nil - }, -} - -func init() { - providerCmd.AddCommand(providerListCmd) - providerCmd.AddCommand(providerUseCmd) - providerCmd.AddCommand(providerAddCmd) - rootCmd.AddCommand(providerCmd) -} diff --git a/internal/cmd/server.go b/internal/cmd/server.go deleted file mode 100644 index d2a3fb7..0000000 --- a/internal/cmd/server.go +++ /dev/null @@ -1,177 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -var serverCmd = &cobra.Command{ - Use: "server", - Short: "Manage cloud servers", -} - -var serverListCmd = &cobra.Command{ - Use: "list", - Short: "List servers", - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Server() - if svc == nil { - return fmt.Errorf("server management not supported by current provider") - } - servers, err := svc.List(cmd.Context()) - if err != nil { - return err - } - if flagRaw { - var raw []any - for _, s := range servers { - raw = append(raw, s.Raw) - } - return out.Print(raw) - } - return out.Print(servers) - }, -} - -var serverGetCmd = &cobra.Command{ - Use: "get ", - Short: "Get server details", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Server() - if svc == nil { - return fmt.Errorf("server management not supported") - } - s, err := svc.Get(cmd.Context(), args[0]) - if err != nil { - return err - } - if flagRaw { - return out.Print(s.Raw) - } - return out.Print(s) - }, -} - -var serverStartCmd = &cobra.Command{ - Use: "start ", - Short: "Start a server", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Server() - if svc == nil { - return fmt.Errorf("server management not supported") - } - if err := svc.Start(cmd.Context(), args[0]); err != nil { - return err - } - fmt.Printf("Server %s started\n", args[0]) - return nil - }, -} - -var serverStopCmd = &cobra.Command{ - Use: "stop ", - Short: "Stop a server", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Server() - if svc == nil { - return fmt.Errorf("server management not supported") - } - if err := svc.Stop(cmd.Context(), args[0]); err != nil { - return err - } - fmt.Printf("Server %s stopped\n", args[0]) - return nil - }, -} - -var serverRebootCmd = &cobra.Command{ - Use: "reboot ", - Short: "Reboot a server", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Server() - if svc == nil { - return fmt.Errorf("server management not supported") - } - if err := svc.Reboot(cmd.Context(), args[0]); err != nil { - return err - } - fmt.Printf("Server %s rebooted\n", args[0]) - return nil - }, -} - -var serverReinstallCmd = &cobra.Command{ - Use: "reinstall ", - Short: "Reinstall server OS", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Server() - if svc == nil { - return fmt.Errorf("server management not supported") - } - osTemplate, _ := cmd.Flags().GetString("os") - if osTemplate == "" { - return fmt.Errorf("--os flag is required (e.g. --os ubuntu-22.04)") - } - if err := svc.Reinstall(cmd.Context(), args[0], osTemplate); err != nil { - return err - } - fmt.Printf("Server %s reinstalling with %s\n", args[0], osTemplate) - return nil - }, -} - -var serverResetPasswordCmd = &cobra.Command{ - Use: "reset-password ", - Short: "Reset server password", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Server() - if svc == nil { - return fmt.Errorf("server management not supported") - } - if err := svc.ResetPassword(cmd.Context(), args[0]); err != nil { - return err - } - fmt.Printf("Password reset for server %s\n", args[0]) - return nil - }, -} - -var serverVNCCmd = &cobra.Command{ - Use: "vnc ", - Short: "Get VNC connection URL", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Server() - if svc == nil { - return fmt.Errorf("server management not supported") - } - url, err := svc.GetVNC(cmd.Context(), args[0]) - if err != nil { - return err - } - fmt.Println(url) - return nil - }, -} - -func init() { - serverReinstallCmd.Flags().String("os", "", "OS template name (required)") - serverReinstallCmd.MarkFlagRequired("os") - - serverCmd.AddCommand(serverListCmd) - serverCmd.AddCommand(serverGetCmd) - serverCmd.AddCommand(serverStartCmd) - serverCmd.AddCommand(serverStopCmd) - serverCmd.AddCommand(serverRebootCmd) - serverCmd.AddCommand(serverReinstallCmd) - serverCmd.AddCommand(serverResetPasswordCmd) - serverCmd.AddCommand(serverVNCCmd) - rootCmd.AddCommand(serverCmd) -} diff --git a/internal/cmd/storage.go b/internal/cmd/storage.go deleted file mode 100644 index 1e9655f..0000000 --- a/internal/cmd/storage.go +++ /dev/null @@ -1,75 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -var storageCmd = &cobra.Command{ - Use: "storage", - Short: "Manage object storage", -} - -var storageListCmd = &cobra.Command{ - Use: "list", - Short: "List storage instances", - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Storage() - if svc == nil { - return fmt.Errorf("storage management not supported by current provider") - } - instances, err := svc.List(cmd.Context()) - if err != nil { - return err - } - return out.Print(instances) - }, -} - -var bucketCmd = &cobra.Command{ - Use: "bucket ", - Short: "Manage buckets", - Args: cobra.ExactArgs(1), -} - -var bucketListCmd = &cobra.Command{ - Use: "list", - Short: "List buckets", - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Storage() - if svc == nil { - return fmt.Errorf("storage management not supported") - } - buckets, err := svc.BucketList(cmd.Context(), args[0]) - if err != nil { - return err - } - return out.Print(buckets) - }, -} - -var bucketCreateCmd = &cobra.Command{ - Use: "create ", - Short: "Create a bucket", - Args: cobra.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - svc := prov.Storage() - if svc == nil { - return fmt.Errorf("storage management not supported") - } - if err := svc.BucketCreate(cmd.Context(), args[0], args[1]); err != nil { - return err - } - fmt.Printf("Bucket %q created\n", args[1]) - return nil - }, -} - -func init() { - bucketCmd.AddCommand(bucketListCmd) - bucketCmd.AddCommand(bucketCreateCmd) - storageCmd.AddCommand(storageListCmd) - storageCmd.AddCommand(bucketCmd) - rootCmd.AddCommand(storageCmd) -}