Added traefik source

This commit is contained in:
Thomas Kosiewski 2022-09-27 22:56:56 +02:00
parent 6e0b8b311b
commit 458b702762
7 changed files with 667 additions and 3 deletions

View File

@ -0,0 +1,98 @@
# Configuring ExternalDNS to use the Traefik Proxy Source
This tutorial describes how to configure ExternalDNS to use the Traefik Proxy source.
It is meant to supplement the other provider-specific setup tutorials.
## Manifest (for clusters without RBAC enabled)
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
# update this to the desired external-dns version
image: k8s.gcr.io/external-dns/external-dns:v0.12.2
args:
- --source=traefik-proxy
- --provider=aws
- --registry=txt
- --txt-owner-id=my-identifier
```
## Manifest (for clusters with RBAC enabled)
Could be change if you have mulitple sources
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services","endpoints","pods"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list","watch"]
- apiGroups: ["traefik.containo.us"]
resources: ["ingressroutes", "ingressroutestcps", "ingressroutesudps"]
verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
# update this to the desired external-dns version
image: k8s.gcr.io/external-dns/external-dns:v0.12.2
args:
- --source=traefik-proxy
- --provider=aws
- --registry=txt
- --txt-owner-id=my-identifier
```

4
go.mod
View File

@ -56,7 +56,7 @@ require (
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.599
github.com/transip/gotransip/v6 v6.19.0
github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60
github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556
github.com/vinyldns/go-vinyldns v0.9.16
github.com/vultr/govultr/v2 v2.17.2
go.etcd.io/etcd/api/v3 v3.5.5
go.etcd.io/etcd/client/v3 v3.5.5
@ -167,6 +167,7 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/terra-farm/udnssdk v1.3.5 // indirect
github.com/traefik/paerser v0.1.9 // indirect
github.com/vektah/gqlparser/v2 v2.5.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect
go.mongodb.org/mongo-driver v1.5.1 // indirect
@ -189,6 +190,7 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/resty.v1 v1.12.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a // indirect

6
go.sum
View File

@ -189,6 +189,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bodgit/tsig v1.2.0 h1:wNfc7yTk2OuWh/s7nEFa9h+SkIfTn7e4xlFtf1Sgvr4=
github.com/bodgit/tsig v1.2.0/go.mod h1:bsN2ntwGE/s3EeoawjAoKUcAfO4Fr0nGKC72vNF/cqM=
@ -197,6 +198,7 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8n
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@ -797,6 +799,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
@ -1171,6 +1175,8 @@ github.com/vektah/gqlparser/v2 v2.5.0 h1:GwEwy7AJsqPWrey0bHnn+3JLaHLZVT66wY/+O+T
github.com/vektah/gqlparser/v2 v2.5.0/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs=
github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556 h1:UbVjBjgJUYGD8MlobEdOR+yTeNqaNa2Gf1/nskVNCSE=
github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg=
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=

View File

@ -19,11 +19,12 @@ package testutils
import (
"fmt"
"sort"
"testing"
"sigs.k8s.io/external-dns/endpoint"
)
func ExampleSameEndpoints() {
func TestExampleSameEndpoints(t *testing.T) {
eps := []*endpoint.Endpoint{
{
DNSName: "example.org",

View File

@ -404,7 +404,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(source.DefaultRoutegroupVersion).StringVar(&cfg.SkipperRouteGroupVersion)
// Flags related to processing source
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, gateway-httproute, gateway-grpcroute, gateway-tlsroute, gateway-tcproute, gateway-udproute, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress, f5-virtualserver)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "gateway-httproute", "gateway-grpcroute", "gateway-tlsroute", "gateway-tcproute", "gateway-udproute", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress", "f5-virtualserver")
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, gateway-httproute, gateway-grpcroute, gateway-tlsroute, gateway-tcproute, gateway-udproute, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress, f5-virtualserver)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "gateway-httproute", "gateway-grpcroute", "gateway-tlsroute", "gateway-tcproute", "gateway-udproute", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress", "f5-virtualserver", "traefik-proxy")
app.Flag("openshift-router-name", "if source is openshift-route then you can pass the ingress controller name. Based on this name external-dns will select the respective router from the route status and map that routerCanonicalHostname to the route host while creating a CNAME record.").StringVar(&cfg.OCPRouterName)
app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
app.Flag("annotation-filter", "Filter sources managed by external-dns via annotation using label selector semantics (default: all sources)").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter)

View File

@ -289,6 +289,16 @@ func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg
return nil, err
}
return NewGlooSource(dynamicClient, kubernetesClient, cfg.GlooNamespace)
case "traefik-proxy":
kubernetesClient, err := p.KubeClient()
if err != nil {
return nil, err
}
dynamicClient, err := p.DynamicKubernetesClient()
if err != nil {
return nil, err
}
return NewTraefikSource(ctx, dynamicClient, kubernetesClient, cfg.Namespace, cfg.AnnotationFilter)
case "openshift-route":
ocpClient, err := p.OpenShiftClient()
if err != nil {

547
source/traefik_proxy.go Normal file
View File

@ -0,0 +1,547 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package source
import (
"context"
"fmt"
"sort"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/dynamic/dynamicinformer"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/external-dns/endpoint"
traefikV1alpha1 "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
)
var (
ingressrouteGVR = schema.GroupVersionResource{
Group: traefikV1alpha1.SchemeGroupVersion.Group,
Version: traefikV1alpha1.SchemeGroupVersion.Version,
Resource: "ingressroutes",
}
ingressrouteTCPGVR = schema.GroupVersionResource{
Group: traefikV1alpha1.SchemeGroupVersion.Group,
Version: traefikV1alpha1.SchemeGroupVersion.Version,
Resource: "ingressroutetcps",
}
ingressrouteUDPGVR = schema.GroupVersionResource{
Group: traefikV1alpha1.SchemeGroupVersion.Group,
Version: traefikV1alpha1.SchemeGroupVersion.Version,
Resource: "ingressrouteudps",
}
)
type traefikSource struct {
annotationFilter string
dynamicKubeClient dynamic.Interface
ingressRouteInformer informers.GenericInformer
ingressRouteTcpInformer informers.GenericInformer
ingressRouteUdpInformer informers.GenericInformer
kubeClient kubernetes.Interface
namespace string
unstructuredConverter *unstructuredConverter
}
func NewTraefikSource(ctx context.Context, dynamicKubeClient dynamic.Interface, kubeClient kubernetes.Interface, namespace string, annotationFilter string) (Source, error) {
// Use shared informer to listen for add/update/delete of Host in the specified namespace.
// Set resync period to 0, to prevent processing when nothing has changed.
informerFactory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dynamicKubeClient, 0, namespace, nil)
ingressRouteInformer := informerFactory.ForResource(ingressrouteGVR)
ingressRouteTcpInformer := informerFactory.ForResource(ingressrouteTCPGVR)
ingressRouteUdpInformer := informerFactory.ForResource(ingressrouteUDPGVR)
// Add default resource event handlers to properly initialize informers.
ingressRouteInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
},
)
ingressRouteTcpInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
},
)
ingressRouteUdpInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
},
)
informerFactory.Start((ctx.Done()))
// wait for the local cache to be populated.
if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil {
return nil, err
}
uc, err := newTraefikUnstructuredConverter()
if err != nil {
return nil, errors.Wrapf(err, "failed to setup Unstructured Converter")
}
return &traefikSource{
annotationFilter: annotationFilter,
dynamicKubeClient: dynamicKubeClient,
ingressRouteInformer: ingressRouteInformer,
ingressRouteTcpInformer: ingressRouteTcpInformer,
ingressRouteUdpInformer: ingressRouteUdpInformer,
kubeClient: kubeClient,
namespace: namespace,
unstructuredConverter: uc,
}, nil
}
func (ts *traefikSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
ingressRouteEndpoints, err := ts.ingressRouteEndpoints()
if err != nil {
return nil, err
}
ingressRouteTCPEndpoints, err := ts.ingressRouteTCPEndpoints()
if err != nil {
return nil, err
}
ingressRouteUDPEndpoints, err := ts.ingressRouteUDPEndpoints()
if err != nil {
return nil, err
}
endpoints = append(endpoints, ingressRouteEndpoints...)
endpoints = append(endpoints, ingressRouteTCPEndpoints...)
endpoints = append(endpoints, ingressRouteUDPEndpoints...)
for _, ep := range endpoints {
sort.Sort(ep.Targets)
}
return endpoints, nil
}
func (ts *traefikSource) ingressRouteEndpoints() ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
irs, err := ts.ingressRouteInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything())
if err != nil {
return nil, err
}
var ingressRoutes []*traefikV1alpha1.IngressRoute
for _, ingressRouteObj := range irs {
unstructuredHost, ok := ingressRouteObj.(*unstructured.Unstructured)
if !ok {
return nil, errors.New("could not convert")
}
ingressRoute := &traefikV1alpha1.IngressRoute{}
err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRoute, nil)
if err != nil {
return nil, err
}
ingressRoutes = append(ingressRoutes, ingressRoute)
}
ingressRoutes, err = ts.filterByAnnotationsIngressRoute(ingressRoutes)
if err != nil {
return nil, errors.Wrap(err, "failed to filter IngressRoute")
}
for _, ingressRoute := range ingressRoutes {
var targets endpoint.Targets
targets = append(targets, getTargetsFromTargetAnnotation(ingressRoute.Annotations)...)
fullname := fmt.Sprintf("%s/%s", ingressRoute.Namespace, ingressRoute.Name)
ingressEndpoints, err := ts.endpointsFromIngressRoute(ingressRoute, targets)
if err != nil {
return nil, err
}
if len(ingressEndpoints) == 0 {
log.Debugf("No endpoints could be generated from Host %s", fullname)
continue
}
log.Debugf("Endpoints generated from IngressRoute: %s: %v", fullname, ingressEndpoints)
ts.setResourceLabelIngressRoute(ingressRoute, ingressEndpoints)
ts.setDualstackLabelIngressRoute(ingressRoute, ingressEndpoints)
endpoints = append(endpoints, ingressEndpoints...)
}
return endpoints, nil
}
func (ts *traefikSource) ingressRouteTCPEndpoints() ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
irs, err := ts.ingressRouteTcpInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything())
if err != nil {
return nil, err
}
var ingressRoutes []*traefikV1alpha1.IngressRouteTCP
for _, ingressRouteObj := range irs {
unstructuredHost, ok := ingressRouteObj.(*unstructured.Unstructured)
if !ok {
return nil, errors.New("could not convert")
}
ingressRoute := &traefikV1alpha1.IngressRouteTCP{}
err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRoute, nil)
if err != nil {
return nil, err
}
ingressRoutes = append(ingressRoutes, ingressRoute)
}
ingressRoutes, err = ts.filterByAnnotationsIngressRouteTCP(ingressRoutes)
if err != nil {
return nil, errors.Wrap(err, "failed to filter IngressRoute")
}
for _, ingressRoute := range ingressRoutes {
var targets endpoint.Targets
targets = append(targets, getTargetsFromTargetAnnotation(ingressRoute.Annotations)...)
fullname := fmt.Sprintf("%s/%s", ingressRoute.Namespace, ingressRoute.Name)
ingressEndpoints, err := ts.endpointsFromIngressRouteTCP(ingressRoute, targets)
if err != nil {
return nil, err
}
if len(ingressEndpoints) == 0 {
log.Debugf("No endpoints could be generated from Host %s", fullname)
continue
}
log.Debugf("Endpoints generated from IngressRoute: %s: %v", fullname, ingressEndpoints)
ts.setResourceLabelIngressRouteTCP(ingressRoute, ingressEndpoints)
ts.setDualstackLabelIngressRouteTCP(ingressRoute, ingressEndpoints)
endpoints = append(endpoints, ingressEndpoints...)
}
return endpoints, nil
}
func (ts *traefikSource) ingressRouteUDPEndpoints() ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
irs, err := ts.ingressRouteUdpInformer.Lister().ByNamespace(ts.namespace).List(labels.Everything())
if err != nil {
return nil, err
}
var ingressRoutes []*traefikV1alpha1.IngressRouteUDP
for _, ingressRouteObj := range irs {
unstructuredHost, ok := ingressRouteObj.(*unstructured.Unstructured)
if !ok {
return nil, errors.New("could not convert")
}
ingressRoute := &traefikV1alpha1.IngressRouteUDP{}
err := ts.unstructuredConverter.scheme.Convert(unstructuredHost, ingressRoute, nil)
if err != nil {
return nil, err
}
ingressRoutes = append(ingressRoutes, ingressRoute)
}
ingressRoutes, err = ts.filterByAnnotationsIngressRouteUDP(ingressRoutes)
if err != nil {
return nil, errors.Wrap(err, "failed to filter IngressRoute")
}
for _, ingressRoute := range ingressRoutes {
var targets endpoint.Targets
targets = append(targets, getTargetsFromTargetAnnotation(ingressRoute.Annotations)...)
fullname := fmt.Sprintf("%s/%s", ingressRoute.Namespace, ingressRoute.Name)
ingressEndpoints, err := ts.endpointsFromIngressRouteUDP(ingressRoute, targets)
if err != nil {
return nil, err
}
if len(ingressEndpoints) == 0 {
log.Debugf("No endpoints could be generated from Host %s", fullname)
continue
}
log.Debugf("Endpoints generated from IngressRoute: %s: %v", fullname, ingressEndpoints)
ts.setResourceLabelIngressRouteUDP(ingressRoute, ingressEndpoints)
ts.setDualstackLabelIngressRouteUDP(ingressRoute, ingressEndpoints)
endpoints = append(endpoints, ingressEndpoints...)
}
return endpoints, nil
}
// filterByAnnotations filters a list of IngressRoute by a given annotation selector.
func (ts *traefikSource) filterByAnnotationsIngressRoute(ingressRoutes []*traefikV1alpha1.IngressRoute) ([]*traefikV1alpha1.IngressRoute, error) {
labelSelector, err := metav1.ParseToLabelSelector(ts.annotationFilter)
if err != nil {
return nil, err
}
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
if err != nil {
return nil, err
}
// empty filter returns original list
if selector.Empty() {
return ingressRoutes, nil
}
filteredList := []*traefikV1alpha1.IngressRoute{}
for _, ingressRoute := range ingressRoutes {
// convert the IngressRoute's annotations to an equivalent label selector
annotations := labels.Set(ingressRoute.Annotations)
// include IngressRoute if its annotations match the selector
if selector.Matches(annotations) {
filteredList = append(filteredList, ingressRoute)
}
}
return filteredList, nil
}
// filterByAnnotations filters a list of IngressRouteTCP by a given annotation selector.
func (ts *traefikSource) filterByAnnotationsIngressRouteTCP(ingressRoutes []*traefikV1alpha1.IngressRouteTCP) ([]*traefikV1alpha1.IngressRouteTCP, error) {
labelSelector, err := metav1.ParseToLabelSelector(ts.annotationFilter)
if err != nil {
return nil, err
}
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
if err != nil {
return nil, err
}
// empty filter returns original list
if selector.Empty() {
return ingressRoutes, nil
}
filteredList := []*traefikV1alpha1.IngressRouteTCP{}
for _, ingressRoute := range ingressRoutes {
// convert the IngressRoute's annotations to an equivalent label selector
annotations := labels.Set(ingressRoute.Annotations)
// include IngressRoute if its annotations match the selector
if selector.Matches(annotations) {
filteredList = append(filteredList, ingressRoute)
}
}
return filteredList, nil
}
// filterByAnnotations filters a list of IngressRoute by a given annotation selector.
func (ts *traefikSource) filterByAnnotationsIngressRouteUDP(ingressRoutes []*traefikV1alpha1.IngressRouteUDP) ([]*traefikV1alpha1.IngressRouteUDP, error) {
labelSelector, err := metav1.ParseToLabelSelector(ts.annotationFilter)
if err != nil {
return nil, err
}
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
if err != nil {
return nil, err
}
// empty filter returns original list
if selector.Empty() {
return ingressRoutes, nil
}
filteredList := []*traefikV1alpha1.IngressRouteUDP{}
for _, ingressRoute := range ingressRoutes {
// convert the IngressRoute's annotations to an equivalent label selector
annotations := labels.Set(ingressRoute.Annotations)
// include IngressRoute if its annotations match the selector
if selector.Matches(annotations) {
filteredList = append(filteredList, ingressRoute)
}
}
return filteredList, nil
}
func (ts *traefikSource) setResourceLabelIngressRoute(ingressroute *traefikV1alpha1.IngressRoute, endpoints []*endpoint.Endpoint) {
for _, ep := range endpoints {
ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("ingressroute/%s/%s", ingressroute.Namespace, ingressroute.Name)
}
}
func (ts *traefikSource) setResourceLabelIngressRouteTCP(ingressroute *traefikV1alpha1.IngressRouteTCP, endpoints []*endpoint.Endpoint) {
for _, ep := range endpoints {
ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("ingressroutetcp/%s/%s", ingressroute.Namespace, ingressroute.Name)
}
}
func (ts *traefikSource) setResourceLabelIngressRouteUDP(ingressroute *traefikV1alpha1.IngressRouteUDP, endpoints []*endpoint.Endpoint) {
for _, ep := range endpoints {
ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("ingressrouteudp/%s/%s", ingressroute.Namespace, ingressroute.Name)
}
}
func (ts *traefikSource) setDualstackLabelIngressRoute(ingressRoute *traefikV1alpha1.IngressRoute, endpoints []*endpoint.Endpoint) {
val, ok := ingressRoute.Annotations[ALBDualstackAnnotationKey]
if ok && val == ALBDualstackAnnotationValue {
log.Debugf("Adding dualstack label to IngressRoute %s/%s.", ingressRoute.Namespace, ingressRoute.Name)
for _, ep := range endpoints {
ep.Labels[endpoint.DualstackLabelKey] = "true"
}
}
}
func (ts *traefikSource) setDualstackLabelIngressRouteTCP(ingressRoute *traefikV1alpha1.IngressRouteTCP, endpoints []*endpoint.Endpoint) {
val, ok := ingressRoute.Annotations[ALBDualstackAnnotationKey]
if ok && val == ALBDualstackAnnotationValue {
log.Debugf("Adding dualstack label to IngressRouteTCP %s/%s.", ingressRoute.Namespace, ingressRoute.Name)
for _, ep := range endpoints {
ep.Labels[endpoint.DualstackLabelKey] = "true"
}
}
}
func (ts *traefikSource) setDualstackLabelIngressRouteUDP(ingressRoute *traefikV1alpha1.IngressRouteUDP, endpoints []*endpoint.Endpoint) {
val, ok := ingressRoute.Annotations[ALBDualstackAnnotationKey]
if ok && val == ALBDualstackAnnotationValue {
log.Debugf("Adding dualstack label to IngressRouteUDP %s/%s.", ingressRoute.Namespace, ingressRoute.Name)
for _, ep := range endpoints {
ep.Labels[endpoint.DualstackLabelKey] = "true"
}
}
}
// endpointsFromIngressRoute extracts the endpoints from a IngressRoute object
func (ts *traefikSource) endpointsFromIngressRoute(ingressRoute *traefikV1alpha1.IngressRoute, targets endpoint.Targets) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
providerSpecific, setIdentifier := getProviderSpecificAnnotations(ingressRoute.Annotations)
ttl, err := getTTLFromAnnotations(ingressRoute.Annotations)
if err != nil {
return nil, err
}
hostnameList := getHostnamesFromAnnotations(ingressRoute.Annotations)
for _, hostname := range hostnameList {
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...)
}
// TODO: Implement Traefik router rule logic/regex magic
// if ingressRoute.Spec.Rules != nil {
// for _, rule := range ingressRoute.Spec.Rules {
// if rule.Host != "" {
// endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific, setIdentifier)...)
// }
// }
// }
return endpoints, nil
}
func (ts *traefikSource) endpointsFromIngressRouteTCP(ingressRoute *traefikV1alpha1.IngressRouteTCP, targets endpoint.Targets) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
providerSpecific, setIdentifier := getProviderSpecificAnnotations(ingressRoute.Annotations)
ttl, err := getTTLFromAnnotations(ingressRoute.Annotations)
if err != nil {
return nil, err
}
hostnameList := getHostnamesFromAnnotations(ingressRoute.Annotations)
for _, hostname := range hostnameList {
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...)
}
// TODO: Implement Traefik router rule logic/regex magic
// if ingressRoute.Spec.Rules != nil {
// for _, rule := range ingressRoute.Spec.Rules {
// if rule.Host != "" {
// endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific, setIdentifier)...)
// }
// }
// }
return endpoints, nil
}
func (ts *traefikSource) endpointsFromIngressRouteUDP(ingressRoute *traefikV1alpha1.IngressRouteUDP, targets endpoint.Targets) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
providerSpecific, setIdentifier := getProviderSpecificAnnotations(ingressRoute.Annotations)
ttl, err := getTTLFromAnnotations(ingressRoute.Annotations)
if err != nil {
return nil, err
}
hostnameList := getHostnamesFromAnnotations(ingressRoute.Annotations)
for _, hostname := range hostnameList {
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...)
}
// TODO: Implement Traefik router rule logic/regex magic
// if ingressRoute.Spec.Rules != nil {
// for _, rule := range ingressRoute.Spec.Rules {
// if rule.Host != "" {
// endpoints = append(endpoints, endpointsForHostname(rule.Host, targets, ttl, providerSpecific, setIdentifier)...)
// }
// }
// }
return endpoints, nil
}
func (ts *traefikSource) AddEventHandler(ctx context.Context, handler func()) {
// Right now there is no way to remove event handler from informer, see:
// https://github.com/kubernetes/kubernetes/issues/79610
log.Debug("Adding event handler for IngressRoute")
ts.ingressRouteInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
log.Debug("Adding event handler for IngressRouteTCP")
ts.ingressRouteTcpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
log.Debug("Adding event handler for IngressRouteUDP")
ts.ingressRouteUdpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
}
// newTraefikUnstructuredConverter returns a new unstructuredConverter initialized
func newTraefikUnstructuredConverter() (*unstructuredConverter, error) {
uc := &unstructuredConverter{
scheme: runtime.NewScheme(),
}
// Add the core types we need
uc.scheme.AddKnownTypes(ingressrouteGVR.GroupVersion(), &traefikV1alpha1.IngressRoute{}, &traefikV1alpha1.IngressRouteList{})
uc.scheme.AddKnownTypes(ingressrouteTCPGVR.GroupVersion(), &traefikV1alpha1.IngressRouteTCP{}, &traefikV1alpha1.IngressRouteTCPList{})
uc.scheme.AddKnownTypes(ingressrouteUDPGVR.GroupVersion(), &traefikV1alpha1.IngressRouteUDP{}, &traefikV1alpha1.IngressRouteUDP{})
if err := scheme.AddToScheme(uc.scheme); err != nil {
return nil, err
}
return uc, nil
}