forked from toolshed/abra
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:
parent
9f9248b987
commit
5ae06bbd42
@ -8,9 +8,9 @@ import (
|
|||||||
|
|
||||||
"coopcloud.tech/abra/cli/app"
|
"coopcloud.tech/abra/cli/app"
|
||||||
"coopcloud.tech/abra/cli/catalogue"
|
"coopcloud.tech/abra/cli/catalogue"
|
||||||
"coopcloud.tech/abra/cli/domain"
|
|
||||||
"coopcloud.tech/abra/cli/internal"
|
"coopcloud.tech/abra/cli/internal"
|
||||||
"coopcloud.tech/abra/cli/recipe"
|
"coopcloud.tech/abra/cli/recipe"
|
||||||
|
"coopcloud.tech/abra/cli/record"
|
||||||
"coopcloud.tech/abra/cli/server"
|
"coopcloud.tech/abra/cli/server"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
logrusStack "github.com/Gurpartap/logrus-stack"
|
logrusStack "github.com/Gurpartap/logrus-stack"
|
||||||
@ -60,7 +60,7 @@ func newAbraApp(version, commit string) *cli.App {
|
|||||||
server.ServerCommand,
|
server.ServerCommand,
|
||||||
recipe.RecipeCommand,
|
recipe.RecipeCommand,
|
||||||
catalogue.CatalogueCommand,
|
catalogue.CatalogueCommand,
|
||||||
domain.DomainCommand,
|
record.RecordCommand,
|
||||||
UpgradeCommand,
|
UpgradeCommand,
|
||||||
},
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
@ -72,11 +72,9 @@ var DNSProviderFlag = &cli.StringFlag{
|
|||||||
Aliases: []string{"p"},
|
Aliases: []string{"p"},
|
||||||
Usage: "DNS provider",
|
Usage: "DNS provider",
|
||||||
Destination: &DNSProvider,
|
Destination: &DNSProvider,
|
||||||
Required: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var NoInput bool
|
var NoInput bool
|
||||||
|
|
||||||
var NoInputFlag = &cli.BoolFlag{
|
var NoInputFlag = &cli.BoolFlag{
|
||||||
Name: "no-input",
|
Name: "no-input",
|
||||||
Value: false,
|
Value: false,
|
||||||
@ -84,3 +82,53 @@ var NoInputFlag = &cli.BoolFlag{
|
|||||||
Usage: "Toggle non-interactive mode",
|
Usage: "Toggle non-interactive mode",
|
||||||
Destination: &NoInput,
|
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
86
cli/internal/domain.go
Normal 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
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package domain
|
package record
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -13,17 +13,18 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DomainListCommand lists domains.
|
// RecordListCommand lists domains.
|
||||||
var DomainListCommand = &cli.Command{
|
var RecordListCommand = &cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "List domain name records for a zone",
|
Usage: "List domain name records",
|
||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
ArgsUsage: "<zone>",
|
ArgsUsage: "<zone>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DNSProviderFlag,
|
internal.DNSProviderFlag,
|
||||||
},
|
},
|
||||||
Description: `
|
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
|
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.
|
are listed. This zone must already be created on your provider account.
|
@ -1,4 +1,4 @@
|
|||||||
package domain
|
package record
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -15,14 +15,19 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DomainCreateCommand lists domains.
|
// RecordCreateCommand lists domains.
|
||||||
var DomainCreateCommand = &cli.Command{
|
var RecordCreateCommand = &cli.Command{
|
||||||
Name: "create",
|
Name: "new",
|
||||||
Usage: "Create a new domain record",
|
Usage: "Create a new domain record",
|
||||||
Aliases: []string{"c"},
|
Aliases: []string{"n"},
|
||||||
ArgsUsage: "<zone> <type> <name> <value> [<ttl>] [<priority>]",
|
ArgsUsage: "<zone>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DNSProviderFlag,
|
internal.DNSProviderFlag,
|
||||||
|
internal.DNSTypeFlag,
|
||||||
|
internal.DNSNameFlag,
|
||||||
|
internal.DNSValueFlag,
|
||||||
|
internal.DNSTTLFlag,
|
||||||
|
internal.DNSPriorityFlag,
|
||||||
},
|
},
|
||||||
Description: `
|
Description: `
|
||||||
This command creates a new domain name record for a specific zone.
|
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:
|
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 {
|
Action: func(c *cli.Context) error {
|
||||||
zone := c.Args().First()
|
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"))
|
internal.ShowSubcommandHelpAndError(c, errors.New("no zone provided"))
|
||||||
}
|
}
|
||||||
|
|
||||||
recordType := c.Args().Get(1)
|
if err := internal.EnsureDNSProvider(); err != nil {
|
||||||
recordName := c.Args().Get(2)
|
logrus.Fatal(err)
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
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)
|
logrus.Fatalf("'%s' is not a supported DNS provider", internal.DNSProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
ttl, err := strconv.Atoi(recordTTL)
|
if err := internal.EnsureDNSTypeFlag(c); err != nil {
|
||||||
if 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)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
record := libdns.Record{
|
record := libdns.Record{
|
||||||
Type: recordType,
|
Type: internal.DNSType,
|
||||||
Name: recordName,
|
Name: internal.DNSName,
|
||||||
Value: recordValue,
|
Value: internal.DNSValue,
|
||||||
TTL: time.Duration(ttl),
|
TTL: time.Duration(internal.DNSTTL),
|
||||||
}
|
}
|
||||||
|
|
||||||
if recordType == "MX" || recordType == "SRV" || recordType == "URI" {
|
if internal.DNSType == "MX" || internal.DNSType == "SRV" || internal.DNSType == "URI" {
|
||||||
priority, err := strconv.Atoi(recordPriority)
|
record.Priority = internal.DNSPriority
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
record.Priority = priority
|
|
||||||
}
|
}
|
||||||
|
|
||||||
records, err := provider.GetRecords(c.Context, zone)
|
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 {
|
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]
|
createdRecord := createdRecords[0]
|
@ -1,15 +1,15 @@
|
|||||||
package domain
|
package record
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DomainCommand supports managing DNS entries.
|
// RecordCommand supports managing DNS entries.
|
||||||
var DomainCommand = &cli.Command{
|
var RecordCommand = &cli.Command{
|
||||||
Name: "domain",
|
Name: "record",
|
||||||
Usage: "Manage domains via 3rd party providers",
|
Usage: "Manage domain name records via 3rd party providers",
|
||||||
Aliases: []string{"d"},
|
Aliases: []string{"rc"},
|
||||||
ArgsUsage: "<domain>",
|
ArgsUsage: "<record>",
|
||||||
Description: `
|
Description: `
|
||||||
This command supports managing domain name records via 3rd party providers such
|
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
|
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{
|
Subcommands: []*cli.Command{
|
||||||
DomainListCommand,
|
RecordListCommand,
|
||||||
DomainCreateCommand,
|
RecordCreateCommand,
|
||||||
DomainRemoveCommand,
|
RecordRemoveCommand,
|
||||||
},
|
},
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package domain
|
package record
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -15,29 +15,32 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DomainRemoveCommand lists domains.
|
// RecordRemoveCommand lists domains.
|
||||||
var DomainRemoveCommand = &cli.Command{
|
var RecordRemoveCommand = &cli.Command{
|
||||||
Name: "remove",
|
Name: "remove",
|
||||||
Usage: "Remove domain name records for a zone",
|
Usage: "Remove a domain name record",
|
||||||
Aliases: []string{"rm"},
|
Aliases: []string{"rm"},
|
||||||
ArgsUsage: "<zone> <type> <name>",
|
ArgsUsage: "<zone>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
internal.DNSProviderFlag,
|
internal.DNSProviderFlag,
|
||||||
|
internal.DNSTypeFlag,
|
||||||
|
internal.DNSNameFlag,
|
||||||
},
|
},
|
||||||
Description: `
|
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
|
It uses the type of record and name to match existing records and choose one
|
||||||
are listed. This zone must already be created on your provider account.
|
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
|
||||||
The "<id>" can be retrieved by running "abra domain list <zone>". This can be
|
provider account.
|
||||||
used then in this command for deletion of the record.
|
|
||||||
|
|
||||||
Example:
|
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 {
|
Action: func(c *cli.Context) error {
|
||||||
zone := c.Args().First()
|
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"))
|
internal.ShowSubcommandHelpAndError(c, errors.New("no zone provided"))
|
||||||
}
|
}
|
||||||
|
|
||||||
recordType := c.Args().Get(1)
|
if err := internal.EnsureDNSProvider(); err != nil {
|
||||||
recordName := c.Args().Get(2)
|
logrus.Fatal(err)
|
||||||
|
|
||||||
if recordType == "" {
|
|
||||||
internal.ShowSubcommandHelpAndError(c, errors.New("no type provided"))
|
|
||||||
}
|
|
||||||
if recordName == "" {
|
|
||||||
internal.ShowSubcommandHelpAndError(c, errors.New("no name provided"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
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)
|
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)
|
records, err := provider.GetRecords(c.Context, zone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
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
|
var toDelete libdns.Record
|
||||||
for _, record := range records {
|
for _, record := range records {
|
||||||
if record.Type == recordType && record.Name == recordName {
|
if record.Type == internal.DNSType && record.Name == internal.DNSName {
|
||||||
toDelete = record
|
toDelete = record
|
||||||
break
|
break
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user