mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 09:06:58 +02:00
Added traefik source
This commit is contained in:
parent
6e0b8b311b
commit
458b702762
98
docs/tutorials/traefik-proxy.md
Normal file
98
docs/tutorials/traefik-proxy.md
Normal 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
4
go.mod
@ -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
6
go.sum
@ -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=
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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
547
source/traefik_proxy.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user