refactor!: abra domain -> abra record + prompts

This reconciles the fact that we manage records and not domains which
was a bad first naming take on this imho. Now it is clear that we are
manipulating domain name records and not entire zones.

The UX of record creation/deletion now mirrors the UX of new apps. All
the things are prompted for.
This commit is contained in:
decentral1se 2021-10-22 08:58:18 +02:00
parent 9f9248b987
commit 5ae06bbd42
No known key found for this signature in database
GPG Key ID: 5E2EF5A63E3718CC
7 changed files with 214 additions and 86 deletions

View File

@ -8,9 +8,9 @@ import (
"coopcloud.tech/abra/cli/app"
"coopcloud.tech/abra/cli/catalogue"
"coopcloud.tech/abra/cli/domain"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/cli/recipe"
"coopcloud.tech/abra/cli/record"
"coopcloud.tech/abra/cli/server"
"coopcloud.tech/abra/pkg/config"
logrusStack "github.com/Gurpartap/logrus-stack"
@ -60,7 +60,7 @@ func newAbraApp(version, commit string) *cli.App {
server.ServerCommand,
recipe.RecipeCommand,
catalogue.CatalogueCommand,
domain.DomainCommand,
record.RecordCommand,
UpgradeCommand,
},
Flags: []cli.Flag{

View File

@ -72,11 +72,9 @@ var DNSProviderFlag = &cli.StringFlag{
Aliases: []string{"p"},
Usage: "DNS provider",
Destination: &DNSProvider,
Required: true,
}
var NoInput bool
var NoInputFlag = &cli.BoolFlag{
Name: "no-input",
Value: false,
@ -84,3 +82,53 @@ var NoInputFlag = &cli.BoolFlag{
Usage: "Toggle non-interactive mode",
Destination: &NoInput,
}
var DNSType string
var DNSTypeFlag = &cli.StringFlag{
Name: "type",
Value: "",
Aliases: []string{"t"},
Usage: "Domain name record type (e.g. A)",
Destination: &DNSType,
}
var DNSName string
var DNSNameFlag = &cli.StringFlag{
Name: "name",
Value: "",
Aliases: []string{"n"},
Usage: "Domain name record name (e.g. mysubdomain)",
Destination: &DNSName,
}
var DNSValue string
var DNSValueFlag = &cli.StringFlag{
Name: "value",
Value: "",
Aliases: []string{"v"},
Usage: "Domain name record value (e.g. 192.168.1.1)",
Destination: &DNSValue,
}
var DNSTTL int
var DNSTTLFlag = &cli.IntFlag{
Name: "ttl",
Value: 86400,
Aliases: []string{"T"},
Usage: "Domain name TTL value (e.g. 86400)",
Destination: &DNSTTL,
}
var DNSPriority int
var DNSPriorityFlag = &cli.IntFlag{
Name: "priority",
Value: 10,
Aliases: []string{"P"},
Usage: "Domain name priority value (e.g. 100)",
Destination: &DNSPriority,
}

86
cli/internal/domain.go Normal file
View File

@ -0,0 +1,86 @@
package internal
import (
"errors"
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
)
// 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 DNSName == "" {
ShowSubcommandHelpAndError(c, errors.New("no record value provided"))
}
return nil
}

View File

@ -1,4 +1,4 @@
package domain
package record
import (
"errors"
@ -13,17 +13,18 @@ import (
"github.com/urfave/cli/v2"
)
// DomainListCommand lists domains.
var DomainListCommand = &cli.Command{
// RecordListCommand lists domains.
var RecordListCommand = &cli.Command{
Name: "list",
Usage: "List domain name records for a zone",
Usage: "List domain name records",
Aliases: []string{"ls"},
ArgsUsage: "<zone>",
Flags: []cli.Flag{
internal.DNSProviderFlag,
},
Description: `
This command lists all domain name records managed by a 3rd party provider.
This command lists 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.

View File

@ -1,4 +1,4 @@
package domain
package record
import (
"errors"
@ -15,14 +15,19 @@ import (
"github.com/urfave/cli/v2"
)
// DomainCreateCommand lists domains.
var DomainCreateCommand = &cli.Command{
Name: "create",
// RecordCreateCommand lists domains.
var RecordCreateCommand = &cli.Command{
Name: "new",
Usage: "Create a new domain record",
Aliases: []string{"c"},
ArgsUsage: "<zone> <type> <name> <value> [<ttl>] [<priority>]",
Aliases: []string{"n"},
ArgsUsage: "<zone>",
Flags: []cli.Flag{
internal.DNSProviderFlag,
internal.DNSTypeFlag,
internal.DNSNameFlag,
internal.DNSValueFlag,
internal.DNSTTLFlag,
internal.DNSPriorityFlag,
},
Description: `
This command creates a new domain name record for a specific zone.
@ -32,9 +37,11 @@ are listed. This zone must already be created on your provider account.
Example:
abra domain create -p gandi foo.com A myapp 192.168.178.44
abra record new foo.com -p gandi -t A -n myapp -v 192.168.178.44
Which means you can then deploy an app against "myapp.foo.com" successfully.
You may also invoke this command in "wizard" mode and be prompted for input
abra record new
`,
Action: func(c *cli.Context) error {
zone := c.Args().First()
@ -42,30 +49,8 @@ Which means you can then deploy an app against "myapp.foo.com" successfully.
internal.ShowSubcommandHelpAndError(c, errors.New("no zone provided"))
}
recordType := c.Args().Get(1)
recordName := c.Args().Get(2)
recordValue := c.Args().Get(3)
recordTTL := c.Args().Get(4)
recordPriority := c.Args().Get(5)
if recordType == "" {
internal.ShowSubcommandHelpAndError(c, errors.New("no type provided"))
}
if recordName == "" {
internal.ShowSubcommandHelpAndError(c, errors.New("no name provided"))
}
if recordValue == "" {
internal.ShowSubcommandHelpAndError(c, errors.New("no value provided"))
}
if recordTTL == "" {
recordTTL = "86400"
}
if recordType == "MX" || recordType == "SRV" || recordType == "URI" {
if recordPriority == "" {
logrus.Fatal("record priority must be set when using MX/SRV/URI records")
}
if err := internal.EnsureDNSProvider(); err != nil {
logrus.Fatal(err)
}
var err error
@ -80,24 +65,27 @@ Which means you can then deploy an app against "myapp.foo.com" successfully.
logrus.Fatalf("'%s' is not a supported DNS provider", internal.DNSProvider)
}
ttl, err := strconv.Atoi(recordTTL)
if err != nil {
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)
}
record := libdns.Record{
Type: recordType,
Name: recordName,
Value: recordValue,
TTL: time.Duration(ttl),
Type: internal.DNSType,
Name: internal.DNSName,
Value: internal.DNSValue,
TTL: time.Duration(internal.DNSTTL),
}
if recordType == "MX" || recordType == "SRV" || recordType == "URI" {
priority, err := strconv.Atoi(recordPriority)
if err != nil {
logrus.Fatal(err)
}
record.Priority = priority
if internal.DNSType == "MX" || internal.DNSType == "SRV" || internal.DNSType == "URI" {
record.Priority = internal.DNSPriority
}
records, err := provider.GetRecords(c.Context, zone)
@ -120,7 +108,7 @@ Which means you can then deploy an app against "myapp.foo.com" successfully.
)
if len(createdRecords) == 0 {
logrus.Fatal("provider library reports no record created?")
logrus.Fatal("provider library reports that no record was created?")
}
createdRecord := createdRecords[0]

View File

@ -1,15 +1,15 @@
package domain
package record
import (
"github.com/urfave/cli/v2"
)
// DomainCommand supports managing DNS entries.
var DomainCommand = &cli.Command{
Name: "domain",
Usage: "Manage domains via 3rd party providers",
Aliases: []string{"d"},
ArgsUsage: "<domain>",
// RecordCommand supports managing DNS entries.
var RecordCommand = &cli.Command{
Name: "record",
Usage: "Manage domain name records via 3rd party providers",
Aliases: []string{"rc"},
ArgsUsage: "<record>",
Description: `
This command supports managing domain name records via 3rd party providers such
as Gandi DNS. It supports listing, creating and removing all types of records
@ -31,8 +31,8 @@ allows to implement new provider support easily.
`,
Subcommands: []*cli.Command{
DomainListCommand,
DomainCreateCommand,
DomainRemoveCommand,
RecordListCommand,
RecordCreateCommand,
RecordRemoveCommand,
},
}

View File

@ -1,4 +1,4 @@
package domain
package record
import (
"errors"
@ -15,29 +15,32 @@ import (
"github.com/urfave/cli/v2"
)
// DomainRemoveCommand lists domains.
var DomainRemoveCommand = &cli.Command{
// RecordRemoveCommand lists domains.
var RecordRemoveCommand = &cli.Command{
Name: "remove",
Usage: "Remove domain name records for a zone",
Usage: "Remove a domain name record",
Aliases: []string{"rm"},
ArgsUsage: "<zone> <type> <name>",
ArgsUsage: "<zone>",
Flags: []cli.Flag{
internal.DNSProviderFlag,
internal.DNSTypeFlag,
internal.DNSNameFlag,
},
Description: `
This command removes a new domain name record for a specific zone.
This command removes a 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.
The "<id>" can be retrieved by running "abra domain list <zone>". This can be
used then in this command for deletion of the record.
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 domain remove -p gandi foo.com A myapp
abra record remove foo.com -p gandi -t A -n myapp
Which means that the domain name record of type A for myapp.foo.com will be deleted.
You may also invoke this command in "wizard" mode and be prompted for input
abra record rm
`,
Action: func(c *cli.Context) error {
zone := c.Args().First()
@ -45,14 +48,8 @@ Which means that the domain name record of type A for myapp.foo.com will be dele
internal.ShowSubcommandHelpAndError(c, errors.New("no zone provided"))
}
recordType := c.Args().Get(1)
recordName := c.Args().Get(2)
if recordType == "" {
internal.ShowSubcommandHelpAndError(c, errors.New("no type provided"))
}
if recordName == "" {
internal.ShowSubcommandHelpAndError(c, errors.New("no name provided"))
if err := internal.EnsureDNSProvider(); err != nil {
logrus.Fatal(err)
}
var err error
@ -67,6 +64,14 @@ Which means that the domain name record of type A for myapp.foo.com will be dele
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(c.Context, zone)
if err != nil {
logrus.Fatal(err)
@ -74,7 +79,7 @@ Which means that the domain name record of type A for myapp.foo.com will be dele
var toDelete libdns.Record
for _, record := range records {
if record.Type == recordType && record.Name == recordName {
if record.Type == internal.DNSType && record.Name == internal.DNSName {
toDelete = record
break
}