From ab7edd2a6296aec5e061c65dc56eeb14355a8b05 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Thu, 14 Sep 2023 10:02:43 +0200 Subject: [PATCH] refactor!: drop "record" & "server new" command These were alpha prototypes and we'll reconsider once other layers of Abra are more stable. --- cli/cli.go | 2 - cli/internal/cli.go | 168 ------------------ cli/internal/validate.go | 296 ------------------------------- cli/record/list.go | 83 --------- cli/record/new.go | 149 ---------------- cli/record/record.go | 37 ---- cli/record/remove.go | 137 -------------- cli/server/new.go | 261 --------------------------- cli/server/remove.go | 147 +-------------- cli/server/server.go | 9 - go.mod | 6 +- go.sum | 9 - pkg/autocomplete/autocomplete.go | 1 - pkg/dns/{common.go => dns.go} | 33 ---- pkg/dns/gandi/gandi.go | 15 -- 15 files changed, 6 insertions(+), 1347 deletions(-) delete mode 100644 cli/record/list.go delete mode 100644 cli/record/new.go delete mode 100644 cli/record/record.go delete mode 100644 cli/record/remove.go delete mode 100644 cli/server/new.go rename pkg/dns/{common.go => dns.go} (59%) delete mode 100644 pkg/dns/gandi/gandi.go diff --git a/cli/cli.go b/cli/cli.go index b38833f9..3da74228 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -12,7 +12,6 @@ import ( "coopcloud.tech/abra/cli/catalogue" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/cli/recipe" - "coopcloud.tech/abra/cli/record" "coopcloud.tech/abra/cli/server" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/config" @@ -159,7 +158,6 @@ func newAbraApp(version, commit string) *cli.App { server.ServerCommand, recipe.RecipeCommand, catalogue.CatalogueCommand, - record.RecordCommand, UpgradeCommand, AutoCompleteCommand, }, diff --git a/cli/internal/cli.go b/cli/internal/cli.go index 6c715c43..07e16ac5 100644 --- a/cli/internal/cli.go +++ b/cli/internal/cli.go @@ -68,17 +68,6 @@ var TtyFlag = &cli.BoolFlag{ Destination: &Tty, } -// DNSProvider specifies a DNS provider. -var DNSProvider string - -// DNSProviderFlag selects a DNS provider. -var DNSProviderFlag = &cli.StringFlag{ - Name: "provider, p", - Value: "", - Usage: "DNS provider", - Destination: &DNSProvider, -} - var NoInput bool var NoInputFlag = &cli.BoolFlag{ Name: "no-input, n", @@ -86,163 +75,6 @@ var NoInputFlag = &cli.BoolFlag{ Destination: &NoInput, } -var DNSType string - -var DNSTypeFlag = &cli.StringFlag{ - Name: "record-type, rt", - Value: "", - Usage: "Domain name record type (e.g. A)", - Destination: &DNSType, -} - -var DNSName string - -var DNSNameFlag = &cli.StringFlag{ - Name: "record-name, rn", - Value: "", - Usage: "Domain name record name (e.g. mysubdomain)", - Destination: &DNSName, -} - -var DNSValue string - -var DNSValueFlag = &cli.StringFlag{ - Name: "record-value, rv", - Value: "", - Usage: "Domain name record value (e.g. 192.168.1.1)", - Destination: &DNSValue, -} - -var DNSTTL string -var DNSTTLFlag = &cli.StringFlag{ - Name: "record-ttl, rl", - Value: "600s", - Usage: "Domain name TTL value (seconds)", - Destination: &DNSTTL, -} - -var DNSPriority int - -var DNSPriorityFlag = &cli.IntFlag{ - Name: "record-priority, rp", - Value: 10, - Usage: "Domain name priority value", - Destination: &DNSPriority, -} - -var ServerProvider string - -var ServerProviderFlag = &cli.StringFlag{ - Name: "provider, p", - Usage: "3rd party server provider", - Destination: &ServerProvider, -} - -var CapsulInstanceURL string - -var CapsulInstanceURLFlag = &cli.StringFlag{ - Name: "capsul-url, cu", - Value: "yolo.servers.coop", - Usage: "capsul instance URL", - Destination: &CapsulInstanceURL, -} - -var CapsulName string - -var CapsulNameFlag = &cli.StringFlag{ - Name: "capsul-name, cn", - Value: "", - Usage: "capsul name", - Destination: &CapsulName, -} - -var CapsulType string - -var CapsulTypeFlag = &cli.StringFlag{ - Name: "capsul-type, ct", - Value: "f1-xs", - Usage: "capsul type", - Destination: &CapsulType, -} - -var CapsulImage string - -var CapsulImageFlag = &cli.StringFlag{ - Name: "capsul-image, ci", - Value: "debian10", - Usage: "capsul image", - Destination: &CapsulImage, -} - -var CapsulSSHKeys cli.StringSlice -var CapsulSSHKeysFlag = &cli.StringSliceFlag{ - Name: "capsul-ssh-keys, cs", - Usage: "capsul SSH key", - Value: &CapsulSSHKeys, -} - -var CapsulAPIToken string - -var CapsulAPITokenFlag = &cli.StringFlag{ - Name: "capsul-token, ca", - Usage: "capsul API token", - EnvVar: "CAPSUL_TOKEN", - Destination: &CapsulAPIToken, -} - -var HetznerCloudName string - -var HetznerCloudNameFlag = &cli.StringFlag{ - Name: "hetzner-name, hn", - Value: "", - Usage: "hetzner cloud name", - Destination: &HetznerCloudName, -} - -var HetznerCloudType string - -var HetznerCloudTypeFlag = &cli.StringFlag{ - Name: "hetzner-type, ht", - Usage: "hetzner cloud type", - Destination: &HetznerCloudType, - Value: "cx11", -} - -var HetznerCloudImage string - -var HetznerCloudImageFlag = &cli.StringFlag{ - Name: "hetzner-image, hi", - Usage: "hetzner cloud image", - Value: "debian-10", - Destination: &HetznerCloudImage, -} - -var HetznerCloudSSHKeys cli.StringSlice - -var HetznerCloudSSHKeysFlag = &cli.StringSliceFlag{ - Name: "hetzner-ssh-keys, hs", - Usage: "hetzner cloud SSH keys (e.g. me@foo.com)", - Value: &HetznerCloudSSHKeys, -} - -var HetznerCloudLocation string - -var HetznerCloudLocationFlag = &cli.StringFlag{ - Name: "hetzner-location, hl", - Usage: "hetzner cloud server location", - Value: "hel1", - Destination: &HetznerCloudLocation, -} - -var HetznerCloudAPIToken string - -var HetznerCloudAPITokenFlag = &cli.StringFlag{ - Name: "hetzner-token, ha", - Usage: "hetzner cloud API token", - EnvVar: "HCLOUD_TOKEN", - Destination: &HetznerCloudAPIToken, -} - // Debug stores the variable from DebugFlag. var Debug bool diff --git a/cli/internal/validate.go b/cli/internal/validate.go index e04948a1..05bc7fdb 100644 --- a/cli/internal/validate.go +++ b/cli/internal/validate.go @@ -2,8 +2,6 @@ package internal import ( "errors" - "fmt" - "os" "strings" "coopcloud.tech/abra/pkg/app" @@ -204,297 +202,3 @@ func ValidateServer(c *cli.Context) string { return serverName } - -// EnsureDNSProvider ensures a DNS provider is chosen. -func EnsureDNSProvider() error { - if DNSProvider == "" && !NoInput { - prompt := &survey.Select{ - Message: "Select DNS provider", - Options: []string{"gandi"}, - } - - if err := survey.AskOne(prompt, &DNSProvider); err != nil { - return err - } - } - - if DNSProvider == "" { - return fmt.Errorf("missing DNS provider?") - } - - return nil -} - -// EnsureDNSTypeFlag ensures a DNS type flag is present. -func EnsureDNSTypeFlag(c *cli.Context) error { - if DNSType == "" && !NoInput { - prompt := &survey.Input{ - Message: "Specify DNS record type", - Default: "A", - } - if err := survey.AskOne(prompt, &DNSType); err != nil { - return err - } - } - - if DNSType == "" { - ShowSubcommandHelpAndError(c, errors.New("no record type provided")) - } - - return nil -} - -// EnsureDNSNameFlag ensures a DNS name flag is present. -func EnsureDNSNameFlag(c *cli.Context) error { - if DNSName == "" && !NoInput { - prompt := &survey.Input{ - Message: "Specify DNS record name", - Default: "mysubdomain", - } - if err := survey.AskOne(prompt, &DNSName); err != nil { - return err - } - } - - if DNSName == "" { - ShowSubcommandHelpAndError(c, errors.New("no record name provided")) - } - - return nil -} - -// EnsureDNSValueFlag ensures a DNS value flag is present. -func EnsureDNSValueFlag(c *cli.Context) error { - if DNSValue == "" && !NoInput { - prompt := &survey.Input{ - Message: "Specify DNS record value", - Default: "192.168.1.2", - } - if err := survey.AskOne(prompt, &DNSValue); err != nil { - return err - } - } - - if DNSValue == "" { - ShowSubcommandHelpAndError(c, errors.New("no record value provided")) - } - - return nil -} - -// EnsureZoneArgument ensures a zone argument is present. -func EnsureZoneArgument(c *cli.Context) (string, error) { - zone := c.Args().First() - - if zone == "" && !NoInput { - prompt := &survey.Input{ - Message: "Specify a domain name zone", - Default: "example.com", - } - - if err := survey.AskOne(prompt, &zone); err != nil { - return zone, err - } - } - - if zone == "" { - ShowSubcommandHelpAndError(c, errors.New("no zone value provided")) - } - - return zone, nil -} - -// EnsureServerProvider ensures a 3rd party server provider is chosen. -func EnsureServerProvider() error { - if ServerProvider == "" && !NoInput { - prompt := &survey.Select{ - Message: "Select server provider", - Options: []string{"capsul", "hetzner-cloud"}, - } - - if err := survey.AskOne(prompt, &ServerProvider); err != nil { - return err - } - } - - if ServerProvider == "" { - return fmt.Errorf("missing server provider?") - } - - return nil -} - -// EnsureNewCapsulVPSFlags ensure all flags are present. -func EnsureNewCapsulVPSFlags(c *cli.Context) error { - if CapsulName == "" && !NoInput { - prompt := &survey.Input{ - Message: "specify capsul name", - } - if err := survey.AskOne(prompt, &CapsulName); err != nil { - return err - } - } - - if !NoInput { - prompt := &survey.Input{ - Message: "specify capsul instance URL", - Default: CapsulInstanceURL, - } - if err := survey.AskOne(prompt, &CapsulInstanceURL); err != nil { - return err - } - } - - if !NoInput { - prompt := &survey.Input{ - Message: "specify capsul type", - Default: CapsulType, - } - if err := survey.AskOne(prompt, &CapsulType); err != nil { - return err - } - } - - if !NoInput { - prompt := &survey.Input{ - Message: "specify capsul image", - Default: CapsulImage, - } - if err := survey.AskOne(prompt, &CapsulImage); err != nil { - return err - } - } - - if len(CapsulSSHKeys.Value()) == 0 && !NoInput { - var sshKeys string - prompt := &survey.Input{ - Message: "specify capsul SSH keys (e.g. me@foo.com)", - Default: "", - } - if err := survey.AskOne(prompt, &CapsulSSHKeys); err != nil { - return err - } - CapsulSSHKeys = cli.StringSlice(strings.Split(sshKeys, ",")) - } - - if CapsulAPIToken == "" && !NoInput { - token, ok := os.LookupEnv("CAPSUL_TOKEN") - if !ok { - prompt := &survey.Input{ - Message: "specify capsul API token", - } - if err := survey.AskOne(prompt, &CapsulAPIToken); err != nil { - return err - } - } else { - CapsulAPIToken = token - } - } - - if CapsulName == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul name?")) - } - if CapsulInstanceURL == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul instance url?")) - } - if CapsulType == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul type?")) - } - if CapsulImage == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul image?")) - } - if len(CapsulSSHKeys.Value()) == 0 { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul ssh keys?")) - } - if CapsulAPIToken == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul API token?")) - } - - return nil -} - -// EnsureNewHetznerCloudVPSFlags ensure all flags are present. -func EnsureNewHetznerCloudVPSFlags(c *cli.Context) error { - if HetznerCloudName == "" && !NoInput { - prompt := &survey.Input{ - Message: "specify hetzner cloud VPS name", - } - if err := survey.AskOne(prompt, &HetznerCloudName); err != nil { - return err - } - } - - if !NoInput { - prompt := &survey.Input{ - Message: "specify hetzner cloud VPS type", - Default: HetznerCloudType, - } - if err := survey.AskOne(prompt, &HetznerCloudType); err != nil { - return err - } - } - - if !NoInput { - prompt := &survey.Input{ - Message: "specify hetzner cloud VPS image", - Default: HetznerCloudImage, - } - if err := survey.AskOne(prompt, &HetznerCloudImage); err != nil { - return err - } - } - - if len(HetznerCloudSSHKeys.Value()) == 0 && !NoInput { - var sshKeys string - prompt := &survey.Input{ - Message: "specify hetzner cloud SSH keys (e.g. me@foo.com)", - Default: "", - } - if err := survey.AskOne(prompt, &sshKeys); err != nil { - return err - } - HetznerCloudSSHKeys = cli.StringSlice(strings.Split(sshKeys, ",")) - } - - if !NoInput { - prompt := &survey.Input{ - Message: "specify hetzner cloud VPS location", - Default: HetznerCloudLocation, - } - if err := survey.AskOne(prompt, &HetznerCloudLocation); err != nil { - return err - } - } - - if HetznerCloudAPIToken == "" && !NoInput { - token, ok := os.LookupEnv("HCLOUD_TOKEN") - if !ok { - prompt := &survey.Input{ - Message: "specify hetzner cloud API token", - } - if err := survey.AskOne(prompt, &HetznerCloudAPIToken); err != nil { - return err - } - } else { - HetznerCloudAPIToken = token - } - } - - if HetznerCloudName == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS name?")) - } - if HetznerCloudType == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS type?")) - } - if HetznerCloudImage == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud image?")) - } - if HetznerCloudLocation == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS location?")) - } - if HetznerCloudAPIToken == "" { - ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud API token?")) - } - - return nil -} diff --git a/cli/record/list.go b/cli/record/list.go deleted file mode 100644 index 054caccb..00000000 --- a/cli/record/list.go +++ /dev/null @@ -1,83 +0,0 @@ -package record - -import ( - "context" - "fmt" - "strconv" - - "coopcloud.tech/abra/cli/internal" - gandiPkg "coopcloud.tech/abra/pkg/dns/gandi" - "coopcloud.tech/abra/pkg/formatter" - "github.com/libdns/gandi" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" -) - -// RecordListCommand lists domains. -var RecordListCommand = cli.Command{ - Name: "list", - Usage: "List domain name records", - Aliases: []string{"ls"}, - ArgsUsage: "", - Flags: []cli.Flag{ - internal.DebugFlag, - internal.DNSProviderFlag, - internal.OfflineFlag, - }, - Before: internal.SubCommandBefore, - Description: ` -List all domain name records managed by a 3rd party provider for a specific -zone. - -You must specify a zone (e.g. example.com) under which your domain name records -are listed. This zone must already be created on your provider account. -`, - Action: func(c *cli.Context) error { - if err := internal.EnsureDNSProvider(); err != nil { - logrus.Fatal(err) - } - - zone, err := internal.EnsureZoneArgument(c) - if err != nil { - logrus.Fatal(err) - } - - var provider gandi.Provider - switch internal.DNSProvider { - case "gandi": - provider, err = gandiPkg.New() - if err != nil { - logrus.Fatal(err) - } - default: - logrus.Fatalf("%s is not a supported DNS provider", internal.DNSProvider) - } - - records, err := provider.GetRecords(context.Background(), zone) - if err != nil { - logrus.Fatal(err) - } - - tableCol := []string{"type", "name", "value", "TTL", "priority"} - table := formatter.CreateTable(tableCol) - - for _, record := range records { - value := record.Value - if len(record.Value) > 30 { - value = fmt.Sprintf("%s...", record.Value[:30]) - } - - table.Append([]string{ - record.Type, - record.Name, - value, - record.TTL.String(), - strconv.Itoa(record.Priority), - }) - } - - table.Render() - - return nil - }, -} diff --git a/cli/record/new.go b/cli/record/new.go deleted file mode 100644 index cebf310b..00000000 --- a/cli/record/new.go +++ /dev/null @@ -1,149 +0,0 @@ -package record - -import ( - "context" - "fmt" - "strconv" - - "coopcloud.tech/abra/cli/internal" - "coopcloud.tech/abra/pkg/dns" - gandiPkg "coopcloud.tech/abra/pkg/dns/gandi" - "coopcloud.tech/abra/pkg/formatter" - "github.com/libdns/gandi" - "github.com/libdns/libdns" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" -) - -// RecordNewCommand creates a new domain name record. -var RecordNewCommand = cli.Command{ - Name: "new", - Usage: "Create a new domain record", - Aliases: []string{"n"}, - ArgsUsage: "", - Flags: []cli.Flag{ - internal.DebugFlag, - internal.NoInputFlag, - internal.DNSProviderFlag, - internal.DNSTypeFlag, - internal.DNSNameFlag, - internal.DNSValueFlag, - internal.DNSTTLFlag, - internal.DNSPriorityFlag, - internal.OfflineFlag, - }, - Before: internal.SubCommandBefore, - Description: ` -Create a new domain name record for a specific zone. - -You must specify a zone (e.g. example.com) under which your domain name records -are listed. This zone must already be created on your provider account. - -Example: - - abra record new foo.com -p gandi -t A -n myapp -v 192.168.178.44 - -You may also invoke this command in "wizard" mode and be prompted for input: - - abra record new -`, - Action: func(c *cli.Context) error { - zone, err := internal.EnsureZoneArgument(c) - if err != nil { - logrus.Fatal(err) - } - - if err := internal.EnsureDNSProvider(); err != nil { - logrus.Fatal(err) - } - - var provider gandi.Provider - switch internal.DNSProvider { - case "gandi": - provider, err = gandiPkg.New() - if err != nil { - logrus.Fatal(err) - } - default: - logrus.Fatalf("%s is not a supported DNS provider", internal.DNSProvider) - } - - if err := internal.EnsureDNSTypeFlag(c); err != nil { - logrus.Fatal(err) - } - - if err := internal.EnsureDNSNameFlag(c); err != nil { - logrus.Fatal(err) - } - - if err := internal.EnsureDNSValueFlag(c); err != nil { - logrus.Fatal(err) - } - - ttl, err := dns.GetTTL(internal.DNSTTL) - if err != nil { - return err - } - - record := libdns.Record{ - Type: internal.DNSType, - Name: internal.DNSName, - Value: internal.DNSValue, - TTL: ttl, - } - - if internal.DNSType == "MX" || internal.DNSType == "SRV" || internal.DNSType == "URI" { - record.Priority = internal.DNSPriority - } - - records, err := provider.GetRecords(context.Background(), zone) - if err != nil { - logrus.Fatal(err) - } - - for _, existingRecord := range records { - if existingRecord.Type == record.Type && - existingRecord.Name == record.Name && - existingRecord.Value == record.Value { - logrus.Fatalf("%s record for %s already exists?", record.Type, zone) - } - } - - createdRecords, err := provider.SetRecords( - context.Background(), - zone, - []libdns.Record{record}, - ) - if err != nil { - logrus.Fatal(err) - } - - if len(createdRecords) == 0 { - logrus.Fatal("provider library reports that no record was created?") - } - - createdRecord := createdRecords[0] - - tableCol := []string{"type", "name", "value", "TTL", "priority"} - table := formatter.CreateTable(tableCol) - - value := createdRecord.Value - if len(createdRecord.Value) > 30 { - value = fmt.Sprintf("%s...", createdRecord.Value[:30]) - } - - table.Append([]string{ - createdRecord.Type, - createdRecord.Name, - value, - createdRecord.TTL.String(), - strconv.Itoa(createdRecord.Priority), - }) - - table.Render() - - logrus.Info("record created") - - return nil - }, -} diff --git a/cli/record/record.go b/cli/record/record.go deleted file mode 100644 index 80603804..00000000 --- a/cli/record/record.go +++ /dev/null @@ -1,37 +0,0 @@ -package record - -import ( - "github.com/urfave/cli" -) - -// RecordCommand supports managing DNS entries. -var RecordCommand = cli.Command{ - Name: "record", - Usage: "Manage domain name records", - Aliases: []string{"rc"}, - ArgsUsage: "", - Description: ` -Manage domain name records via 3rd party providers such as Gandi DNS. It -supports listing, creating and removing all types of records that you might -need for managing Co-op Cloud apps. - -The following providers are supported: - - Gandi DNS https://www.gandi.net - -You need an account with such a provider already. Typically, you need to -provide an API token on the Abra command-line when using these commands so that -you can authenticate with your provider account. - -New providers can be integrated, we welcome change sets. See the underlying DNS -library documentation for more. It supports many existing providers and allows -to implement new provider support easily. - - https://pkg.go.dev/github.com/libdns/libdns -`, - Subcommands: []cli.Command{ - RecordListCommand, - RecordNewCommand, - RecordRemoveCommand, - }, -} diff --git a/cli/record/remove.go b/cli/record/remove.go deleted file mode 100644 index 4208e03a..00000000 --- a/cli/record/remove.go +++ /dev/null @@ -1,137 +0,0 @@ -package record - -import ( - "context" - "fmt" - "strconv" - - "coopcloud.tech/abra/cli/internal" - gandiPkg "coopcloud.tech/abra/pkg/dns/gandi" - "coopcloud.tech/abra/pkg/formatter" - "github.com/AlecAivazis/survey/v2" - "github.com/libdns/gandi" - "github.com/libdns/libdns" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" -) - -// RecordRemoveCommand lists domains. -var RecordRemoveCommand = cli.Command{ - Name: "remove", - Usage: "Remove a domain name record", - Aliases: []string{"rm"}, - ArgsUsage: "", - Flags: []cli.Flag{ - internal.DebugFlag, - internal.NoInputFlag, - internal.DNSProviderFlag, - internal.DNSTypeFlag, - internal.DNSNameFlag, - internal.OfflineFlag, - }, - Before: internal.SubCommandBefore, - Description: ` -Remove a domain name record for a specific zone. - -It uses the type of record and name to match existing records and choose one -for deletion. You must specify a zone (e.g. example.com) under which your -domain name records are listed. This zone must already be created on your -provider account. - -Example: - - abra record remove foo.com -p gandi -t A -n myapp - -You may also invoke this command in "wizard" mode and be prompted for input: - - abra record rm -`, - Action: func(c *cli.Context) error { - zone, err := internal.EnsureZoneArgument(c) - if err != nil { - logrus.Fatal(err) - } - - if err := internal.EnsureDNSProvider(); err != nil { - logrus.Fatal(err) - } - - var provider gandi.Provider - switch internal.DNSProvider { - case "gandi": - provider, err = gandiPkg.New() - if err != nil { - logrus.Fatal(err) - } - default: - logrus.Fatalf("%s is not a supported DNS provider", internal.DNSProvider) - } - - if err := internal.EnsureDNSTypeFlag(c); err != nil { - logrus.Fatal(err) - } - - if err := internal.EnsureDNSNameFlag(c); err != nil { - logrus.Fatal(err) - } - - records, err := provider.GetRecords(context.Background(), zone) - if err != nil { - logrus.Fatal(err) - } - - var toDelete libdns.Record - for _, record := range records { - if record.Type == internal.DNSType && record.Name == internal.DNSName { - toDelete = record - break - } - } - - if (libdns.Record{}) == toDelete { - logrus.Fatal("provider library reports no matching record?") - } - - tableCol := []string{"type", "name", "value", "TTL", "priority"} - table := formatter.CreateTable(tableCol) - - value := toDelete.Value - if len(toDelete.Value) > 30 { - value = fmt.Sprintf("%s...", toDelete.Value[:30]) - } - - table.Append([]string{ - toDelete.Type, - toDelete.Name, - value, - toDelete.TTL.String(), - strconv.Itoa(toDelete.Priority), - }) - - table.Render() - - if !internal.NoInput { - response := false - prompt := &survey.Confirm{ - Message: "continue with record deletion?", - } - - if err := survey.AskOne(prompt, &response); err != nil { - return err - } - - if !response { - logrus.Fatal("exiting as requested") - } - } - - _, err = provider.DeleteRecords(context.Background(), zone, []libdns.Record{toDelete}) - if err != nil { - logrus.Fatal(err) - } - - logrus.Info("record successfully deleted") - - return nil - }, -} diff --git a/cli/server/new.go b/cli/server/new.go deleted file mode 100644 index 98e28151..00000000 --- a/cli/server/new.go +++ /dev/null @@ -1,261 +0,0 @@ -package server - -import ( - "context" - "fmt" - "strings" - - "coopcloud.tech/abra/cli/internal" - "coopcloud.tech/abra/pkg/formatter" - "coopcloud.tech/libcapsul" - "github.com/AlecAivazis/survey/v2" - "github.com/hetznercloud/hcloud-go/hcloud" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" -) - -func newHetznerCloudVPS(c *cli.Context) error { - if err := internal.EnsureNewHetznerCloudVPSFlags(c); err != nil { - return err - } - - client := hcloud.NewClient(hcloud.WithToken(internal.HetznerCloudAPIToken)) - - var sshKeysRaw []string - var sshKeys []*hcloud.SSHKey - for _, sshKey := range c.StringSlice("hetzner-ssh-keys") { - if sshKey == "" { - continue - } - - sshKey, _, err := client.SSHKey.GetByName(context.Background(), sshKey) - if err != nil { - return err - } - sshKeys = append(sshKeys, sshKey) - sshKeysRaw = append(sshKeysRaw, sshKey.Name) - } - - serverOpts := hcloud.ServerCreateOpts{ - Name: internal.HetznerCloudName, - ServerType: &hcloud.ServerType{Name: internal.HetznerCloudType}, - Image: &hcloud.Image{Name: internal.HetznerCloudImage}, - SSHKeys: sshKeys, - Location: &hcloud.Location{Name: internal.HetznerCloudLocation}, - } - - sshKeyIDs := strings.Join(sshKeysRaw, "\n") - if sshKeyIDs == "" { - sshKeyIDs = "N/A (password auth)" - } - - tableColumns := []string{"name", "type", "image", "ssh-keys", "location"} - table := formatter.CreateTable(tableColumns) - table.Append([]string{ - internal.HetznerCloudName, - internal.HetznerCloudType, - internal.HetznerCloudImage, - sshKeyIDs, - internal.HetznerCloudLocation, - }) - table.Render() - - response := false - prompt := &survey.Confirm{ - Message: "continue with hetzner cloud VPS creation?", - } - - if err := survey.AskOne(prompt, &response); err != nil { - return err - } - - if !response { - logrus.Fatal("exiting as requested") - } - - res, _, err := client.Server.Create(context.Background(), serverOpts) - if err != nil { - return err - } - - var rootPassword string - if len(sshKeys) > 0 { - rootPassword = "N/A (using SSH keys)" - } else { - rootPassword = res.RootPassword - } - - ip := res.Server.PublicNet.IPv4.IP.String() - - fmt.Println(fmt.Sprintf(` -Your new Hetzner Cloud VPS has successfully been created! Here are the details: - - name: %s - IP address: %s - root password: %s - -You can access this new VPS via SSH using the following command: - - ssh root@%s - -Please note, this server is not managed by Abra yet (i.e. "abra server ls" will -not list this server)! You will need to assign a domain name record (manually -or by using "abra record new") and add the server to your Abra configuration -("abra server add") to have a working server that you can deploy Co-op Cloud -apps to. - -When setting up domain name records, you probably want to set up the following -2 A records. This supports deploying apps to your root domain (e.g. -example.com) and other apps on sub-domains (e.g. foo.example.com, -bar.example.com). - - @ 1800 IN A %s - * 1800 IN A %s - `, - internal.HetznerCloudName, ip, rootPassword, - ip, ip, ip, - )) - - return nil -} - -func newCapsulVPS(c *cli.Context) error { - if err := internal.EnsureNewCapsulVPSFlags(c); err != nil { - return err - } - - capsulCreateURL := fmt.Sprintf("https://%s/api/capsul/create", internal.CapsulInstanceURL) - - var sshKeys []string - for _, sshKey := range c.StringSlice("capsul-ssh-keys") { - if sshKey == "" { - continue - } - sshKeys = append(sshKeys, sshKey) - } - - tableColumns := []string{"instance", "name", "type", "image", "ssh-keys"} - table := formatter.CreateTable(tableColumns) - table.Append([]string{ - internal.CapsulInstanceURL, - internal.CapsulName, - internal.CapsulType, - internal.CapsulImage, - strings.Join(sshKeys, "\n"), - }) - table.Render() - - response := false - prompt := &survey.Confirm{ - Message: "continue with capsul creation?", - } - - if err := survey.AskOne(prompt, &response); err != nil { - return err - } - - if !response { - logrus.Fatal("exiting as requested") - } - - capsulClient := libcapsul.New(capsulCreateURL, internal.CapsulAPIToken) - resp, err := capsulClient.Create( - internal.CapsulName, - internal.CapsulType, - internal.CapsulImage, - sshKeys, - ) - if err != nil { - return err - } - - fmt.Println(fmt.Sprintf(` -Your new Capsul has successfully been created! Here are the details: - - Capsul name: %s - Capsul ID: %v - -You will need to log into your Capsul instance web interface to retrieve the IP -address. You can learn all about how to get SSH access to your new Capsul on: - - %s/about-ssh - -Please note, this server is not managed by Abra yet (i.e. "abra server ls" will -not list this server)! You will need to assign a domain name record (manually -or by using "abra record new") and add the server to your Abra configuration -("abra server add") to have a working server that you can deploy Co-op Cloud -apps to. - -When setting up domain name records, you probably want to set up the following -2 A records. This supports deploying apps to your root domain (e.g. -example.com) and other apps on sub-domains (e.g. foo.example.com, -bar.example.com). - - @ 1800 IN A - * 1800 IN A - `, internal.CapsulName, resp.ID, internal.CapsulInstanceURL)) - - return nil -} - -var serverNewCommand = cli.Command{ - Name: "new", - Aliases: []string{"n"}, - Usage: "Create a new server using a 3rd party provider", - Description: ` -Create a new server via a 3rd party provider. - -The following providers are supported: - - Capsul https://git.cyberia.club/Cyberia/capsul-flask - Hetzner Cloud https://docs.hetzner.com/cloud - -You may invoke this command in "wizard" mode and be prompted for input: - - abra record new - -API tokens are read from the environment if specified, e.g. - - export HCLOUD_TOKEN=... -`, - Flags: []cli.Flag{ - internal.DebugFlag, - internal.NoInputFlag, - internal.ServerProviderFlag, - internal.OfflineFlag, - - // Capsul - internal.CapsulInstanceURLFlag, - internal.CapsulTypeFlag, - internal.CapsulImageFlag, - internal.CapsulSSHKeysFlag, - internal.CapsulAPITokenFlag, - - // Hetzner - internal.HetznerCloudNameFlag, - internal.HetznerCloudTypeFlag, - internal.HetznerCloudImageFlag, - internal.HetznerCloudSSHKeysFlag, - internal.HetznerCloudLocationFlag, - internal.HetznerCloudAPITokenFlag, - }, - Before: internal.SubCommandBefore, - Action: func(c *cli.Context) error { - if err := internal.EnsureServerProvider(); err != nil { - logrus.Fatal(err) - } - - switch internal.ServerProvider { - case "capsul": - if err := newCapsulVPS(c); err != nil { - logrus.Fatal(err) - } - case "hetzner-cloud": - if err := newHetznerCloudVPS(c); err != nil { - logrus.Fatal(err) - } - } - - return nil - }, -} diff --git a/cli/server/remove.go b/cli/server/remove.go index b6f303f1..196dc421 100644 --- a/cli/server/remove.go +++ b/cli/server/remove.go @@ -1,8 +1,6 @@ package server import ( - "context" - "fmt" "os" "path/filepath" @@ -10,168 +8,31 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" - "coopcloud.tech/abra/pkg/formatter" - "github.com/AlecAivazis/survey/v2" - "github.com/hetznercloud/hcloud-go/hcloud" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) -var rmServer bool -var rmServerFlag = &cli.BoolFlag{ - Name: "server, s", - Usage: "remove the actual server also", - Destination: &rmServer, -} - -func rmHetznerCloudVPS(c *cli.Context) error { - 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(context.Background(), 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(context.Background(), 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"}, ArgsUsage: "[]", Usage: "Remove a managed server", - Description: ` -Remova a server from Abra management. + Description: `Remove a managed server. -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 -"--server/-s" to tell Abra to try to delete this VPS. - -Otherwise, Abra will remove the internal bookkeeping (~/.abra/servers/...) and -underlying client connection context. This server will then be lost in time, -like tears in rain. +Abra will remove the internal bookkeeping (~/.abra/servers/...) and underlying +client connection context. This server will then be lost in time, like tears in +rain. `, Flags: []cli.Flag{ internal.DebugFlag, internal.NoInputFlag, - rmServerFlag, - internal.ServerProviderFlag, internal.OfflineFlag, - - // Hetzner - internal.HetznerCloudNameFlag, - internal.HetznerCloudAPITokenFlag, }, Before: internal.SubCommandBefore, BashComplete: autocomplete.ServerNameComplete, Action: func(c *cli.Context) error { serverName := internal.ValidateServer(c) - warnMsg := `Did not pass -s/--server for actual server deletion, prompting! - -Abra doesn't currently know if it helped you create this server with one of the -3rd party integrations (e.g. Capsul). You have a choice here to actually, -really and finally destroy this server using those integrations. If you want to -do this, choose Yes. - -If you just want to remove the server config files & context, choose No. -` - - if !rmServer { - logrus.Warn(fmt.Sprintf(warnMsg)) - - response := false - prompt := &survey.Confirm{ - Message: "delete actual live server?", - } - if err := survey.AskOne(prompt, &response); err != nil { - logrus.Fatal(err) - } - if response { - logrus.Info("setting -s/--server and attempting to remove actual server") - rmServer = true - } - } - - if rmServer { - if err := internal.EnsureServerProvider(); err != nil { - logrus.Fatal(err) - } - - switch internal.ServerProvider { - case "capsul": - logrus.Warn("capsul provider does not support automatic removal yet, sorry!") - case "hetzner-cloud": - if err := rmHetznerCloudVPS(c); err != nil { - logrus.Fatal(err) - } - } - } - if err := client.DeleteContext(serverName); err != nil { logrus.Fatal(err) } diff --git a/cli/server/server.go b/cli/server/server.go index 034e6fd6..5d1c36d7 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -9,16 +9,7 @@ var ServerCommand = cli.Command{ Name: "server", Aliases: []string{"s"}, Usage: "Manage servers", - Description: ` -Create, manage and remove servers using 3rd party integrations. - -Servers can be created from scratch using the "abra server new" command. If you -already have a server, you can add it to your configuration using "abra server -add". Abra can provision servers so that they are ready to deploy Co-op Cloud -recipes, see available flags on "abra server add" for more. -`, Subcommands: []cli.Command{ - serverNewCommand, serverAddCommand, serverListCommand, serverRemoveCommand, diff --git a/go.mod b/go.mod index ce1b8698..70b4ac6b 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,6 @@ require ( ) require ( - coopcloud.tech/libcapsul v0.0.0-20230605070824-878af473f07b github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect github.com/buger/goterm v1.0.4 github.com/containerd/containerd v1.5.9 // indirect @@ -35,15 +34,14 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/hashicorp/go-retryablehttp v0.7.4 - github.com/hetznercloud/hcloud-go v1.50.0 github.com/klauspost/pgzip v1.2.6 - github.com/libdns/gandi v1.0.2 - github.com/libdns/libdns v0.2.1 github.com/moby/patternmatcher v0.5.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/spf13/cobra v1.3.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect github.com/theupdateframework/notary v0.7.0 // indirect github.com/urfave/cli v1.22.9 github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b // indirect diff --git a/go.sum b/go.sum index f0cc0282..9700b419 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -coopcloud.tech/libcapsul v0.0.0-20230605070824-878af473f07b h1:ORxAmzrd6SSlSGm/RdPM2TSTTe5+QWp7rqzjEY/pIKA= -coopcloud.tech/libcapsul v0.0.0-20230605070824-878af473f07b/go.mod h1:6u7ekg+v+yL07QtU7E+K+WqK9LKDDqTF4s+PrIXZ+QM= coopcloud.tech/tagcmp v0.0.0-20211103052201-885b22f77d52 h1:cyFFOl0tKe+dVHt8saejG8xoff33eQiHxFCVzRpPUjM= coopcloud.tech/tagcmp v0.0.0-20211103052201-885b22f77d52/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= @@ -627,8 +625,6 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hetznercloud/hcloud-go v1.50.0 h1:vS9tJvmSRwgDpMLmPnThGN87Rz8OMP3D4M3rWm8QHEQ= -github.com/hetznercloud/hcloud-go v1.50.0/go.mod h1:VzDWThl47lOnZXY0q5/LPFD+M62pfe/52TV+mOrpp9Q= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -701,11 +697,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/libdns/gandi v1.0.2 h1:1Ts8UpI1x5PVKpOjKC7Dn4+EObndz9gm6vdZnloHSKQ= -github.com/libdns/gandi v1.0.2/go.mod h1:hxpbQKcQFgQrTS5lV4tAgn6QoL6HcCnoBJaW5nOW4Sk= -github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= -github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= -github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= diff --git a/pkg/autocomplete/autocomplete.go b/pkg/autocomplete/autocomplete.go index e1eb560b..c952ba57 100644 --- a/pkg/autocomplete/autocomplete.go +++ b/pkg/autocomplete/autocomplete.go @@ -74,7 +74,6 @@ func SubcommandComplete(c *cli.Context) { "autocomplete", "catalogue", "recipe", - "record", "server", "upgrade", } diff --git a/pkg/dns/common.go b/pkg/dns/dns.go similarity index 59% rename from pkg/dns/common.go rename to pkg/dns/dns.go index 45021541..4c888a3f 100644 --- a/pkg/dns/common.go +++ b/pkg/dns/dns.go @@ -3,32 +3,8 @@ package dns import ( "fmt" "net" - "os" - "time" - - "github.com/AlecAivazis/survey/v2" - "github.com/sirupsen/logrus" ) -// NewToken constructs a new DNS provider token. -func NewToken(provider, providerTokenEnvVar string) (string, error) { - if token, present := os.LookupEnv(providerTokenEnvVar); present { - return token, nil - } - - logrus.Debugf("no %s in environment, asking via stdin", providerTokenEnvVar) - - var token string - prompt := &survey.Input{ - Message: fmt.Sprintf("%s API token?", provider), - } - if err := survey.AskOne(prompt, &token); err != nil { - return "", err - } - - return token, nil -} - // EnsureIPv4 ensures that an ipv4 address is set for a domain name func EnsureIPv4(domainName string) (string, error) { ipv4, err := net.ResolveIPAddr("ip", domainName) @@ -72,12 +48,3 @@ func EnsureDomainsResolveSameIPv4(domainName, server string) (string, error) { return ipv4, nil } - -// GetTTL parses a ttl string into a duration -func GetTTL(ttl string) (time.Duration, error) { - val, err := time.ParseDuration(ttl) - if err != nil { - return val, err - } - return val, nil -} diff --git a/pkg/dns/gandi/gandi.go b/pkg/dns/gandi/gandi.go deleted file mode 100644 index 6049bf92..00000000 --- a/pkg/dns/gandi/gandi.go +++ /dev/null @@ -1,15 +0,0 @@ -package gandi - -import ( - "coopcloud.tech/abra/pkg/dns" - "github.com/libdns/gandi" -) - -// New constructs a new DNs provider. -func New() (gandi.Provider, error) { - token, err := dns.NewToken("Gandi", "GANDI_TOKEN") - if err != nil { - return gandi.Provider{}, err - } - return gandi.Provider{APIToken: token}, nil -}