/* rrs returns the ResourceRecordSets interface for a given zone */ func rrs(t *testing.T, zone dnsprovider.Zone) (r dnsprovider.ResourceRecordSets) { rrsets, supported := zone.ResourceRecordSets() if !supported { t.Fatalf("ResourceRecordSets interface not supported by zone %v", zone) return r } return rrsets }
/* CommonTestResourceRecordSetsHonorsType verifies that we can add records of the same name but different types */ func CommonTestResourceRecordSetsDifferentTypes(t *testing.T, zone dnsprovider.Zone) { rrsets, _ := zone.ResourceRecordSets() sets := rrs(t, zone) rrset := rrsets.New("alpha.test.com", []string{"8.8.4.4"}, 40, rrstype.A) addRrsetOrFail(t, sets, rrset) defer sets.StartChangeset().Remove(rrset).Apply() t.Logf("Successfully added resource record set: %v", rrset) aaaaRrset := rrsets.New("alpha.test.com", []string{"2001:4860:4860::8888"}, 80, rrstype.AAAA) // Add the resource with the same name but different type err := sets.StartChangeset().Add(aaaaRrset).Apply() if err != nil { t.Errorf("Failed to add resource record set %v: %v", aaaaRrset, err) } defer sets.StartChangeset().Remove(aaaaRrset).Apply() // Check that both records exist assertHasRecord(t, sets, aaaaRrset) assertHasRecord(t, sets, rrset) }
/* CommonTestResourceRecordSetsReplace verifies that replacing an RRS works */ func CommonTestResourceRecordSetsReplace(t *testing.T, zone dnsprovider.Zone) { rrsets, _ := zone.ResourceRecordSets() sets := rrs(t, zone) rrset := rrsets.New("alpha.test.com", []string{"8.8.4.4"}, 40, rrstype.A) addRrsetOrFail(t, sets, rrset) defer sets.StartChangeset().Remove(rrset).Apply() t.Logf("Successfully added resource record set: %v", rrset) // Replace the record (change ttl and rrdatas) newRrset := rrsets.New("alpha.test.com", []string{"8.8.8.8"}, 80, rrstype.A) err := sets.StartChangeset().Add(newRrset).Remove(rrset).Apply() if err != nil { t.Errorf("Failed to replace resource record set %v -> %v: %v", rrset, newRrset, err) } else { t.Logf("Correctly replaced resource record %v -> %v", rrset, newRrset) } defer sets.StartChangeset().Remove(newRrset).Apply() // Check that the record was updated assertHasRecord(t, sets, newRrset) }
/* CommonTestResourceRecordSetsReplaceAll verifies that we can remove an RRS and create one with a different name*/ func CommonTestResourceRecordSetsReplaceAll(t *testing.T, zone dnsprovider.Zone) { rrsets, _ := zone.ResourceRecordSets() sets := rrs(t, zone) rrset := rrsets.New("alpha.test.com", []string{"8.8.4.4"}, 40, rrstype.A) addRrsetOrFail(t, sets, rrset) defer sets.StartChangeset().Remove(rrset).Apply() t.Logf("Successfully added resource record set: %v", rrset) newRrset := rrsets.New("beta.test.com", []string{"8.8.8.8"}, 80, rrstype.A) // Try to add it again, and verify that the call fails. err := sets.StartChangeset().Add(newRrset).Remove(rrset).Apply() if err != nil { t.Errorf("Failed to replace resource record set %v -> %v: %v", rrset, newRrset, err) } else { defer sets.StartChangeset().Remove(newRrset).Apply() t.Logf("Correctly replaced resource record %v -> %v", rrset, newRrset) } // Check that it was updated assertHasRecord(t, sets, newRrset) assertNotHasRecord(t, sets, rrset.Name(), rrset.Type()) }
func getInvalidRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet { rrsets, _ := zone.ResourceRecordSets() return rrsets.New("www12."+zone.Name(), []string{"rubbish", "rubbish"}, 180, rrstype.A) }
func getExampleRrs(zone dnsprovider.Zone) dnsprovider.ResourceRecordSet { rrsets, _ := zone.ResourceRecordSets() return rrsets.New("www11."+zone.Name(), []string{"10.10.10.10", "169.20.20.20"}, 180, rrstype.A) }
/* ensureDnsRrsets ensures (idempotently, and with minimum mutations) that all of the DNS resource record sets for dnsName are consistent with endpoints. if endpoints is nil or empty, a CNAME record to uplevelCname is ensured. */ func (s *ServiceController) ensureDnsRrsets(dnsZone dnsprovider.Zone, dnsName string, endpoints []string, uplevelCname string) error { rrsets, supported := dnsZone.ResourceRecordSets() if !supported { return fmt.Errorf("Failed to ensure DNS records for %s. DNS provider does not support the ResourceRecordSets interface.", dnsName) } rrset, err := getRrset(dnsName, rrsets) // TODO: rrsets.Get(dnsName) if err != nil { return err } if rrset == nil { glog.V(4).Infof("No recordsets found for DNS name %q. Need to add either A records (if we have healthy endpoints), or a CNAME record to %q", dnsName, uplevelCname) if len(endpoints) < 1 { glog.V(4).Infof("There are no healthy endpoint addresses at level %q, so CNAME to %q, if provided", dnsName, uplevelCname) if uplevelCname != "" { glog.V(4).Infof("Creating CNAME to %q for %q", uplevelCname, dnsName) newRrset := rrsets.New(dnsName, []string{uplevelCname}, minDnsTtl, rrstype.CNAME) glog.V(4).Infof("Adding recordset %v", newRrset) err = rrsets.StartChangeset().Add(newRrset).Apply() if err != nil { return err } glog.V(4).Infof("Successfully created CNAME to %q for %q", uplevelCname, dnsName) } else { glog.V(4).Infof("We want no record for %q, and we have no record, so we're all good.", dnsName) } } else { // We have valid endpoint addresses, so just add them as A records. // But first resolve DNS names, as some cloud providers (like AWS) expose // load balancers behind DNS names, not IP addresses. glog.V(4).Infof("We have valid endpoint addresses %v at level %q, so add them as A records, after resolving DNS names", endpoints, dnsName) resolvedEndpoints, err := getResolvedEndpoints(endpoints) if err != nil { return err // TODO: We could potentially add the ones we did get back, even if some of them failed to resolve. } newRrset := rrsets.New(dnsName, resolvedEndpoints, minDnsTtl, rrstype.A) glog.V(4).Infof("Adding recordset %v", newRrset) err = rrsets.StartChangeset().Add(newRrset).Apply() if err != nil { return err } glog.V(4).Infof("Successfully added recordset %v", newRrset) } } else { // the rrset already exists, so make it right. glog.V(4).Infof("Recordset %v already exists. Ensuring that it is correct.", rrset) if len(endpoints) < 1 { // Need an appropriate CNAME record. Check that we have it. newRrset := rrsets.New(dnsName, []string{uplevelCname}, minDnsTtl, rrstype.CNAME) glog.V(4).Infof("No healthy endpoints for %s. Have recordset %v. Need recordset %v", dnsName, rrset, newRrset) if dnsprovider.ResourceRecordSetsEquivalent(rrset, newRrset) { // The existing rrset is equivalent to the required one - our work is done here glog.V(4).Infof("Existing recordset %v is equivalent to needed recordset %v, our work is done here.", rrset, newRrset) return nil } else { // Need to replace the existing one with a better one (or just remove it if we have no healthy endpoints). glog.V(4).Infof("Existing recordset %v not equivalent to needed recordset %v removing existing and adding needed.", rrset, newRrset) changeSet := rrsets.StartChangeset() changeSet.Remove(rrset) if uplevelCname != "" { changeSet.Add(newRrset) if err := changeSet.Apply(); err != nil { return err } glog.V(4).Infof("Successfully replaced needed recordset %v -> %v", rrset, newRrset) } else { if err := changeSet.Apply(); err != nil { return err } glog.V(4).Infof("Successfully removed existing recordset %v", rrset) glog.V(4).Infof("Uplevel CNAME is empty string. Not adding recordset %v", newRrset) } } } else { // We have an rrset in DNS, possibly with some missing addresses and some unwanted addresses. // And we have healthy endpoints. Just replace what's there with the healthy endpoints, if it's not already correct. glog.V(4).Infof("%s: Healthy endpoints %v exist. Recordset %v exists. Reconciling.", dnsName, endpoints, rrset) resolvedEndpoints, err := getResolvedEndpoints(endpoints) if err != nil { // Some invalid addresses or otherwise unresolvable DNS names. return err // TODO: We could potentially add the ones we did get back, even if some of them failed to resolve. } newRrset := rrsets.New(dnsName, resolvedEndpoints, minDnsTtl, rrstype.A) glog.V(4).Infof("Have recordset %v. Need recordset %v", rrset, newRrset) if dnsprovider.ResourceRecordSetsEquivalent(rrset, newRrset) { glog.V(4).Infof("Existing recordset %v is equivalent to needed recordset %v, our work is done here.", rrset, newRrset) // TODO: We could be more thorough about checking for equivalence to avoid unnecessary updates, but in the // worst case we'll just replace what's there with an equivalent, if not exactly identical record set. return nil } else { // Need to replace the existing one with a better one glog.V(4).Infof("Existing recordset %v is not equivalent to needed recordset %v, removing existing and adding needed.", rrset, newRrset) if err = rrsets.StartChangeset().Remove(rrset).Add(newRrset).Apply(); err != nil { return err } glog.V(4).Infof("Successfully replaced recordset %v -> %v", rrset, newRrset) } } } return nil }