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.AutoDNSRecordFlag, }, Before: internal.SubCommandBefore, Description: ` This command creates 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 Typically, you need two records, an A record which points at the zone (@.) and a wildcard record for your apps (*.). Pass "--auto" to have Abra automatically set this up. abra record new --auto foo.com -p gandi -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 internal.AutoDNSRecord { ipv4, err := dns.EnsureIPv4(zone) if err != nil { logrus.Debugf("no ipv4 associated with %s, prompting for input", zone) if err := internal.EnsureDNSValueFlag(c); err != nil { logrus.Fatal(err) } ipv4 = internal.DNSValue } logrus.Infof("automatically configuring @./*. A records for %s for %s (--auto)", zone, ipv4) if err := autoConfigure(c, &provider, zone, ipv4); err != nil { logrus.Fatal(err) } return 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) } 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 }, } func autoConfigure(c *cli.Context, provider *gandi.Provider, zone, ipv4 string) error { ttl, err := dns.GetTTL(internal.DNSTTL) if err != nil { return err } atRecord := libdns.Record{ Type: "A", Name: "@", Value: ipv4, TTL: ttl, } wildcardRecord := libdns.Record{ Type: "A", Name: "*", Value: ipv4, TTL: ttl, } records := []libdns.Record{atRecord, wildcardRecord} tableCol := []string{"type", "name", "value", "TTL", "priority"} table := formatter.CreateTable(tableCol) for _, record := range records { existingRecords, err := provider.GetRecords(context.Background(), zone) if err != nil { return err } discovered := false for _, existingRecord := range existingRecords { if existingRecord.Type == record.Type && existingRecord.Name == record.Name && existingRecord.Value == record.Value { logrus.Warnf("%s record: %s %s for %s already exists?", record.Type, record.Name, record.Value, zone) discovered = true } } if discovered { continue } createdRecords, err := provider.SetRecords( context.Background(), zone, []libdns.Record{record}, ) if err != nil { return err } if len(createdRecords) == 0 { return fmt.Errorf("provider library reports that no record was created?") } createdRecord := createdRecords[0] 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), }) } if table.NumLines() > 0 { table.Render() } return nil }