diff --git a/source/contour_httpproxy.go b/source/contour_httpproxy.go index 821ec0e9c..b99b16494 100644 --- a/source/contour_httpproxy.go +++ b/source/contour_httpproxy.go @@ -17,11 +17,9 @@ limitations under the License. package source import ( - "bytes" "context" "fmt" "sort" - "strings" "text/template" "time" @@ -189,22 +187,17 @@ func (sc *httpProxySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, } func (sc *httpProxySource) endpointsFromTemplate(httpProxy *projectcontour.HTTPProxy) ([]*endpoint.Endpoint, error) { - // Process the whole template string - var buf bytes.Buffer - err := sc.fqdnTemplate.Execute(&buf, httpProxy) + hostnames, err := execTemplate(sc.fqdnTemplate, httpProxy) if err != nil { - return nil, errors.Wrapf(err, "failed to apply template on HTTPProxy %s/%s", httpProxy.Namespace, httpProxy.Name) + return nil, err } - hostnames := buf.String() - ttl, err := getTTLFromAnnotations(httpProxy.Annotations) if err != nil { log.Warn(err) } targets := getTargetsFromTargetAnnotation(httpProxy.Annotations) - if len(targets) == 0 { for _, lb := range httpProxy.Status.LoadBalancer.Ingress { if lb.IP != "" { @@ -219,10 +212,7 @@ func (sc *httpProxySource) endpointsFromTemplate(httpProxy *projectcontour.HTTPP providerSpecific, setIdentifier := getProviderSpecificAnnotations(httpProxy.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, ".") + for _, hostname := range hostnames { endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...) } return endpoints, nil diff --git a/source/contour_ingressroute.go b/source/contour_ingressroute.go index be37cab54..c6b243597 100644 --- a/source/contour_ingressroute.go +++ b/source/contour_ingressroute.go @@ -17,7 +17,6 @@ limitations under the License. package source import ( - "bytes" "context" "fmt" "sort" @@ -200,22 +199,17 @@ func (sc *ingressRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoi } func (sc *ingressRouteSource) endpointsFromTemplate(ctx context.Context, ingressRoute *contour.IngressRoute) ([]*endpoint.Endpoint, error) { - // Process the whole template string - var buf bytes.Buffer - err := sc.fqdnTemplate.Execute(&buf, ingressRoute) + hostnames, err := execTemplate(sc.fqdnTemplate, ingressRoute) if err != nil { - return nil, fmt.Errorf("failed to apply template on ingressroute %s/%s: %v", ingressRoute.Namespace, ingressRoute.Name, err) + return nil, err } - hostnames := buf.String() - ttl, err := getTTLFromAnnotations(ingressRoute.Annotations) if err != nil { log.Warn(err) } targets := getTargetsFromTargetAnnotation(ingressRoute.Annotations) - if len(targets) == 0 { targets, err = sc.targetsFromContourLoadBalancer(ctx) if err != nil { @@ -226,10 +220,7 @@ func (sc *ingressRouteSource) endpointsFromTemplate(ctx context.Context, ingress providerSpecific, setIdentifier := getProviderSpecificAnnotations(ingressRoute.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, ".") + for _, hostname := range hostnames { endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...) } return endpoints, nil diff --git a/source/ingress.go b/source/ingress.go index 72f1d100a..3bcbff2a9 100644 --- a/source/ingress.go +++ b/source/ingress.go @@ -17,7 +17,6 @@ limitations under the License. package source import ( - "bytes" "context" "fmt" "sort" @@ -167,22 +166,17 @@ func (sc *ingressSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e } func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoint.Endpoint, error) { - // Process the whole template string - var buf bytes.Buffer - err := sc.fqdnTemplate.Execute(&buf, ing) + hostnames, err := execTemplate(sc.fqdnTemplate, ing) if err != nil { - return nil, fmt.Errorf("failed to apply template on ingress %s: %v", ing.String(), err) + return nil, err } - hostnames := buf.String() - ttl, err := getTTLFromAnnotations(ing.Annotations) if err != nil { log.Warn(err) } targets := getTargetsFromTargetAnnotation(ing.Annotations) - if len(targets) == 0 { targets = targetsFromIngressStatus(ing.Status) } @@ -190,10 +184,7 @@ func (sc *ingressSource) endpointsFromTemplate(ing *v1beta1.Ingress) ([]*endpoin 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, ".") + for _, hostname := range hostnames { endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...) } return endpoints, nil diff --git a/source/istio_gateway.go b/source/istio_gateway.go index 6d16b6146..bba8a8e6f 100644 --- a/source/istio_gateway.go +++ b/source/istio_gateway.go @@ -17,7 +17,6 @@ limitations under the License. package source import ( - "bytes" "context" "fmt" "sort" @@ -160,7 +159,7 @@ func (sc *gatewaySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e // apply template if host is missing on gateway if (sc.combineFQDNAnnotation || len(gwHostnames) == 0) && sc.fqdnTemplate != nil { - iHostnames, err := sc.hostNamesFromTemplate(gateway) + iHostnames, err := execTemplate(sc.fqdnTemplate, &gateway) if err != nil { return nil, err } @@ -336,18 +335,6 @@ func (sc *gatewaySource) hostNamesFromGateway(gateway networkingv1alpha3.Gateway return hostnames, nil } -func (sc *gatewaySource) hostNamesFromTemplate(gateway networkingv1alpha3.Gateway) ([]string, error) { - // Process the whole template string - var buf bytes.Buffer - err := sc.fqdnTemplate.Execute(&buf, gateway) - if err != nil { - return nil, fmt.Errorf("failed to apply template on istio gateway %v: %v", gateway, err) - } - - hostnames := strings.Split(strings.Replace(buf.String(), " ", "", -1), ",") - return hostnames, nil -} - func gatewaySelectorMatchesServiceSelector(gwSelector, svcSelector map[string]string) bool { for k, v := range gwSelector { if lbl, ok := svcSelector[k]; !ok || lbl != v { diff --git a/source/istio_virtualservice.go b/source/istio_virtualservice.go index 249d42583..32646085c 100644 --- a/source/istio_virtualservice.go +++ b/source/istio_virtualservice.go @@ -17,7 +17,6 @@ limitations under the License. package source import ( - "bytes" "context" "fmt" "sort" @@ -240,28 +239,20 @@ func (sc *virtualServiceSource) getGateway(ctx context.Context, gatewayStr strin } func (sc *virtualServiceSource) endpointsFromTemplate(ctx context.Context, virtualService networkingv1alpha3.VirtualService) ([]*endpoint.Endpoint, error) { - // Process the whole template string - var buf bytes.Buffer - err := sc.fqdnTemplate.Execute(&buf, virtualService) + hostnames, err := execTemplate(sc.fqdnTemplate, &virtualService) if err != nil { - return nil, fmt.Errorf("failed to apply template on istio config %v: %v", virtualService, err) + return nil, err } - hostnamesTemplate := buf.String() - ttl, err := getTTLFromAnnotations(virtualService.Annotations) if err != nil { log.Warn(err) } - var endpoints []*endpoint.Endpoint - providerSpecific, setIdentifier := getProviderSpecificAnnotations(virtualService.Annotations) - // splits the FQDN template and removes the trailing periods - hostnames := strings.Split(strings.Replace(hostnamesTemplate, " ", "", -1), ",") + var endpoints []*endpoint.Endpoint for _, hostname := range hostnames { - hostname = strings.TrimSuffix(hostname, ".") targets, err := sc.targetsFromVirtualService(ctx, virtualService, hostname) if err != nil { return endpoints, err diff --git a/source/node.go b/source/node.go index 31c23d5c9..e9b948926 100644 --- a/source/node.go +++ b/source/node.go @@ -17,7 +17,6 @@ limitations under the License. package source import ( - "bytes" "context" "fmt" "text/template" @@ -121,14 +120,15 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro } if ns.fqdnTemplate != nil { - // Process the whole template string - var buf bytes.Buffer - err := ns.fqdnTemplate.Execute(&buf, node) + hostnames, err := execTemplate(ns.fqdnTemplate, node) if err != nil { - return nil, fmt.Errorf("failed to apply template on node %s: %v", node.Name, err) + return nil, err } - - ep.DNSName = buf.String() + hostname := "" + if len(hostnames) > 0 { + hostname = hostnames[0] + } + ep.DNSName = hostname log.Debugf("applied template for %s, converting to %s", node.Name, ep.DNSName) } else { ep.DNSName = node.Name diff --git a/source/openshift_route.go b/source/openshift_route.go index e1f9aca15..e6613c301 100644 --- a/source/openshift_route.go +++ b/source/openshift_route.go @@ -17,11 +17,9 @@ limitations under the License. package source import ( - "bytes" "context" "fmt" "sort" - "strings" "text/template" "time" @@ -165,22 +163,17 @@ func (ors *ocpRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, } func (ors *ocpRouteSource) endpointsFromTemplate(ocpRoute *routev1.Route) ([]*endpoint.Endpoint, error) { - // Process the whole template string - var buf bytes.Buffer - err := ors.fqdnTemplate.Execute(&buf, ocpRoute) + hostnames, err := execTemplate(ors.fqdnTemplate, ocpRoute) if err != nil { - return nil, fmt.Errorf("failed to apply template on OpenShift Route %s: %s", ocpRoute.Name, err) + return nil, err } - hostnames := buf.String() - ttl, err := getTTLFromAnnotations(ocpRoute.Annotations) if err != nil { log.Warn(err) } targets := getTargetsFromTargetAnnotation(ocpRoute.Annotations) - if len(targets) == 0 { targets = targetsFromOcpRouteStatus(ocpRoute.Status) } @@ -188,10 +181,7 @@ func (ors *ocpRouteSource) endpointsFromTemplate(ocpRoute *routev1.Route) ([]*en providerSpecific, setIdentifier := getProviderSpecificAnnotations(ocpRoute.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, ".") + for _, hostname := range hostnames { endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...) } return endpoints, nil diff --git a/source/service.go b/source/service.go index 026e53ead..954b9bbfe 100644 --- a/source/service.go +++ b/source/service.go @@ -17,7 +17,6 @@ limitations under the License. package source import ( - "bytes" "context" "fmt" "sort" @@ -345,18 +344,15 @@ func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname stri } func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service) ([]*endpoint.Endpoint, error) { - var endpoints []*endpoint.Endpoint - - // Process the whole template string - var buf bytes.Buffer - err := sc.fqdnTemplate.Execute(&buf, svc) + hostnames, err := execTemplate(sc.fqdnTemplate, svc) if err != nil { - return nil, fmt.Errorf("failed to apply template on service %s: %v", svc.String(), err) + return nil, err } providerSpecific, setIdentifier := getProviderSpecificAnnotations(svc.Annotations) - hostnameList := strings.Split(strings.Replace(buf.String(), " ", "", -1), ",") - for _, hostname := range hostnameList { + + var endpoints []*endpoint.Endpoint + for _, hostname := range hostnames { endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific, setIdentifier, false)...) } diff --git a/source/source.go b/source/source.go index 99d921b3e..98b5991f9 100644 --- a/source/source.go +++ b/source/source.go @@ -17,6 +17,7 @@ limitations under the License. package source import ( + "bytes" "context" "fmt" "math" @@ -25,9 +26,11 @@ import ( "strings" "text/template" "time" + "unicode" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/external-dns/endpoint" @@ -107,6 +110,25 @@ func parseTTL(s string) (ttlSeconds int64, err error) { return int64(ttlDuration.Seconds()), nil } +type kubeObject interface { + runtime.Object + metav1.Object +} + +func execTemplate(tmpl *template.Template, obj kubeObject) (hostnames []string, err error) { + var buf bytes.Buffer + if err := tmpl.Execute(&buf, obj); err != nil { + kind := obj.GetObjectKind().GroupVersionKind().Kind + return nil, fmt.Errorf("failed to apply template on %s %s/%s: %w", kind, obj.GetNamespace(), obj.GetName(), err) + } + for _, name := range strings.Split(buf.String(), ",") { + name = strings.TrimFunc(name, unicode.IsSpace) + name = strings.TrimSuffix(name, ".") + hostnames = append(hostnames, name) + } + return hostnames, nil +} + func parseTemplate(fqdnTemplate string) (tmpl *template.Template, err error) { if fqdnTemplate == "" { return nil, nil