Support deleting records

This commit is contained in:
Balakrishnan Balasubramanian 2023-03-23 18:57:30 -04:00
parent 48514a61be
commit 7534108b79
2 changed files with 167 additions and 42 deletions

160
main.go
View File

@ -8,6 +8,8 @@ import (
"log" "log"
"net" "net"
"os" "os"
"sort"
"strings"
"github.com/libdns/cloudflare" "github.com/libdns/cloudflare"
"github.com/libdns/libdns" "github.com/libdns/libdns"
@ -16,27 +18,14 @@ import (
var ( var (
domain = os.Getenv("DOMAIN") domain = os.Getenv("DOMAIN")
token = os.Getenv("CF_TOKEN") token = os.Getenv("CF_TOKEN")
ip = net.IPv4(127, 0, 0, 1)
cname string
sub = "<UNSET>"
path string
del = false
) )
func main() { func genRecord() (libdns.Record, error) {
ctx := context.TODO()
ip := net.IPv4(127, 0, 0, 1)
var cname string
sub := "<UNSET>"
var path string
flag.StringVar(&domain, "d", domain, "Domain name, e.g. example.com")
flag.StringVar(&sub, "s", sub, "Subdomain to add dns entry for, e.g. blog")
flag.StringVar(&token, "a", token, "Cloudflare API Token")
flag.TextVar(&ip, "i", ip, "IP address")
flag.StringVar(&cname, "c", cname, "CNAME target")
flag.StringVar(&path, "o", path, "path to save all records as json, e.g. ./records.json")
flag.Parse()
provider := cloudflare.Provider{APIToken: token}
zone := domain + "."
fmt.Println(zone, sub, cname)
makeRecord := func() (libdns.Record, error) {
switch { switch {
case cname != "": case cname != "":
return libdns.Record{ return libdns.Record{
@ -58,34 +47,155 @@ func main() {
}, nil }, nil
} }
return libdns.Record{}, fmt.Errorf("neither cname nor valid ip is set") return libdns.Record{}, fmt.Errorf("neither cname nor valid ip is set")
}
func selectRecordsToDelete(recs []libdns.Record) []libdns.Record {
name := func() string {
if sub == "@" {
return domain
}
return fmt.Sprintf("%s.%s", sub, domain)
}()
recs = func() (result []libdns.Record) {
for _, r := range recs {
if r.Name == name {
result = append(result, r)
}
}
return
}()
fmt.Printf("Records matching %s\n", name)
for i, r := range recs {
fmt.Printf("%5d %6s %s\n", i, r.Type, r.Value)
}
var delRange string
fmt.Print("Enter record indexes seperated by comma(,) to delete. Use hyphen(-) for a closed range. Also can be all or none\nIndexes: ")
if _, err := fmt.Scanln(&delRange); err != nil {
log.Panicln(err)
}
switch {
case delRange == "all":
return recs
case delRange == "none":
return nil
default:
indexGen := func() func() int {
indexes := parseRange(delRange, len(recs)-1)
indexIndex := 0
return func() int {
if indexIndex == len(indexes) {
return -1
}
val := indexes[indexIndex]
indexIndex++
return val
}
}()
return func() (result []libdns.Record) {
nextIndex := indexGen()
for i, r := range recs {
if i == nextIndex {
result = append(result, r)
nextIndex = indexGen()
}
}
return
}()
} }
}
func main() {
ctx := context.TODO()
flag.StringVar(&domain, "d", domain, "Domain name, e.g. example.com")
flag.StringVar(&sub, "s", sub, "Subdomain, e.g. blog. Use @ for root")
flag.StringVar(&token, "a", token, "Cloudflare API Token")
flag.TextVar(&ip, "i", ip, "IP address")
flag.StringVar(&cname, "c", cname, "CNAME target")
flag.StringVar(&path, "o", path, "Path to save all records as json, e.g. ./records.json")
flag.BoolVar(&del, "x", del, "Delete records of subdomain")
flag.Parse()
provider := cloudflare.Provider{APIToken: token}
zone := domain + "."
if sub != "<UNSET>" { if sub != "<UNSET>" {
record, err := makeRecord() if del {
recs, err := provider.GetRecords(ctx, zone)
if err != nil { if err != nil {
panic(err) log.Panicln(err)
}
recs = selectRecordsToDelete(recs)
recs, err = provider.DeleteRecords(ctx, zone, recs)
if err != nil {
log.Panicln(err)
}
if len(recs) == 0 {
fmt.Println("Nothing was deleted")
} else {
fmt.Println("Records deleted:")
for i, r := range recs {
fmt.Printf("%5d %6s %s\n", i, r.Type, r.Value)
}
}
} else {
record, err := genRecord()
if err != nil {
log.Panicln(err)
} }
log.Printf("setting record, %+v", record) log.Printf("setting record, %+v", record)
newRecs, err := provider.SetRecords(ctx, zone, []libdns.Record{record}) newRecs, err := provider.SetRecords(ctx, zone, []libdns.Record{record})
if err != nil { if err != nil {
panic(err) log.Panicln(err)
} }
fmt.Println(newRecs) fmt.Println(newRecs)
} }
}
if path != "" { if path != "" {
recs, err := provider.GetRecords(ctx, zone) recs, err := provider.GetRecords(ctx, zone)
if err != nil { if err != nil {
panic(err) log.Panicln(err)
} }
data, err := json.Marshal(recs) data, err := json.Marshal(recs)
if err != nil { if err != nil {
panic(err) log.Panicln(err)
} }
err = os.WriteFile(path, data, 0644) err = os.WriteFile(path, data, 0644)
if err != nil { if err != nil {
panic(err) log.Panicln(err)
} }
} }
} }
func parseRange(rs string, max int) []int {
var indexes []int
m := uint(max)
for _, s := range strings.Split(rs, ",") {
s = strings.TrimSpace(s)
var i, a, b uint
if _, err := fmt.Sscanf(s, "%v-%v", &a, &b); err == nil {
for s := a; s <= b && s <= m; {
indexes = append(indexes, int(s))
s++
}
} else {
if _, err := fmt.Sscanf(s, "%v", &i); err == nil && i <= m {
indexes = append(indexes, int(i))
}
}
}
sort.Ints(indexes)
var uniq []int
last := -1
for _, i := range indexes {
if i != last {
uniq = append(uniq, i)
last = i
}
}
return uniq
}

15
main_test.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
"fmt"
"testing"
)
func TestParseRange(t *testing.T) {
expected := []int{0, 1, 2, 6}
actual := parseRange("0-2,-5,6,-7-7,1, 11", 10)
if fmt.Sprint(expected) != fmt.Sprint(actual) {
fmt.Printf("unexpected %#v\n", actual)
t.Fail()
}
}