diff options
Diffstat (limited to '')
-rw-r--r-- | cmd/dnsupdate/main.go | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/cmd/dnsupdate/main.go b/cmd/dnsupdate/main.go new file mode 100644 index 0000000..59ed67a --- /dev/null +++ b/cmd/dnsupdate/main.go @@ -0,0 +1,125 @@ +package main + +import ( + "context" + "fmt" + "log" + + dns "google.golang.org/api/dns/v1" +) + +const ( + GCP_PROJECT_NAME = "fcuny-homelab" + GCP_MANAGED_ZONE = "fcuny-xyz" + TS_DEVICE_NAME = "tahoe" + TTL = 300 +) + +var desiredRecords = []string{ + "bt", + "dash", + "drone", + "music", + "unifi", +} + +func main() { + ctx := context.Background() + + // we only care about IPv4 for now + tsIpV4Addresses, _, err := getTsIpsDevice(ctx, TS_DEVICE_NAME) + if err != nil { + log.Fatalf("failed to get the IP addresses for %s: %v", TS_DEVICE_NAME, err) + } + + svc, err := dns.NewService(ctx) + if err != nil { + log.Fatalf("failed to create the client for Google Cloud DNS: %v", err) + } + + zone, err := svc.ManagedZones.Get(GCP_PROJECT_NAME, GCP_MANAGED_ZONE).Context(ctx).Do() + if err != nil { + log.Fatalf("failed to get information about the managed zone %s: %+v", GCP_MANAGED_ZONE, err) + } + + recordSets, err := svc.ResourceRecordSets.List(GCP_PROJECT_NAME, GCP_MANAGED_ZONE).Context(ctx).Do() + if err != nil { + log.Fatalf("failed to get the list of records: %+v", err) + } + + var ( + existingRecordSets = []*dns.ResourceRecordSet{} + recordSetsToAdd = []*dns.ResourceRecordSet{} + recordSetsToDelete = []*dns.ResourceRecordSet{} + ) + + for _, record := range recordSets.Rrsets { + if record.Type == "A" { + existingRecordSets = append(existingRecordSets, record) + } + } + + // first pass: create what's missing + for _, subdomain := range desiredRecords { + found := false + subdomain = fmt.Sprintf("%s.%s", subdomain, zone.DnsName) + for _, r := range existingRecordSets { + if subdomain == r.Name && r.Type == "A" { + // check that the IP addresses are correct + ipsFound := 0 + for _, rr := range r.Rrdatas { + for _, ip := range tsIpV4Addresses { + if rr == ip { + ipsFound += 1 + continue + } + } + } + // while we found the subdomain with the correct type, + // we also need to make sure the list of IPs is + // correct. If they are not, we delete the record and + // add it again with the correct values. + if ipsFound == len(tsIpV4Addresses) { + found = true + continue + } else { + log.Printf("will delete %s (incorrect IPv4 addresses)\n", subdomain) + recordSetsToDelete = append(recordSetsToDelete, r) + } + } + } + if !found { + log.Printf("will add %s\n", subdomain) + r := &dns.ResourceRecordSet{ + Name: subdomain, + Type: "A", + Ttl: TTL, + Rrdatas: tsIpV4Addresses, + } + recordSetsToAdd = append(recordSetsToAdd, r) + } + } + + // second pass: delete what's not needed + for _, r := range existingRecordSets { + found := false + for _, subdomain := range desiredRecords { + subdomain = fmt.Sprintf("%s.%s", subdomain, zone.DnsName) + if subdomain == r.Name && r.Type == "A" { + found = true + continue + } + } + if !found { + log.Printf("will delete %s\n", r.Name) + recordSetsToDelete = append(recordSetsToDelete, r) + } + } + + if len(recordSetsToAdd) > 0 || len(recordSetsToDelete) > 0 { + change := &dns.Change{Additions: recordSetsToAdd, Deletions: recordSetsToDelete} + if _, err = svc.Changes.Create(GCP_PROJECT_NAME, GCP_MANAGED_ZONE, change).Context(ctx).Do(); err != nil { + log.Fatalf("failed to apply the change: %+v", err) + } + } +} |