Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/sources/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Sources are responsible for:
| **pod** | annotation,label | all,single | true | true | false | kubernetes core | Pod |
| **service** | annotation,label | all,single | true | true | true | kubernetes core | Service |
| **skipper-routegroup** | annotation | all,single | true | false | true | ingress controllers | RouteGroup.zalando.org |
| **traefik-proxy** | annotation | all,single | false | false | true | ingress controllers | IngressRoute.traefik.io<br/>IngressRouteTCP.traefik.io<br/>IngressRouteUDP.traefik.io |
| **traefik-proxy** | annotation | all,single | true | false | true | ingress controllers | IngressRoute.traefik.io<br/>IngressRouteTCP.traefik.io<br/>IngressRouteUDP.traefik.io |
| **unstructured** | annotation,label | all,single | true | false | false | custom resources | Unstructured |

## Usage
Expand Down
113 changes: 97 additions & 16 deletions source/traefik_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/cache"

"sigs.k8s.io/external-dns/source/template"
"sigs.k8s.io/external-dns/source/types"

"sigs.k8s.io/external-dns/endpoint"
Expand Down Expand Up @@ -87,14 +88,15 @@ var (
// +externaldns:source:resources=IngressRoute.traefik.io,IngressRouteTCP.traefik.io,IngressRouteUDP.traefik.io
// +externaldns:source:filters=annotation
// +externaldns:source:namespace=all,single
// +externaldns:source:fqdn-template=false
// +externaldns:source:fqdn-template=true
// +externaldns:source:provider-specific=true
type traefikSource struct {
dynamicKubeClient dynamic.Interface
kubeClient kubernetes.Interface
annotationFilter string
namespace string
ignoreHostnameAnnotation bool
templateEngine template.Engine
ingressRouteInformer kubeinformers.GenericInformer
ingressRouteTcpInformer kubeinformers.GenericInformer
ingressRouteUdpInformer kubeinformers.GenericInformer
Expand Down Expand Up @@ -173,6 +175,7 @@ func NewTraefikSource(
return &traefikSource{
annotationFilter: cfg.AnnotationFilter,
ignoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation,
templateEngine: cfg.TemplateEngine,
dynamicKubeClient: dynamicKubeClient,
ingressRouteInformer: ingressRouteInformer,
ingressRouteTcpInformer: ingressRouteTcpInformer,
Expand Down Expand Up @@ -245,9 +248,7 @@ func (ts *traefikSource) ingressRouteEndpoints() ([]*endpoint.Endpoint, error) {
return typed, ts.unstructuredConverter.scheme.Convert(u, typed, nil)
},
ts.annotationFilter,
func(r *IngressRoute, targets endpoint.Targets) []*endpoint.Endpoint {
return ts.endpointsFromIngressRoute(r, targets)
},
ts.endpointsFromIngressRoute,
)
}

Expand Down Expand Up @@ -287,7 +288,10 @@ func (ts *traefikSource) ingressRouteTCPEndpoints() ([]*endpoint.Endpoint, error

fullname := fmt.Sprintf("%s/%s", ingressRouteTCP.Namespace, ingressRouteTCP.Name)

ingressEndpoints := ts.endpointsFromIngressRouteTCP(ingressRouteTCP, targets)
ingressEndpoints, err := ts.endpointsFromIngressRouteTCP(ingressRouteTCP, targets)
if err != nil {
return nil, err
}
if endpoint.HasNoEmptyEndpoints(ingressEndpoints, types.TraefikProxy, ingressRouteTCP) {
continue
}
Expand Down Expand Up @@ -323,9 +327,7 @@ func (ts *traefikSource) oldIngressRouteEndpoints() ([]*endpoint.Endpoint, error
return typed, ts.unstructuredConverter.scheme.Convert(u, typed, nil)
},
ts.annotationFilter,
func(r *IngressRoute, targets endpoint.Targets) []*endpoint.Endpoint {
return ts.endpointsFromIngressRoute(r, targets)
},
ts.endpointsFromIngressRoute,
)
}

Expand Down Expand Up @@ -358,7 +360,7 @@ func (ts *traefikSource) oldIngressRouteUDPEndpoints() ([]*endpoint.Endpoint, er
}

// endpointsFromIngressRoute extracts the endpoints from a IngressRoute object
func (ts *traefikSource) endpointsFromIngressRoute(ingressRoute *IngressRoute, targets endpoint.Targets) []*endpoint.Endpoint {
func (ts *traefikSource) endpointsFromIngressRoute(ingressRoute *IngressRoute, targets endpoint.Targets) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint

resource := fmt.Sprintf("ingressroute/%s/%s", ingressRoute.Namespace, ingressRoute.Name)
Expand Down Expand Up @@ -387,11 +389,21 @@ func (ts *traefikSource) endpointsFromIngressRoute(ingressRoute *IngressRoute, t
}
}

return endpoints
endpoints, err := ts.templateEngine.CombineWithEndpoints(
endpoints,
func() ([]*endpoint.Endpoint, error) { return ts.endpointsFromFQDNTargetTemplate(ingressRoute) },
)
if err != nil {
return nil, err
}
return ts.templateEngine.CombineWithEndpoints(
endpoints,
func() ([]*endpoint.Endpoint, error) { return ts.endpointsFromTemplate(ingressRoute) },
)
}

// endpointsFromIngressRouteTCP extracts the endpoints from a IngressRouteTCP object
func (ts *traefikSource) endpointsFromIngressRouteTCP(ingressRoute *IngressRouteTCP, targets endpoint.Targets) []*endpoint.Endpoint {
func (ts *traefikSource) endpointsFromIngressRouteTCP(ingressRoute *IngressRouteTCP, targets endpoint.Targets) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint

resource := fmt.Sprintf("ingressroutetcp/%s/%s", ingressRoute.Namespace, ingressRoute.Name)
Expand Down Expand Up @@ -420,11 +432,21 @@ func (ts *traefikSource) endpointsFromIngressRouteTCP(ingressRoute *IngressRoute
}
}

return endpoints
endpoints, err := ts.templateEngine.CombineWithEndpoints(
endpoints,
func() ([]*endpoint.Endpoint, error) { return ts.endpointsFromFQDNTargetTemplate(ingressRoute) },
)
if err != nil {
return nil, err
}
return ts.templateEngine.CombineWithEndpoints(
endpoints,
func() ([]*endpoint.Endpoint, error) { return ts.endpointsFromTemplate(ingressRoute) },
)
}

// endpointsFromIngressRouteUDP extracts the endpoints from a IngressRouteUDP object
func (ts *traefikSource) endpointsFromIngressRouteUDP(ingressRoute *IngressRouteUDP, targets endpoint.Targets) []*endpoint.Endpoint {
func (ts *traefikSource) endpointsFromIngressRouteUDP(ingressRoute *IngressRouteUDP, targets endpoint.Targets) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint

resource := fmt.Sprintf("ingressrouteudp/%s/%s", ingressRoute.Namespace, ingressRoute.Name)
Expand All @@ -440,7 +462,17 @@ func (ts *traefikSource) endpointsFromIngressRouteUDP(ingressRoute *IngressRoute
}
}

return endpoints
endpoints, err := ts.templateEngine.CombineWithEndpoints(
endpoints,
func() ([]*endpoint.Endpoint, error) { return ts.endpointsFromFQDNTargetTemplate(ingressRoute) },
)
if err != nil {
return nil, err
}
return ts.templateEngine.CombineWithEndpoints(
endpoints,
func() ([]*endpoint.Endpoint, error) { return ts.endpointsFromTemplate(ingressRoute) },
)
}

func (ts *traefikSource) AddEventHandler(_ context.Context, handler func()) {
Expand Down Expand Up @@ -839,6 +871,52 @@ func (in *IngressRouteUDP) GetAnnotations() map[string]string {
return in.Annotations
}

// traefikObject is satisfied by IngressRoute, IngressRouteTCP, and IngressRouteUDP.
type traefikObject interface {
runtime.Object
metav1.Object
}

// endpointsFromTemplate creates endpoints using the FQDN and target templates.
func (ts *traefikSource) endpointsFromTemplate(obj traefikObject) ([]*endpoint.Endpoint, error) {
hostnames, err := ts.templateEngine.ExecFQDN(obj)
if err != nil || len(hostnames) == 0 {
return nil, err
}
targets, err := ts.templateEngine.ExecTarget(obj)
if err != nil {
return nil, err
}
return EndpointsForHostsAndTargets(hostnames, targets), nil
}

// endpointsFromFQDNTargetTemplate creates endpoints from host:target pairs produced by the fqdn-target template.
func (ts *traefikSource) endpointsFromFQDNTargetTemplate(obj traefikObject) ([]*endpoint.Endpoint, error) {
pairs, err := ts.templateEngine.ExecFQDNTarget(obj)
if err != nil || len(pairs) == 0 {
return nil, err
}

endpoints := make([]*endpoint.Endpoint, 0, len(pairs))
for _, pair := range pairs {
parts := strings.SplitN(pair, ":", 2)
if len(parts) != 2 {
log.Debugf("Skipping invalid host:target pair %q from %s %s/%s: missing ':' separator",
pair, strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind), obj.GetNamespace(), obj.GetName())
continue
}
host := strings.TrimSpace(parts[0])
target := strings.TrimSpace(parts[1])
if host == "" || target == "" {
log.Debugf("Skipping incomplete host:target pair %q from %s %s/%s: field may not yet be populated",
pair, strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind), obj.GetNamespace(), obj.GetName())
continue
}
endpoints = append(endpoints, endpoint.NewEndpoint(host, endpoint.SuitableType(target), target))
}
return endpoint.MergeEndpoints(endpoints), nil
}

// extractEndpoints is a generic function that extracts endpoints from Kubernetes resources.
// It performs the following steps:
// 1. Lists all objects in the specified namespace using the provided informer.
Expand All @@ -851,7 +929,7 @@ func extractEndpoints[T annotations.AnnotatedObject](
namespace string,
convertFunc func(*unstructured.Unstructured) (T, error),
annotationFilter string,
generateEndpoints func(T, endpoint.Targets) []*endpoint.Endpoint,
generateEndpoints func(T, endpoint.Targets) ([]*endpoint.Endpoint, error),
) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint

Expand Down Expand Up @@ -883,7 +961,10 @@ func extractEndpoints[T annotations.AnnotatedObject](
targets := annotations.TargetsFromTargetAnnotation(item.GetAnnotations())

name := getObjectFullName(item)
ingressEndpoints := generateEndpoints(item, targets)
ingressEndpoints, err := generateEndpoints(item, targets)
if err != nil {
return nil, err
}

if len(ingressEndpoints) == 0 {
log.Debugf("No endpoints could be generated from Host %s", name)
Expand Down
Loading