diff --git a/plan/plan.go b/plan/plan.go index 7b805bc76..cdecda4f5 100644 --- a/plan/plan.go +++ b/plan/plan.go @@ -187,11 +187,34 @@ func shouldUpdateProviderSpecific(desired, current *endpoint.Endpoint) bool { continue } + found := false for _, d := range desired.ProviderSpecific { - if d.Name == c.Name && d.Value != c.Value { - return true + if d.Name == c.Name { + if d.Value != c.Value { + // provider-specific attribute updated + return true + } + found = true + break } } + if !found { + // provider-specific attribute deleted + return true + } + } + for _, d := range desired.ProviderSpecific { + found := false + for _, c := range current.ProviderSpecific { + if d.Name == c.Name { + found = true + break + } + } + if !found { + // provider-specific attribute added + return true + } } return false diff --git a/provider/aws.go b/provider/aws.go index 2ca25d648..febcc82f4 100644 --- a/provider/aws.go +++ b/provider/aws.go @@ -39,11 +39,9 @@ const ( // provider specific key that designates whether an AWS ALIAS record has the EvaluateTargetHealth // field set to true. providerSpecificEvaluateTargetHealth = "aws/evaluate-target-health" - providerSpecificSetIdentifier = "aws/set-identifier" providerSpecificWeight = "aws/weight" providerSpecificRegion = "aws/region" providerSpecificFailover = "aws/failover" - providerSpecificGeolocation = "aws/geolocation" providerSpecificGeolocationContinentCode = "aws/geolocation-continent-code" providerSpecificGeolocationCountryCode = "aws/geolocation-country-code" providerSpecificGeolocationSubdivisionCode = "aws/geolocation-subdivision-code" diff --git a/registry/txt.go b/registry/txt.go index 84a15040a..da574532a 100644 --- a/registry/txt.go +++ b/registry/txt.go @@ -135,6 +135,7 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) } r.Labels[endpoint.OwnerLabelKey] = im.ownerID txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true)).WithSetIdentifier(r.SetIdentifier) + txt.ProviderSpecific = r.ProviderSpecific filteredChanges.Create = append(filteredChanges.Create, txt) if im.cacheInterval > 0 { @@ -144,6 +145,7 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) for _, r := range filteredChanges.Delete { txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true)).WithSetIdentifier(r.SetIdentifier) + txt.ProviderSpecific = r.ProviderSpecific // when we delete TXT records for which value has changed (due to new label) this would still work because // !!! TXT record value is uniquely generated from the Labels of the endpoint. Hence old TXT record can be uniquely reconstructed @@ -157,6 +159,7 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) // make sure TXT records are consistently updated as well for _, r := range filteredChanges.UpdateOld { txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true)).WithSetIdentifier(r.SetIdentifier) + txt.ProviderSpecific = r.ProviderSpecific // when we updateOld TXT records for which value has changed (due to new label) this would still work because // !!! TXT record value is uniquely generated from the Labels of the endpoint. Hence old TXT record can be uniquely reconstructed filteredChanges.UpdateOld = append(filteredChanges.UpdateOld, txt) @@ -169,6 +172,7 @@ func (im *TXTRegistry) ApplyChanges(ctx context.Context, changes *plan.Changes) // make sure TXT records are consistently updated as well for _, r := range filteredChanges.UpdateNew { txt := endpoint.NewEndpoint(im.mapper.toTXTName(r.DNSName), endpoint.RecordTypeTXT, r.Labels.Serialize(true)).WithSetIdentifier(r.SetIdentifier) + txt.ProviderSpecific = r.ProviderSpecific filteredChanges.UpdateNew = append(filteredChanges.UpdateNew, txt) // add new version of record to cache if im.cacheInterval > 0 { diff --git a/source/dedup_source.go b/source/dedup_source.go index de26388fb..01d0df893 100644 --- a/source/dedup_source.go +++ b/source/dedup_source.go @@ -43,7 +43,7 @@ func (ms *dedupSource) Endpoints() ([]*endpoint.Endpoint, error) { } for _, ep := range endpoints { - identifier := ep.DNSName + " / " + ep.Targets.String() + identifier := ep.DNSName + " / " + ep.SetIdentifier + " / " + ep.Targets.String() if _, ok := collected[identifier]; ok { log.Debugf("Removing duplicate endpoint %s", ep) diff --git a/source/gateway.go b/source/gateway.go index 85d58d582..8349a7121 100644 --- a/source/gateway.go +++ b/source/gateway.go @@ -174,14 +174,14 @@ func (sc *gatewaySource) endpointsFromTemplate(config *istiomodel.Config) ([]*en } } - providerSpecific := getProviderSpecificAnnotations(config.Annotations) + providerSpecific, setIdentifier := getProviderSpecificAnnotations(config.Annotations) var endpoints []*endpoint.Endpoint // splits the FQDN template and removes the trailing periods hostnameList := strings.Split(strings.Replace(hostnames, " ", "", -1), ",") for _, hostname := range hostnameList { hostname = strings.TrimSuffix(hostname, ".") - endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...) + endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...) } return endpoints, nil } @@ -266,7 +266,7 @@ func (sc *gatewaySource) endpointsFromGatewayConfig(config istiomodel.Config) ([ gateway := config.Spec.(*istionetworking.Gateway) - providerSpecific := getProviderSpecificAnnotations(config.Annotations) + providerSpecific, setIdentifier := getProviderSpecificAnnotations(config.Annotations) for _, server := range gateway.Servers { for _, host := range server.Hosts { @@ -282,7 +282,7 @@ func (sc *gatewaySource) endpointsFromGatewayConfig(config istiomodel.Config) ([ host = parts[1] } - endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific)...) + endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier)...) } } @@ -290,7 +290,7 @@ func (sc *gatewaySource) endpointsFromGatewayConfig(config istiomodel.Config) ([ if !sc.ignoreHostnameAnnotation { hostnameList := getHostnamesFromAnnotations(config.Annotations) for _, hostname := range hostnameList { - endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...) + endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...) } } diff --git a/source/ingress.go b/source/ingress.go index 3eed66549..03a45c19f 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -187,14 +187,14 @@ func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoin targets = targetsFromIngressStatus(ing.Status) } - providerSpecific := getProviderSpecificAnnotations(ing.Annotations) + providerSpecific, setIdentifier := getProviderSpecificAnnotations(ing.Annotations) var endpoints []*endpoint.Endpoint // splits the FQDN template and removes the trailing periods hostnameList := strings.Split(strings.Replace(hostnames, " ", "", -1), ",") for _, hostname := range hostnameList { hostname = strings.TrimSuffix(hostname, ".") - endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...) + endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...) } return endpoints, nil } @@ -261,13 +261,13 @@ func endpointsFromIngress(ing *v1beta1.Ingress, ignoreHostnameAnnotation bool) [ targets = targetsFromIngressStatus(ing.Status) } - providerSpecific := getProviderSpecificAnnotations(ing.Annotations) + providerSpecific, setIdentifier := getProviderSpecificAnnotations(ing.Annotations) for _, rule := range ing.Spec.Rules { if rule.Host == "" { continue } - endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific)...) + endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific, setIdentifier)...) } for _, tls := range ing.Spec.TLS { @@ -275,7 +275,7 @@ func endpointsFromIngress(ing *v1beta1.Ingress, ignoreHostnameAnnotation bool) [ if host == "" { continue } - endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific)...) + endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier)...) } } @@ -283,7 +283,7 @@ func endpointsFromIngress(ing *v1beta1.Ingress, ignoreHostnameAnnotation bool) [ if !ignoreHostnameAnnotation { hostnameList := getHostnamesFromAnnotations(ing.Annotations) for _, hostname := range hostnameList { - endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific)...) + endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...) } } return endpoints diff --git a/source/service.go b/source/service.go index 1fba3d624..11ab0c3bb 100644 --- a/source/service.go +++ b/source/service.go @@ -285,10 +285,10 @@ func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service) ([]*endpoint.End return nil, fmt.Errorf("failed to apply template on service %s: %v", svc.String(), err) } - providerSpecific := getProviderSpecificAnnotations(svc.Annotations) + providerSpecific, setIdentifier := getProviderSpecificAnnotations(svc.Annotations) hostnameList := strings.Split(strings.Replace(buf.String(), " ", "", -1), ",") for _, hostname := range hostnameList { - endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific)...) + endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific, setIdentifier)...) } return endpoints, nil @@ -299,10 +299,10 @@ func (sc *serviceSource) endpoints(svc *v1.Service) []*endpoint.Endpoint { var endpoints []*endpoint.Endpoint // Skip endpoints if we do not want entries from annotations if !sc.ignoreHostnameAnnotation { - providerSpecific := getProviderSpecificAnnotations(svc.Annotations) + providerSpecific, setIdentifier := getProviderSpecificAnnotations(svc.Annotations) hostnameList := getHostnamesFromAnnotations(svc.Annotations) for _, hostname := range hostnameList { - endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific)...) + endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific, setIdentifier)...) } } return endpoints @@ -358,7 +358,7 @@ func (sc *serviceSource) setResourceLabel(service *v1.Service, endpoints []*endp } } -func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, providerSpecific endpoint.ProviderSpecific) []*endpoint.Endpoint { +func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, providerSpecific endpoint.ProviderSpecific, setIdentifier string) []*endpoint.Endpoint { hostname = strings.TrimSuffix(hostname, ".") ttl, err := getTTLFromAnnotations(svc.Annotations) if err != nil { @@ -366,21 +366,19 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro } epA := &endpoint.Endpoint{ - RecordTTL: ttl, - RecordType: endpoint.RecordTypeA, - Labels: endpoint.NewLabels(), - Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), - DNSName: hostname, - ProviderSpecific: providerSpecific, + RecordTTL: ttl, + RecordType: endpoint.RecordTypeA, + Labels: endpoint.NewLabels(), + Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), + DNSName: hostname, } epCNAME := &endpoint.Endpoint{ - RecordTTL: ttl, - RecordType: endpoint.RecordTypeCNAME, - Labels: endpoint.NewLabels(), - Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), - DNSName: hostname, - ProviderSpecific: providerSpecific, + RecordTTL: ttl, + RecordType: endpoint.RecordTypeCNAME, + Labels: endpoint.NewLabels(), + Targets: make(endpoint.Targets, 0, defaultTargetsCapacity), + DNSName: hostname, } var endpoints []*endpoint.Endpoint @@ -423,6 +421,10 @@ func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, pro if len(epCNAME.Targets) > 0 { endpoints = append(endpoints, epCNAME) } + for _, endpoint := range endpoints { + endpoint.ProviderSpecific = providerSpecific + endpoint.SetIdentifier = setIdentifier + } return endpoints } diff --git a/source/source.go b/source/source.go index 83fc84691..9dc599ac3 100644 --- a/source/source.go +++ b/source/source.go @@ -45,6 +45,8 @@ const ( const ( // The annotation used for determining if traffic will go through Cloudflare CloudflareProxiedKey = "external-dns.alpha.kubernetes.io/cloudflare-proxied" + + SetIdentifierKey = "external-dns.alpha.kubernetes.io/set-identifier" ) const ( @@ -86,7 +88,7 @@ func getAliasFromAnnotations(annotations map[string]string) bool { return exists && aliasAnnotation == "true" } -func getProviderSpecificAnnotations(annotations map[string]string) endpoint.ProviderSpecific { +func getProviderSpecificAnnotations(annotations map[string]string) (endpoint.ProviderSpecific, string) { providerSpecificAnnotations := endpoint.ProviderSpecific{} v, exists := annotations[CloudflareProxiedKey] @@ -102,7 +104,19 @@ func getProviderSpecificAnnotations(annotations map[string]string) endpoint.Prov Value: "true", }) } - return providerSpecificAnnotations + setIdentifier := "" + for k, v := range annotations { + if k == SetIdentifierKey { + setIdentifier = v + } else if strings.HasPrefix(k, "external-dns.alpha.kubernetes.io/aws-") { + attr := strings.TrimPrefix(k, "external-dns.alpha.kubernetes.io/aws-") + providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{ + Name: fmt.Sprintf("aws/%s", attr), + Value: v, + }) + } + } + return providerSpecificAnnotations, setIdentifier } // getTargetsFromTargetAnnotation gets endpoints from optional "target" annotation. @@ -133,7 +147,7 @@ func suitableType(target string) string { } // endpointsForHostname returns the endpoint objects for each host-target combination. -func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoint.TTL, providerSpecific endpoint.ProviderSpecific) []*endpoint.Endpoint { +func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoint.TTL, providerSpecific endpoint.ProviderSpecific, setIdentifier string) []*endpoint.Endpoint { var endpoints []*endpoint.Endpoint var aTargets endpoint.Targets @@ -156,6 +170,7 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin RecordType: endpoint.RecordTypeA, Labels: endpoint.NewLabels(), ProviderSpecific: providerSpecific, + SetIdentifier: setIdentifier, } endpoints = append(endpoints, epA) } @@ -168,6 +183,7 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin RecordType: endpoint.RecordTypeCNAME, Labels: endpoint.NewLabels(), ProviderSpecific: providerSpecific, + SetIdentifier: setIdentifier, } endpoints = append(endpoints, epCNAME) }