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", "cs", "dash", "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) } } }