diff --git a/cli/internal/validate.go b/cli/internal/validate.go index 2a1c0cfa..b177e9d6 100644 --- a/cli/internal/validate.go +++ b/cli/internal/validate.go @@ -126,3 +126,31 @@ func ValidateSubCmdFlags(c *cli.Context) bool { } return true } + +// ValidateServer ensures the server name arg is valid. +func ValidateServer(c *cli.Context) (string, error) { + serverName := c.Args().First() + + serverNames, err := config.ReadServerNames() + if err != nil { + return serverName, err + } + + if serverName == "" && !NoInput { + prompt := &survey.Select{ + Message: "Specify a server name", + Options: serverNames, + } + if err := survey.AskOne(prompt, &serverName); err != nil { + return serverName, err + } + } + + if serverName == "" { + ShowSubcommandHelpAndError(c, errors.New("no server provided")) + } + + logrus.Debugf("validated '%s' as server argument", serverName) + + return serverName, nil +} diff --git a/cli/server/add.go b/cli/server/add.go index 8755c862..6e2fd29f 100644 --- a/cli/server/add.go +++ b/cli/server/add.go @@ -466,7 +466,7 @@ You may omit flags to avoid performing this provisioning logic. if _, err := cl.Info(c.Context); err != nil { cleanUp(domainName) - logrus.Fatalf("couldn't make a remote docker connection to %s?", domainName) + logrus.Fatalf("couldn't make a remote docker connection to %s? use --provision/-p to attempt to install", domainName) } if traefik { diff --git a/cli/server/remove.go b/cli/server/remove.go index 114a79c3..b58b7e0b 100644 --- a/cli/server/remove.go +++ b/cli/server/remove.go @@ -1,12 +1,16 @@ package server import ( + "fmt" "os" "path/filepath" + "coopcloud.tech/abra/cli/formatter" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" + "github.com/AlecAivazis/survey/v2" + "github.com/hetznercloud/hcloud-go/hcloud" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) @@ -16,21 +20,92 @@ var rmServerFlag = &cli.BoolFlag{ Name: "server", Aliases: []string{"s"}, Value: false, - Usage: "Remove the actual server also", + Usage: "remove the actual server also", Destination: &rmServer, } func rmHetznerCloudVPS(c *cli.Context) error { - logrus.Warn("NOT IMPLEMENTED - COMING SOON") + if internal.HetznerCloudName == "" && !internal.NoInput { + prompt := &survey.Input{ + Message: "specify hetzner cloud VPS name", + } + if err := survey.AskOne(prompt, &internal.HetznerCloudName); err != nil { + return err + } + } + + if internal.HetznerCloudAPIToken == "" && !internal.NoInput { + token, ok := os.LookupEnv("HCLOUD_TOKEN") + if !ok { + prompt := &survey.Input{ + Message: "specify hetzner cloud API token", + } + if err := survey.AskOne(prompt, &internal.HetznerCloudAPIToken); err != nil { + return err + } + } else { + internal.HetznerCloudAPIToken = token + } + } + + client := hcloud.NewClient(hcloud.WithToken(internal.HetznerCloudAPIToken)) + + server, _, err := client.Server.Get(c.Context, internal.HetznerCloudName) + if err != nil { + return err + } + + if server == nil { + logrus.Fatalf("library provider reports that %s doesn't exist?", internal.HetznerCloudName) + } + + fmt.Println(fmt.Sprintf(` +You have requested that Abra delete the following server (%s). Please be +absolutely sure that this is indeed the server that you would like to have +removed. There will be no going back once you confirm, the server will be +destroyed. +`, server.Name)) + + tableColumns := []string{"name", "type", "image", "location"} + table := formatter.CreateTable(tableColumns) + table.Append([]string{ + server.Name, + server.ServerType.Name, + server.Image.Name, + server.Datacenter.Name, + }) + table.Render() + + response := false + prompt := &survey.Confirm{ + Message: "continue with hetzner cloud VPS removal?", + } + + if err := survey.AskOne(prompt, &response); err != nil { + return err + } + + if !response { + logrus.Fatal("exiting as requested") + } + + _, err = client.Server.Delete(c.Context, server) + if err != nil { + return err + } + + logrus.Infof("%s has been deleted from your hetzner cloud account", internal.HetznerCloudName) + return nil } var serverRemoveCommand = &cli.Command{ - Name: "remove", - Aliases: []string{"rm"}, - Usage: "Remove a managed server", + Name: "remove", + Aliases: []string{"rm"}, + ArgsUsage: "", + Usage: "Remove a managed server", Description: ` -This command removes a server from being managed by Abra. +This command removes a server from Abra management. Depending on whether you used a 3rd party provider to create this server ("abra server new"), you can also destroy the virtual server as well. Pass @@ -42,10 +117,13 @@ like tears in rain. `, Flags: []cli.Flag{ rmServerFlag, - internal.DNSProviderFlag, + + // Hetzner + internal.HetznerCloudNameFlag, + internal.HetznerCloudAPITokenFlag, }, Action: func(c *cli.Context) error { - domainName, err := internal.ValidateDomain(c) + serverName, err := internal.ValidateServer(c) if err != nil { logrus.Fatal(err) } @@ -66,15 +144,15 @@ like tears in rain. } - if err := client.DeleteContext(domainName); err != nil { + if err := client.DeleteContext(serverName); err != nil { logrus.Fatal(err) } - if err := os.RemoveAll(filepath.Join(config.ABRA_SERVER_FOLDER, domainName)); err != nil { + if err := os.RemoveAll(filepath.Join(config.ABRA_SERVER_FOLDER, serverName)); err != nil { logrus.Fatal(err) } - logrus.Infof("server at '%s' has been lost in time, like tears in rain", domainName) + logrus.Infof("server at '%s' has been lost in time, like tears in rain", serverName) return nil },