Merge remote-tracking branch 'origin/master' into ingress-class-filtering

This commit is contained in:
Dave Salisbury 2021-10-25 11:18:56 +11:00
commit b8cbd4bbf4
26 changed files with 1311 additions and 1155 deletions

View File

@ -104,7 +104,7 @@ build.push/multiarch:
for arch in $(ARCHS); do \ for arch in $(ARCHS); do \
image="$(IMAGE):$(VERSION)-$${arch}" ;\ image="$(IMAGE):$(VERSION)-$${arch}" ;\
# pre-pull due to https://github.com/kubernetes-sigs/cluster-addons/pull/84/files ;\ # pre-pull due to https://github.com/kubernetes-sigs/cluster-addons/pull/84/files ;\
docker pull $${arch}/alpine:3.13 ;\ docker pull $${arch}/alpine:3.14 ;\
docker pull golang:1.16 ;\ docker pull golang:1.16 ;\
DOCKER_BUILDKIT=1 docker build --rm --tag $${image} --build-arg VERSION="$(VERSION)" --build-arg ARCH="$${arch}" . ;\ DOCKER_BUILDKIT=1 docker build --rm --tag $${image} --build-arg VERSION="$(VERSION)" --build-arg ARCH="$${arch}" . ;\
docker push $${image} ;\ docker push $${image} ;\

View File

@ -2,8 +2,8 @@ apiVersion: v2
name: external-dns name: external-dns
description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers. description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
type: application type: application
version: 1.2.0 version: 1.4.0
appVersion: 0.9.0 appVersion: 0.10.1
keywords: keywords:
- kubernetes - kubernetes
- external-dns - external-dns
@ -17,5 +17,5 @@ maintainers:
email: steve.hipwell@gmail.com email: steve.hipwell@gmail.com
annotations: annotations:
artifacthub.io/changes: | artifacthub.io/changes: |
- kind: added - kind: changed
description: Initial official release. description: "Update image to v0.10.1"

View File

@ -59,6 +59,7 @@ spec:
- --source={{ . }} - --source={{ . }}
{{- end }} {{- end }}
- --policy={{ .Values.policy }} - --policy={{ .Values.policy }}
- --registry={{ .Values.registry }}
{{- if eq .Values.registry "txt" }} {{- if eq .Values.registry "txt" }}
{{- if .Values.txtOwnerId }} {{- if .Values.txtOwnerId }}
- --txt-owner-id={{ .Values.txtOwnerId }} - --txt-owner-id={{ .Values.txtOwnerId }}

View File

@ -268,6 +268,11 @@ Note: the `--ingress-class` argument cannot be used at the same time as a `kuber
Beware when using multiple sources, e.g. `--source=service --source=ingress`, `--annotation-filter` will filter every given source objects. Beware when using multiple sources, e.g. `--source=service --source=ingress`, `--annotation-filter` will filter every given source objects.
If you need to filter only one specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`. If you need to filter only one specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`.
**Note:** Filtering based on annotation means that the external-dns controller will receive all resources of that kind and then filter on the client-side.
In larger clusters with many resources which change frequently this can cause performance issues. If only some resources need to be managed by an instance
of external-dns then label filtering can be used instead of annotation filtering. This means that only those resources which match the selector specified
in `--label-filter` will be passed to the controller.
### How do I specify that I want the DNS record to point to either the Node's public or private IP when it has both? ### How do I specify that I want the DNS record to point to either the Node's public or private IP when it has both?
If your Nodes have both public and private IP addresses, you might want to write DNS records with one or the other. If your Nodes have both public and private IP addresses, you might want to write DNS records with one or the other.

View File

@ -78,6 +78,7 @@ spec:
- --infoblox-wapi-port=443 # (optional) Infoblox WAPI port. The default is "443". - --infoblox-wapi-port=443 # (optional) Infoblox WAPI port. The default is "443".
- --infoblox-wapi-version=2.3.1 # (optional) Infoblox WAPI version. The default is "2.3.1" - --infoblox-wapi-version=2.3.1 # (optional) Infoblox WAPI version. The default is "2.3.1"
- --infoblox-ssl-verify # (optional) Use --no-infoblox-ssl-verify to skip server certificate verification. - --infoblox-ssl-verify # (optional) Use --no-infoblox-ssl-verify to skip server certificate verification.
- --infoblox-create-ptr # (optional) Use --infoblox-create-ptr to create a ptr entry in addition to an entry.
env: env:
- name: EXTERNAL_DNS_INFOBLOX_HTTP_POOL_CONNECTIONS - name: EXTERNAL_DNS_INFOBLOX_HTTP_POOL_CONNECTIONS
value: "10" # (optional) Infoblox WAPI request connection pool size. The default is "10". value: "10" # (optional) Infoblox WAPI request connection pool size. The default is "10".
@ -158,6 +159,7 @@ spec:
- --infoblox-wapi-port=443 # (optional) Infoblox WAPI port. The default is "443". - --infoblox-wapi-port=443 # (optional) Infoblox WAPI port. The default is "443".
- --infoblox-wapi-version=2.3.1 # (optional) Infoblox WAPI version. The default is "2.3.1" - --infoblox-wapi-version=2.3.1 # (optional) Infoblox WAPI version. The default is "2.3.1"
- --infoblox-ssl-verify # (optional) Use --no-infoblox-ssl-verify to skip server certificate verification. - --infoblox-ssl-verify # (optional) Use --no-infoblox-ssl-verify to skip server certificate verification.
- --infoblox-create-ptr # (optional) Use --infoblox-create-ptr to create a ptr entry in addition to an entry.
env: env:
- name: EXTERNAL_DNS_INFOBLOX_HTTP_POOL_CONNECTIONS - name: EXTERNAL_DNS_INFOBLOX_HTTP_POOL_CONNECTIONS
value: "10" # (optional) Infoblox WAPI request connection pool size. The default is "10". value: "10" # (optional) Infoblox WAPI request connection pool size. The default is "10".
@ -268,3 +270,11 @@ There is also the ability to filter results from the Infoblox zone_auth service
``` ```
--infoblox-fqdn-regex=^staging.*test.com$ --infoblox-fqdn-regex=^staging.*test.com$
``` ```
## Infoblox PTR record support
There is an option to enable PTR records support for infoblox provider. PTR records allow to do reverse dns search. To enable PTR records support, add following into arguments for external-dns:
`--infoblox-create-ptr` to allow management of PTR records.
You can also add a filter for reverse dns zone to limit PTR records to specific zones only:
`--domain-filter=10.196.0.0/16` change this to the reverse zone(s) as defined in your infoblox.
Now external-dns will manage PTR records for you.

View File

@ -35,6 +35,8 @@ const (
RecordTypeSRV = "SRV" RecordTypeSRV = "SRV"
// RecordTypeNS is a RecordType enum value // RecordTypeNS is a RecordType enum value
RecordTypeNS = "NS" RecordTypeNS = "NS"
// RecordTypePTR is a RecordType enum value
RecordTypePTR = "PTR"
) )
// TTL is a structure defining the TTL of a DNS record // TTL is a structure defining the TTL of a DNS record

9
go.mod
View File

@ -6,10 +6,11 @@ require (
cloud.google.com/go v0.97.0 cloud.google.com/go v0.97.0
git.blindage.org/21h/hcloud-dns v0.0.0-20200807003420-f768ffe03f8d git.blindage.org/21h/hcloud-dns v0.0.0-20200807003420-f768ffe03f8d
github.com/Azure/azure-sdk-for-go v46.4.0+incompatible github.com/Azure/azure-sdk-for-go v46.4.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.20 github.com/Azure/go-autorest/autorest v0.11.21
github.com/Azure/go-autorest/autorest/adal v0.9.16 github.com/Azure/go-autorest/autorest/adal v0.9.16
github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/Azure/go-autorest/autorest/to v0.4.0
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1
github.com/StackExchange/dnscontrol v0.2.8
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
github.com/alecthomas/colour v0.1.0 // indirect github.com/alecthomas/colour v0.1.0 // indirect
github.com/alecthomas/kingpin v2.2.5+incompatible github.com/alecthomas/kingpin v2.2.5+incompatible
@ -21,7 +22,7 @@ require (
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
github.com/datawire/ambassador v1.6.0 github.com/datawire/ambassador v1.6.0
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba
github.com/digitalocean/godo v1.44.0 github.com/digitalocean/godo v1.69.1
github.com/dnsimple/dnsimple-go v0.60.0 github.com/dnsimple/dnsimple-go v0.60.0
github.com/exoscale/egoscale v0.73.2 github.com/exoscale/egoscale v0.73.2
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
@ -57,7 +58,7 @@ require (
github.com/transip/gotransip/v6 v6.6.2 github.com/transip/gotransip/v6 v6.6.2
github.com/ultradns/ultradns-sdk-go v0.0.0-20200616202852-e62052662f60 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.0.0-20200211145900-fe8a3d82e556
github.com/vultr/govultr/v2 v2.8.1 github.com/vultr/govultr/v2 v2.9.0
go.etcd.io/etcd/api/v3 v3.5.0 go.etcd.io/etcd/api/v3 v3.5.0
go.etcd.io/etcd/client/v3 v3.5.0 go.etcd.io/etcd/client/v3 v3.5.0
go.uber.org/ratelimit v0.2.0 go.uber.org/ratelimit v0.2.0
@ -80,3 +81,5 @@ require (
k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e // indirect k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e // indirect
sigs.k8s.io/yaml v1.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect
) )
replace k8s.io/klog/v2 => github.com/Raffo/knolog v0.0.0-20211016155154-e4d5e0cc970a

20
go.sum
View File

@ -66,14 +66,15 @@ github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKn
github.com/Azure/go-autorest/autorest v0.11.6/go.mod h1:V6p3pKZx1KKkJubbxnDWrzNhEIfOy/pTGasLqzHIPHs= github.com/Azure/go-autorest/autorest v0.11.6/go.mod h1:V6p3pKZx1KKkJubbxnDWrzNhEIfOy/pTGasLqzHIPHs=
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest v0.11.20 h1:s8H1PbCZSqg/DH7JMlOz6YMig6htWLNPsjDdlLqCx3M= github.com/Azure/go-autorest/autorest v0.11.21 h1:w77zY/9RnUAWcIQyDC0Fc89mCvwftR8F+zsR/OH6enk=
github.com/Azure/go-autorest/autorest v0.11.20/go.mod h1:o3tqFY+QR40VOlk+pV4d77mORO64jOXSgEnPQgLK6JY= github.com/Azure/go-autorest/autorest v0.11.21/go.mod h1:Do/yuMSW/13ayUkcVREpsMHGG+MvV81uzSCFgYPj4tM=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/adal v0.9.4/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE= github.com/Azure/go-autorest/autorest/adal v0.9.4/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.16 h1:P8An8Z9rH1ldbOLdFpxYorgOt2sywL9V24dAwWHPuGc= github.com/Azure/go-autorest/autorest/adal v0.9.16 h1:P8An8Z9rH1ldbOLdFpxYorgOt2sywL9V24dAwWHPuGc=
github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A= github.com/Azure/go-autorest/autorest/adal v0.9.16/go.mod h1:tGMin8I49Yij6AQ+rvV+Xa/zwxYQB5hmsd6DkfAx2+A=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
@ -120,10 +121,14 @@ github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Raffo/knolog v0.0.0-20211016155154-e4d5e0cc970a h1:4D87FDUGSrfLp/NJmYxybiC8+OR8s5eULyNgmmxAI9U=
github.com/Raffo/knolog v0.0.0-20211016155154-e4d5e0cc970a/go.mod h1:AsBYmtPY5rgsIyjQZDLO+Dwdx91Jmi6uxu7LO6Xf9iw=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/Venafi/vcert/v4 v4.13.1/go.mod h1:Z3sJFoAurFNXPpoSUSHq46aIeHLiGQEMDhprfxlpofQ= github.com/Venafi/vcert/v4 v4.13.1/go.mod h1:Z3sJFoAurFNXPpoSUSHq46aIeHLiGQEMDhprfxlpofQ=
github.com/StackExchange/dnscontrol v0.2.8 h1:7jviqDH9cIqRSRpH0UxgmpT7a8CwEhs9mLHBhoYhXo8=
github.com/StackExchange/dnscontrol v0.2.8/go.mod h1:BH+5nX50JxHDdb3+AD/z/UfYMCc7iaqEkRtQ+NjcFGE=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
@ -273,8 +278,9 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/digitalocean/godo v1.44.0 h1:IMElzMUpO1dVR8qjSg53+5vDkOLzMbhJt4yTAq7NGCQ=
github.com/digitalocean/godo v1.44.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= github.com/digitalocean/godo v1.44.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
github.com/digitalocean/godo v1.69.1 h1:aCyfwth8R3DeOaWB9J9E8v7cjlDIlF19eXTt8R3XhTE=
github.com/digitalocean/godo v1.69.1/go.mod h1:epPuOzTOOJujNo0nduDj2D5O1zu8cSpp9R+DdN0W9I0=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v0.60.0 h1:N+q+ML1CZGf+5r4udu9Opy7WJNtOaFT9aM86Af9gLhk= github.com/dnsimple/dnsimple-go v0.60.0 h1:N+q+ML1CZGf+5r4udu9Opy7WJNtOaFT9aM86Af9gLhk=
@ -726,6 +732,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d h1:JV46OtdhH2vVt8mJ1EWUE94k99vbN9fZs1WQ8kcEapU=
github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d/go.mod h1:CHQ3o5KBH1PIS2Fb1mRLTIWO5YzP9kSUB3KoCICwlvA=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
@ -788,6 +796,8 @@ github.com/miekg/dns v1.1.6/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nr
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1 h1:kZZmnTeY2r+88mDNCVV/uCXL2gG3rkVPTN9jcYfGQcI= github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1 h1:kZZmnTeY2r+88mDNCVV/uCXL2gG3rkVPTN9jcYfGQcI=
github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mikkeloscar/knolog v0.0.0-20190326191552-80742771eb6b h1:5f5B1kp+QerGOF91q1qVJcUWWvXsVEN3OKiyEzAAjIM=
github.com/mikkeloscar/knolog v0.0.0-20190326191552-80742771eb6b/go.mod h1:PizLs/1ddmVrXpFgWOGNmTJ2YHSWUkpUXMYuUkTo3Go=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@ -1080,8 +1090,8 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/vinyldns/go-vinyldns v0.0.0-20200211145900-fe8a3d82e556 h1:UbVjBjgJUYGD8MlobEdOR+yTeNqaNa2Gf1/nskVNCSE= 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.0.0-20200211145900-fe8a3d82e556/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg=
github.com/vultr/govultr/v2 v2.8.1 h1:AjRcJWfTfb4DidRNCeojUIgLVC5XShuc5IAW99K3wHU= github.com/vultr/govultr/v2 v2.9.0 h1:n0a0fGOiHAE07twu1VR3jWTDFDE0+DJ/cIqZqX9IlNw=
github.com/vultr/govultr/v2 v2.8.1/go.mod h1:JjUljQdSZx+MELCAJvZ/JH32bJotmflnsyS0NOjb8Jg= github.com/vultr/govultr/v2 v2.9.0/go.mod h1:JjUljQdSZx+MELCAJvZ/JH32bJotmflnsyS0NOjb8Jg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=

View File

@ -9,6 +9,9 @@ rules:
- apiGroups: ['extensions'] - apiGroups: ['extensions']
resources: ['ingresses'] resources: ['ingresses']
verbs: ['get', 'watch', 'list'] verbs: ['get', 'watch', 'list']
- apiGroups: [''] - apiGroups: ["networking.k8s.io"]
resources: ['nodes'] resources: ["ingresses"]
verbs: ['list'] verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["watch", "list"]

View File

@ -3,7 +3,7 @@ kind: Kustomization
images: images:
- name: k8s.gcr.io/external-dns/external-dns - name: k8s.gcr.io/external-dns/external-dns
newTag: v0.9.0 newTag: v0.10.0
resources: resources:
- ./external-dns-deployment.yaml - ./external-dns-deployment.yaml

View File

@ -26,6 +26,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/labels"
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
"sigs.k8s.io/external-dns/controller" "sigs.k8s.io/external-dns/controller"
@ -99,11 +100,14 @@ func main() {
go serveMetrics(cfg.MetricsAddress) go serveMetrics(cfg.MetricsAddress)
go handleSigterm(cancel) go handleSigterm(cancel)
// error is explicitly ignored because the filter is already validated in validation.ValidateConfig
labelSelector, _ := labels.Parse(cfg.LabelFilter)
// Create a source.Config from the flags passed by the user. // Create a source.Config from the flags passed by the user.
sourceCfg := &source.Config{ sourceCfg := &source.Config{
Namespace: cfg.Namespace, Namespace: cfg.Namespace,
AnnotationFilter: cfg.AnnotationFilter, AnnotationFilter: cfg.AnnotationFilter,
LabelFilter: cfg.LabelFilter, LabelFilter: labelSelector,
IngressClassNames: cfg.IngressClassNames, IngressClassNames: cfg.IngressClassNames,
FQDNTemplate: cfg.FQDNTemplate, FQDNTemplate: cfg.FQDNTemplate,
CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation, CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation,
@ -245,6 +249,7 @@ func main() {
MaxResults: cfg.InfobloxMaxResults, MaxResults: cfg.InfobloxMaxResults,
DryRun: cfg.DryRun, DryRun: cfg.DryRun,
FQDNRexEx: cfg.InfobloxFQDNRegEx, FQDNRexEx: cfg.InfobloxFQDNRegEx,
CreatePTR: cfg.InfobloxCreatePTR,
}, },
) )
case "dyn": case "dyn":

View File

@ -23,6 +23,8 @@ import (
"strconv" "strconv"
"time" "time"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
"github.com/alecthomas/kingpin" "github.com/alecthomas/kingpin"
@ -110,6 +112,7 @@ type Config struct {
InfobloxView string InfobloxView string
InfobloxMaxResults int InfobloxMaxResults int
InfobloxFQDNRegEx string InfobloxFQDNRegEx string
InfobloxCreatePTR bool
DynCustomerName string DynCustomerName string
DynUsername string DynUsername string
DynPassword string `secure:"yes"` DynPassword string `secure:"yes"`
@ -186,7 +189,7 @@ var defaultConfig = &Config{
Sources: nil, Sources: nil,
Namespace: "", Namespace: "",
AnnotationFilter: "", AnnotationFilter: "",
LabelFilter: "", LabelFilter: labels.Everything().String(),
IngressClassNames: nil, IngressClassNames: nil,
FQDNTemplate: "", FQDNTemplate: "",
CombineFQDNAndAnnotation: false, CombineFQDNAndAnnotation: false,
@ -239,6 +242,7 @@ var defaultConfig = &Config{
InfobloxView: "", InfobloxView: "",
InfobloxMaxResults: 0, InfobloxMaxResults: 0,
InfobloxFQDNRegEx: "", InfobloxFQDNRegEx: "",
InfobloxCreatePTR: false,
OCIConfigFile: "/etc/kubernetes/oci.yaml", OCIConfigFile: "/etc/kubernetes/oci.yaml",
InMemoryZones: []string{}, InMemoryZones: []string{},
OVHEndpoint: "ovh-eu", OVHEndpoint: "ovh-eu",
@ -363,7 +367,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress") app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress")
app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace) 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) 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)
app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter) app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently supported by source types CRD, ingress, service and openshift-route").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
app.Flag("ingress-class", "Require an ingress to have this class name (defaults to any class; specify multiple times to allow more than one class)").StringsVar(&cfg.IngressClassNames) app.Flag("ingress-class", "Require an ingress to have this class name (defaults to any class; specify multiple times to allow more than one class)").StringsVar(&cfg.IngressClassNames)
app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN.").Default(defaultConfig.FQDNTemplate).StringVar(&cfg.FQDNTemplate) app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN.").Default(defaultConfig.FQDNTemplate).StringVar(&cfg.FQDNTemplate)
app.Flag("combine-fqdn-annotation", "Combine FQDN template and Annotations instead of overwriting").BoolVar(&cfg.CombineFQDNAndAnnotation) app.Flag("combine-fqdn-annotation", "Combine FQDN template and Annotations instead of overwriting").BoolVar(&cfg.CombineFQDNAndAnnotation)
@ -427,6 +431,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("infoblox-view", "DNS view (default: \"\")").Default(defaultConfig.InfobloxView).StringVar(&cfg.InfobloxView) app.Flag("infoblox-view", "DNS view (default: \"\")").Default(defaultConfig.InfobloxView).StringVar(&cfg.InfobloxView)
app.Flag("infoblox-max-results", "Add _max_results as query parameter to the URL on all API requests. The default is 0 which means _max_results is not set and the default of the server is used.").Default(strconv.Itoa(defaultConfig.InfobloxMaxResults)).IntVar(&cfg.InfobloxMaxResults) app.Flag("infoblox-max-results", "Add _max_results as query parameter to the URL on all API requests. The default is 0 which means _max_results is not set and the default of the server is used.").Default(strconv.Itoa(defaultConfig.InfobloxMaxResults)).IntVar(&cfg.InfobloxMaxResults)
app.Flag("infoblox-fqdn-regex", "Apply this regular expression as a filter for obtaining zone_auth objects. This is disabled by default.").Default(defaultConfig.InfobloxFQDNRegEx).StringVar(&cfg.InfobloxFQDNRegEx) app.Flag("infoblox-fqdn-regex", "Apply this regular expression as a filter for obtaining zone_auth objects. This is disabled by default.").Default(defaultConfig.InfobloxFQDNRegEx).StringVar(&cfg.InfobloxFQDNRegEx)
app.Flag("infoblox-create-ptr", "When using the Infoblox provider, create a ptr entry in addition to an entry").Default(strconv.FormatBool(defaultConfig.InfobloxCreatePTR)).BoolVar(&cfg.InfobloxCreatePTR)
app.Flag("dyn-customer-name", "When using the Dyn provider, specify the Customer Name").Default("").StringVar(&cfg.DynCustomerName) app.Flag("dyn-customer-name", "When using the Dyn provider, specify the Customer Name").Default("").StringVar(&cfg.DynCustomerName)
app.Flag("dyn-username", "When using the Dyn provider, specify the Username").Default("").StringVar(&cfg.DynUsername) app.Flag("dyn-username", "When using the Dyn provider, specify the Username").Default("").StringVar(&cfg.DynUsername)
app.Flag("dyn-password", "When using the Dyn provider, specify the password").Default("").StringVar(&cfg.DynPassword) app.Flag("dyn-password", "When using the Dyn provider, specify the password").Default("").StringVar(&cfg.DynPassword)

View File

@ -20,6 +20,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/external-dns/pkg/apis/externaldns" "sigs.k8s.io/external-dns/pkg/apis/externaldns"
) )
@ -110,5 +112,9 @@ func ValidateConfig(cfg *externaldns.Config) error {
return errors.New("txt-prefix and txt-suffix are mutual exclusive") return errors.New("txt-prefix and txt-suffix are mutual exclusive")
} }
_, err := labels.Parse(cfg.LabelFilter)
if err != nil {
return errors.New("--label-filter does not specify a valid label selector")
}
return nil return nil
} }

View File

@ -308,7 +308,7 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
} }
err := p.Client.UpdateDNSRecord(zoneID, recordID, change.ResourceRecord) err := p.Client.UpdateDNSRecord(zoneID, recordID, change.ResourceRecord)
if err != nil { if err != nil {
log.WithFields(logFields).Errorf("failed to delete record: %v", err) log.WithFields(logFields).Errorf("failed to update record: %v", err)
} }
} else if change.Action == cloudFlareDelete { } else if change.Action == cloudFlareDelete {
recordID := p.getRecordID(records, change.ResourceRecord) recordID := p.getRecordID(records, change.ResourceRecord)

View File

@ -145,10 +145,11 @@ func NewGoogleProvider(ctx context.Context, project string, domainFilter endpoin
if project == "" { if project == "" {
mProject, mErr := metadata.ProjectID() mProject, mErr := metadata.ProjectID()
if mErr == nil { if mErr != nil {
log.Infof("Google project auto-detected: %s", mProject) return nil, fmt.Errorf("failed to auto-detect the project id: %w", mErr)
project = mProject
} }
log.Infof("Google project auto-detected: %s", mProject)
project = mProject
} }
zoneTypeFilter := provider.NewZoneTypeFilter(zoneVisibility) zoneTypeFilter := provider.NewZoneTypeFilter(zoneVisibility)

View File

@ -19,12 +19,14 @@ package infoblox
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"net/http" "net/http"
"os" "os"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
transform "github.com/StackExchange/dnscontrol/pkg/transform"
ibclient "github.com/infobloxopen/infoblox-go-client" ibclient "github.com/infobloxopen/infoblox-go-client"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -33,6 +35,11 @@ import (
"sigs.k8s.io/external-dns/provider" "sigs.k8s.io/external-dns/provider"
) )
const (
// provider specific key to track if PTR record was already created or not for A records
providerSpecificInfobloxPtrRecord = "infoblox-ptr-record-exists"
)
// InfobloxConfig clarifies the method signature // InfobloxConfig clarifies the method signature
type InfobloxConfig struct { type InfobloxConfig struct {
DomainFilter endpoint.DomainFilter DomainFilter endpoint.DomainFilter
@ -47,6 +54,7 @@ type InfobloxConfig struct {
View string View string
MaxResults int MaxResults int
FQDNRexEx string FQDNRexEx string
CreatePTR bool
} }
// InfobloxProvider implements the DNS provider for Infoblox. // InfobloxProvider implements the DNS provider for Infoblox.
@ -58,6 +66,7 @@ type InfobloxProvider struct {
view string view string
dryRun bool dryRun bool
fqdnRegEx string fqdnRegEx string
createPTR bool
} }
type infobloxRecordSet struct { type infobloxRecordSet struct {
@ -143,6 +152,7 @@ func NewInfobloxProvider(infobloxConfig InfobloxConfig) (*InfobloxProvider, erro
dryRun: infobloxConfig.DryRun, dryRun: infobloxConfig.DryRun,
view: infobloxConfig.View, view: infobloxConfig.View,
fqdnRegEx: infobloxConfig.FQDNRexEx, fqdnRegEx: infobloxConfig.FQDNRexEx,
createPTR: infobloxConfig.CreatePTR,
} }
return provider, nil return provider, nil
@ -170,6 +180,9 @@ func (p *InfobloxProvider) Records(ctx context.Context) (endpoints []*endpoint.E
} }
for _, res := range resA { for _, res := range resA {
newEndpoint := endpoint.NewEndpoint(res.Name, endpoint.RecordTypeA, res.Ipv4Addr) newEndpoint := endpoint.NewEndpoint(res.Name, endpoint.RecordTypeA, res.Ipv4Addr)
if p.createPTR {
newEndpoint.WithProviderSpecific(providerSpecificInfobloxPtrRecord, "false")
}
// Check if endpoint already exists and add to existing endpoint if it does // Check if endpoint already exists and add to existing endpoint if it does
foundExisting := false foundExisting := false
for _, ep := range endpoints { for _, ep := range endpoints {
@ -203,7 +216,13 @@ func (p *InfobloxProvider) Records(ctx context.Context) (endpoints []*endpoint.E
} }
for _, res := range resH { for _, res := range resH {
for _, ip := range res.Ipv4Addrs { for _, ip := range res.Ipv4Addrs {
endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeA, ip.Ipv4Addr)) // host record is an abstraction in infoblox that combines A and PTR records
// for any host record we already should have a PTR record in infoblox, so mark it as created
newEndpoint := endpoint.NewEndpoint(res.Name, endpoint.RecordTypeA, ip.Ipv4Addr)
if p.createPTR {
newEndpoint.WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true")
}
endpoints = append(endpoints, newEndpoint)
} }
} }
@ -222,6 +241,29 @@ func (p *InfobloxProvider) Records(ctx context.Context) (endpoints []*endpoint.E
endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeCNAME, res.Canonical)) endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeCNAME, res.Canonical))
} }
if p.createPTR {
// infoblox doesn't accept reverse zone's fqdn, and instead expects .in-addr.arpa zone
// so convert our zone fqdn (if it is a correct cidr block) into in-addr.arpa address and pass that into infoblox
// example: 10.196.38.0/24 becomes 38.196.10.in-addr.arpa
arpaZone, err := transform.ReverseDomainName(zone.Fqdn)
if err == nil {
var resP []ibclient.RecordPTR
objP := ibclient.NewRecordPTR(
ibclient.RecordPTR{
Zone: arpaZone,
View: p.view,
},
)
err = p.client.GetObject(objP, "", &resP)
if err != nil {
return nil, fmt.Errorf("could not fetch PTR records from zone '%s': %s", zone.Fqdn, err)
}
for _, res := range resP {
endpoints = append(endpoints, endpoint.NewEndpoint(res.PtrdName, endpoint.RecordTypePTR, res.Ipv4Addr))
}
}
}
var resT []ibclient.RecordTXT var resT []ibclient.RecordTXT
objT := ibclient.NewRecordTXT( objT := ibclient.NewRecordTXT(
ibclient.RecordTXT{ ibclient.RecordTXT{
@ -242,10 +284,66 @@ func (p *InfobloxProvider) Records(ctx context.Context) (endpoints []*endpoint.E
endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeTXT, res.Text)) endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeTXT, res.Text))
} }
} }
// update A records that have PTR record created for them already
if p.createPTR {
// save all ptr records into map for a quick look up
ptrRecordsMap := make(map[string]bool)
for _, ptrRecord := range endpoints {
if ptrRecord.RecordType != endpoint.RecordTypePTR {
continue
}
ptrRecordsMap[ptrRecord.DNSName] = true
}
for i := range endpoints {
if endpoints[i].RecordType != endpoint.RecordTypeA {
continue
}
// if PTR record already exists for A record, then mark it as such
if ptrRecordsMap[endpoints[i].DNSName] {
found := false
for j := range endpoints[i].ProviderSpecific {
if endpoints[i].ProviderSpecific[j].Name == providerSpecificInfobloxPtrRecord {
endpoints[i].ProviderSpecific[j].Value = "true"
found = true
}
}
if !found {
endpoints[i].WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true")
}
}
}
}
logrus.Debugf("fetched %d records from infoblox", len(endpoints)) logrus.Debugf("fetched %d records from infoblox", len(endpoints))
return endpoints, nil return endpoints, nil
} }
func (p *InfobloxProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) []*endpoint.Endpoint {
if !p.createPTR {
return endpoints
}
// for all A records, we want to create PTR records
// so add provider specific property to track if the record was created or not
for i := range endpoints {
if endpoints[i].RecordType == endpoint.RecordTypeA {
found := false
for j := range endpoints[i].ProviderSpecific {
if endpoints[i].ProviderSpecific[j].Name == providerSpecificInfobloxPtrRecord {
endpoints[i].ProviderSpecific[j].Value = "true"
found = true
}
}
if !found {
endpoints[i].WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true")
}
}
}
return endpoints
}
// ApplyChanges applies the given changes. // ApplyChanges applies the given changes.
func (p *InfobloxProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { func (p *InfobloxProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
zones, err := p.zones() zones, err := p.zones()
@ -301,6 +399,17 @@ func (p *InfobloxProvider) mapChanges(zones []ibclient.ZoneAuth, changes *plan.C
} }
// Ensure the record type is suitable // Ensure the record type is suitable
changeMap[zone.Fqdn] = append(changeMap[zone.Fqdn], change) changeMap[zone.Fqdn] = append(changeMap[zone.Fqdn], change)
if p.createPTR && change.RecordType == endpoint.RecordTypeA {
reverseZone := p.findReverseZone(zones, change.Targets[0])
if reverseZone == nil {
logrus.Debugf("Ignoring changes to '%s' because a suitable Infoblox DNS reverse zone was not found.", change.Targets[0])
return
}
changecopy := *change
changecopy.RecordType = endpoint.RecordTypePTR
changeMap[reverseZone.Fqdn] = append(changeMap[reverseZone.Fqdn], &changecopy)
}
} }
for _, change := range changes.Delete { for _, change := range changes.Delete {
@ -338,6 +447,28 @@ func (p *InfobloxProvider) findZone(zones []ibclient.ZoneAuth, name string) *ibc
return result return result
} }
func (p *InfobloxProvider) findReverseZone(zones []ibclient.ZoneAuth, name string) *ibclient.ZoneAuth {
ip := net.ParseIP(name)
networks := map[int]*ibclient.ZoneAuth{}
maxMask := 0
for i, zone := range zones {
_, net, err := net.ParseCIDR(zone.Fqdn)
if err != nil {
logrus.WithError(err).Debugf("fqdn %s is no cidr", zone.Fqdn)
} else {
if net.Contains(ip) {
_, mask := net.Mask.Size()
networks[mask] = &zones[i]
if mask > maxMask {
maxMask = mask
}
}
}
}
return networks[maxMask]
}
func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool, targetIndex int) (recordSet infobloxRecordSet, err error) { func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool, targetIndex int) (recordSet infobloxRecordSet, err error) {
switch ep.RecordType { switch ep.RecordType {
case endpoint.RecordTypeA: case endpoint.RecordTypeA:
@ -359,6 +490,25 @@ func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool, targ
obj: obj, obj: obj,
res: &res, res: &res,
} }
case endpoint.RecordTypePTR:
var res []ibclient.RecordPTR
obj := ibclient.NewRecordPTR(
ibclient.RecordPTR{
PtrdName: ep.DNSName,
Ipv4Addr: ep.Targets[targetIndex],
View: p.view,
},
)
if getObject {
err = p.client.GetObject(obj, "", &res)
if err != nil {
return
}
}
recordSet = infobloxRecordSet{
obj: obj,
res: &res,
}
case endpoint.RecordTypeCNAME: case endpoint.RecordTypeCNAME:
var res []ibclient.RecordCNAME var res []ibclient.RecordCNAME
obj := ibclient.NewRecordCNAME( obj := ibclient.NewRecordCNAME(
@ -483,6 +633,10 @@ func (p *InfobloxProvider) deleteRecords(deleted infobloxChangeMap) {
for _, record := range *recordSet.res.(*[]ibclient.RecordA) { for _, record := range *recordSet.res.(*[]ibclient.RecordA) {
_, err = p.client.DeleteObject(record.Ref) _, err = p.client.DeleteObject(record.Ref)
} }
case endpoint.RecordTypePTR:
for _, record := range *recordSet.res.(*[]ibclient.RecordPTR) {
_, err = p.client.DeleteObject(record.Ref)
}
case endpoint.RecordTypeCNAME: case endpoint.RecordTypeCNAME:
for _, record := range *recordSet.res.(*[]ibclient.RecordCNAME) { for _, record := range *recordSet.res.(*[]ibclient.RecordCNAME) {
_, err = p.client.DeleteObject(record.Ref) _, err = p.client.DeleteObject(record.Ref)

View File

@ -25,6 +25,7 @@ import (
"testing" "testing"
ibclient "github.com/infobloxopen/infoblox-go-client" ibclient "github.com/infobloxopen/infoblox-go-client"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
@ -89,6 +90,21 @@ func (client *mockIBConnector) CreateObject(obj ibclient.IBObject) (ref string,
) )
obj.(*ibclient.RecordTXT).Ref = ref obj.(*ibclient.RecordTXT).Ref = ref
ref = fmt.Sprintf("%s/%s:%s/default", obj.ObjectType(), base64.StdEncoding.EncodeToString([]byte(obj.(*ibclient.RecordTXT).Name)), obj.(*ibclient.RecordTXT).Name) ref = fmt.Sprintf("%s/%s:%s/default", obj.ObjectType(), base64.StdEncoding.EncodeToString([]byte(obj.(*ibclient.RecordTXT).Name)), obj.(*ibclient.RecordTXT).Name)
case "record:ptr":
client.createdEndpoints = append(
client.createdEndpoints,
endpoint.NewEndpoint(
obj.(*ibclient.RecordPTR).PtrdName,
endpoint.RecordTypePTR,
obj.(*ibclient.RecordPTR).Ipv4Addr,
),
)
obj.(*ibclient.RecordPTR).Ref = ref
reverseAddr, err := dns.ReverseAddr(obj.(*ibclient.RecordPTR).Ipv4Addr)
if err != nil {
return ref, fmt.Errorf("unable to create reverse addr from %s", obj.(*ibclient.RecordPTR).Ipv4Addr)
}
ref = fmt.Sprintf("%s/%s:%s/default", obj.ObjectType(), base64.StdEncoding.EncodeToString([]byte(obj.(*ibclient.RecordPTR).PtrdName)), reverseAddr)
} }
*client.mockInfobloxObjects = append( *client.mockInfobloxObjects = append(
*client.mockInfobloxObjects, *client.mockInfobloxObjects,
@ -163,6 +179,22 @@ func (client *mockIBConnector) GetObject(obj ibclient.IBObject, ref string, res
} }
} }
*res.(*[]ibclient.RecordTXT) = result *res.(*[]ibclient.RecordTXT) = result
case "record:ptr":
var result []ibclient.RecordPTR
for _, object := range *client.mockInfobloxObjects {
if object.ObjectType() == "record:ptr" {
if ref != "" &&
ref != object.(*ibclient.RecordPTR).Ref {
continue
}
if obj.(*ibclient.RecordPTR).PtrdName != "" &&
obj.(*ibclient.RecordPTR).PtrdName != object.(*ibclient.RecordPTR).PtrdName {
continue
}
result = append(result, *object.(*ibclient.RecordPTR))
}
}
*res.(*[]ibclient.RecordPTR) = result
case "zone_auth": case "zone_auth":
*res.(*[]ibclient.ZoneAuth) = *client.mockInfobloxZones *res.(*[]ibclient.ZoneAuth) = *client.mockInfobloxZones
} }
@ -246,6 +278,24 @@ func (client *mockIBConnector) DeleteObject(ref string) (refRes string, err erro
), ),
) )
} }
case "record:ptr":
var records []ibclient.RecordPTR
obj := ibclient.NewRecordPTR(
ibclient.RecordPTR{
Name: result[2],
},
)
client.GetObject(obj, ref, &records)
for _, record := range records {
client.deletedEndpoints = append(
client.deletedEndpoints,
endpoint.NewEndpoint(
record.PtrdName,
endpoint.RecordTypePTR,
"",
),
)
}
} }
return "", nil return "", nil
} }
@ -339,16 +389,25 @@ func createMockInfobloxObject(name, recordType, value string) ibclient.IBObject
}, },
}, },
) )
case endpoint.RecordTypePTR:
return ibclient.NewRecordPTR(
ibclient.RecordPTR{
Ref: ref,
PtrdName: name,
Ipv4Addr: value,
},
)
} }
return nil return nil
} }
func newInfobloxProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, client ibclient.IBConnector) *InfobloxProvider { func newInfobloxProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, createPTR bool, client ibclient.IBConnector) *InfobloxProvider {
return &InfobloxProvider{ return &InfobloxProvider{
client: client, client: client,
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter, zoneIDFilter: zoneIDFilter,
dryRun: dryRun, dryRun: dryRun,
createPTR: createPTR,
} }
} }
@ -376,7 +435,7 @@ func TestInfobloxRecords(t *testing.T) {
}, },
} }
provider := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, &client) provider := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, false, &client)
actual, err := provider.Records(context.Background()) actual, err := provider.Records(context.Background())
if err != nil { if err != nil {
@ -399,10 +458,66 @@ func TestInfobloxRecords(t *testing.T) {
validateEndpoints(t, actual, expected) validateEndpoints(t, actual, expected)
} }
func TestInfobloxAdjustEndpoints(t *testing.T) {
client := mockIBConnector{
mockInfobloxZones: &[]ibclient.ZoneAuth{
createMockInfobloxZone("example.com"),
createMockInfobloxZone("other.com"),
},
mockInfobloxObjects: &[]ibclient.IBObject{
createMockInfobloxObject("example.com", endpoint.RecordTypeA, "123.123.123.122"),
createMockInfobloxObject("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
createMockInfobloxObject("hack.example.com", endpoint.RecordTypeCNAME, "cerberus.infoblox.com"),
createMockInfobloxObject("host.example.com", "HOST", "125.1.1.1"),
},
}
provider := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, true, &client)
actual, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
provider.AdjustEndpoints(actual)
expected := []*endpoint.Endpoint{
endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122").WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "\"heritage=external-dns,external-dns/owner=default\""),
endpoint.NewEndpoint("hack.example.com", endpoint.RecordTypeCNAME, "cerberus.infoblox.com"),
endpoint.NewEndpoint("host.example.com", endpoint.RecordTypeA, "125.1.1.1").WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true"),
}
validateEndpoints(t, actual, expected)
}
func TestInfobloxRecordsReverse(t *testing.T) {
client := mockIBConnector{
mockInfobloxZones: &[]ibclient.ZoneAuth{
createMockInfobloxZone("10.0.0.0/24"),
createMockInfobloxZone("10.0.1.0/24"),
},
mockInfobloxObjects: &[]ibclient.IBObject{
createMockInfobloxObject("example.com", endpoint.RecordTypePTR, "10.0.0.1"),
createMockInfobloxObject("example2.com", endpoint.RecordTypePTR, "10.0.0.2"),
},
}
provider := newInfobloxProvider(endpoint.NewDomainFilter([]string{"10.0.0.0/24"}), provider.NewZoneIDFilter([]string{""}), true, true, &client)
actual, err := provider.Records(context.Background())
if err != nil {
t.Fatal(err)
}
expected := []*endpoint.Endpoint{
endpoint.NewEndpoint("example.com", endpoint.RecordTypePTR, "10.0.0.1"),
endpoint.NewEndpoint("example2.com", endpoint.RecordTypePTR, "10.0.0.2"),
}
validateEndpoints(t, actual, expected)
}
func TestInfobloxApplyChanges(t *testing.T) { func TestInfobloxApplyChanges(t *testing.T) {
client := mockIBConnector{} client := mockIBConnector{}
testInfobloxApplyChangesInternal(t, false, &client) testInfobloxApplyChangesInternal(t, false, false, &client)
validateEndpoints(t, client.createdEndpoints, []*endpoint.Endpoint{ validateEndpoints(t, client.createdEndpoints, []*endpoint.Endpoint{
endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"), endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"),
@ -423,7 +538,39 @@ func TestInfobloxApplyChanges(t *testing.T) {
endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""), endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""),
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""), endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""),
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""), endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""),
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeTXT, ""), endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""),
})
validateEndpoints(t, client.updatedEndpoints, []*endpoint.Endpoint{})
}
func TestInfobloxApplyChangesReverse(t *testing.T) {
client := mockIBConnector{}
testInfobloxApplyChangesInternal(t, false, true, &client)
validateEndpoints(t, client.createdEndpoints, []*endpoint.Endpoint{
endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypePTR, "1.2.3.4"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypePTR, "1.2.3.4"),
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"),
endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("other.com", endpoint.RecordTypeA, "5.6.7.8"),
endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("new.example.com", endpoint.RecordTypeA, "111.222.111.222"),
endpoint.NewEndpoint("newcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeA, "1.2.3.4,3.4.5.6,8.9.10.11"),
endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeTXT, "tag-multiple-A-records"),
})
validateEndpoints(t, client.deletedEndpoints, []*endpoint.Endpoint{
endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""),
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""),
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""),
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypePTR, ""),
endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""), endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""),
}) })
@ -435,7 +582,7 @@ func TestInfobloxApplyChangesDryRun(t *testing.T) {
mockInfobloxObjects: &[]ibclient.IBObject{}, mockInfobloxObjects: &[]ibclient.IBObject{},
} }
testInfobloxApplyChangesInternal(t, true, &client) testInfobloxApplyChangesInternal(t, true, false, &client)
validateEndpoints(t, client.createdEndpoints, []*endpoint.Endpoint{}) validateEndpoints(t, client.createdEndpoints, []*endpoint.Endpoint{})
@ -444,14 +591,16 @@ func TestInfobloxApplyChangesDryRun(t *testing.T) {
validateEndpoints(t, client.updatedEndpoints, []*endpoint.Endpoint{}) validateEndpoints(t, client.updatedEndpoints, []*endpoint.Endpoint{})
} }
func testInfobloxApplyChangesInternal(t *testing.T, dryRun bool, client ibclient.IBConnector) { func testInfobloxApplyChangesInternal(t *testing.T, dryRun, createPTR bool, client ibclient.IBConnector) {
client.(*mockIBConnector).mockInfobloxZones = &[]ibclient.ZoneAuth{ client.(*mockIBConnector).mockInfobloxZones = &[]ibclient.ZoneAuth{
createMockInfobloxZone("example.com"), createMockInfobloxZone("example.com"),
createMockInfobloxZone("other.com"), createMockInfobloxZone("other.com"),
createMockInfobloxZone("1.2.3.0/24"),
} }
client.(*mockIBConnector).mockInfobloxObjects = &[]ibclient.IBObject{ client.(*mockIBConnector).mockInfobloxObjects = &[]ibclient.IBObject{
createMockInfobloxObject("deleted.example.com", endpoint.RecordTypeA, "121.212.121.212"), createMockInfobloxObject("deleted.example.com", endpoint.RecordTypeA, "121.212.121.212"),
createMockInfobloxObject("deleted.example.com", endpoint.RecordTypeTXT, "test-deleting-txt"), createMockInfobloxObject("deleted.example.com", endpoint.RecordTypeTXT, "test-deleting-txt"),
createMockInfobloxObject("deleted.example.com", endpoint.RecordTypePTR, "121.212.121.212"),
createMockInfobloxObject("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"), createMockInfobloxObject("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
createMockInfobloxObject("old.example.com", endpoint.RecordTypeA, "121.212.121.212"), createMockInfobloxObject("old.example.com", endpoint.RecordTypeA, "121.212.121.212"),
createMockInfobloxObject("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"), createMockInfobloxObject("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
@ -461,6 +610,7 @@ func testInfobloxApplyChangesInternal(t *testing.T, dryRun bool, client ibclient
endpoint.NewDomainFilter([]string{""}), endpoint.NewDomainFilter([]string{""}),
provider.NewZoneIDFilter([]string{""}), provider.NewZoneIDFilter([]string{""}),
dryRun, dryRun,
createPTR,
client, client,
) )
@ -493,11 +643,14 @@ func testInfobloxApplyChangesInternal(t *testing.T, dryRun bool, client ibclient
deleteRecords := []*endpoint.Endpoint{ deleteRecords := []*endpoint.Endpoint{
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, "121.212.121.212"), endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, "121.212.121.212"),
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeTXT, "test-deleting-txt"),
endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"), endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
endpoint.NewEndpoint("deleted.nope.com", endpoint.RecordTypeA, "222.111.222.111"), endpoint.NewEndpoint("deleted.nope.com", endpoint.RecordTypeA, "222.111.222.111"),
} }
if createPTR {
deleteRecords = append(deleteRecords, endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypePTR, "121.212.121.212"))
}
changes := &plan.Changes{ changes := &plan.Changes{
Create: createRecords, Create: createRecords,
UpdateNew: updateNewRecords, UpdateNew: updateNewRecords,
@ -516,11 +669,12 @@ func TestInfobloxZones(t *testing.T) {
createMockInfobloxZone("example.com"), createMockInfobloxZone("example.com"),
createMockInfobloxZone("lvl1-1.example.com"), createMockInfobloxZone("lvl1-1.example.com"),
createMockInfobloxZone("lvl2-1.lvl1-1.example.com"), createMockInfobloxZone("lvl2-1.lvl1-1.example.com"),
createMockInfobloxZone("1.2.3.0/24"),
}, },
mockInfobloxObjects: &[]ibclient.IBObject{}, mockInfobloxObjects: &[]ibclient.IBObject{},
} }
provider := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, &client) provider := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com", "1.2.3.0/24"}), provider.NewZoneIDFilter([]string{""}), true, false, &client)
zones, _ := provider.zones() zones, _ := provider.zones()
var emptyZoneAuth *ibclient.ZoneAuth var emptyZoneAuth *ibclient.ZoneAuth
assert.Equal(t, provider.findZone(zones, "example.com").Fqdn, "example.com") assert.Equal(t, provider.findZone(zones, "example.com").Fqdn, "example.com")
@ -531,6 +685,26 @@ func TestInfobloxZones(t *testing.T) {
assert.Equal(t, provider.findZone(zones, "lvl2-1.lvl1-1.example.com").Fqdn, "lvl2-1.lvl1-1.example.com") assert.Equal(t, provider.findZone(zones, "lvl2-1.lvl1-1.example.com").Fqdn, "lvl2-1.lvl1-1.example.com")
assert.Equal(t, provider.findZone(zones, "lvl2-2.lvl1-1.example.com").Fqdn, "lvl1-1.example.com") assert.Equal(t, provider.findZone(zones, "lvl2-2.lvl1-1.example.com").Fqdn, "lvl1-1.example.com")
assert.Equal(t, provider.findZone(zones, "lvl2-2.lvl1-2.example.com").Fqdn, "example.com") assert.Equal(t, provider.findZone(zones, "lvl2-2.lvl1-2.example.com").Fqdn, "example.com")
assert.Equal(t, provider.findZone(zones, "1.2.3.0/24").Fqdn, "1.2.3.0/24")
}
func TestInfobloxReverseZones(t *testing.T) {
client := mockIBConnector{
mockInfobloxZones: &[]ibclient.ZoneAuth{
createMockInfobloxZone("example.com"),
createMockInfobloxZone("1.2.3.0/24"),
createMockInfobloxZone("10.0.0.0/8"),
},
mockInfobloxObjects: &[]ibclient.IBObject{},
}
provider := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com", "1.2.3.0/24", "10.0.0.0/8"}), provider.NewZoneIDFilter([]string{""}), true, false, &client)
zones, _ := provider.zones()
var emptyZoneAuth *ibclient.ZoneAuth
assert.Equal(t, provider.findReverseZone(zones, "nomatch-example.com"), emptyZoneAuth)
assert.Equal(t, provider.findReverseZone(zones, "192.168.0.1"), emptyZoneAuth)
assert.Equal(t, provider.findReverseZone(zones, "1.2.3.4").Fqdn, "1.2.3.0/24")
assert.Equal(t, provider.findReverseZone(zones, "10.28.29.30").Fqdn, "10.0.0.0/8")
} }
func TestExtendedRequestFDQDRegExBuilder(t *testing.T) { func TestExtendedRequestFDQDRegExBuilder(t *testing.T) {

View File

@ -43,7 +43,7 @@ type crdSource struct {
crdResource string crdResource string
codec runtime.ParameterCodec codec runtime.ParameterCodec
annotationFilter string annotationFilter string
labelFilter string labelSelector labels.Selector
} }
func addKnownTypes(scheme *runtime.Scheme, groupVersion schema.GroupVersion) error { func addKnownTypes(scheme *runtime.Scheme, groupVersion schema.GroupVersion) error {
@ -103,12 +103,12 @@ func NewCRDClientForAPIVersionKind(client kubernetes.Interface, kubeConfig, apiS
} }
// NewCRDSource creates a new crdSource with the given config. // NewCRDSource creates a new crdSource with the given config.
func NewCRDSource(crdClient rest.Interface, namespace, kind string, annotationFilter string, labelFilter string, scheme *runtime.Scheme) (Source, error) { func NewCRDSource(crdClient rest.Interface, namespace, kind string, annotationFilter string, labelSelector labels.Selector, scheme *runtime.Scheme) (Source, error) {
return &crdSource{ return &crdSource{
crdResource: strings.ToLower(kind) + "s", crdResource: strings.ToLower(kind) + "s",
namespace: namespace, namespace: namespace,
annotationFilter: annotationFilter, annotationFilter: annotationFilter,
labelFilter: labelFilter, labelSelector: labelSelector,
crdClient: crdClient, crdClient: crdClient,
codec: runtime.NewParameterCodec(scheme), codec: runtime.NewParameterCodec(scheme),
}, nil }, nil
@ -126,11 +126,7 @@ func (cs *crdSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error
err error err error
) )
if cs.labelFilter != "" { result, err = cs.List(ctx, &metav1.ListOptions{LabelSelector: cs.labelSelector.String()})
result, err = cs.List(ctx, &metav1.ListOptions{LabelSelector: cs.labelFilter})
} else {
result, err = cs.List(ctx, &metav1.ListOptions{})
}
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -30,6 +30,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
@ -381,9 +382,13 @@ func testCRDSourceEndpoints(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
addKnownTypes(scheme, groupVersion) require.NoError(t, addKnownTypes(scheme, groupVersion))
cs, _ := NewCRDSource(restClient, ti.namespace, ti.kind, ti.annotationFilter, ti.labelFilter, scheme) labelSelector, err := labels.Parse(ti.labelFilter)
require.NoError(t, err)
cs, err := NewCRDSource(restClient, ti.namespace, ti.kind, ti.annotationFilter, labelSelector, scheme)
require.NoError(t, err)
receivedEndpoints, err := cs.Endpoints(context.Background()) receivedEndpoints, err := cs.Endpoints(context.Background())
if ti.expectError { if ti.expectError {

View File

@ -63,10 +63,11 @@ type ingressSource struct {
ingressInformer netinformers.IngressInformer ingressInformer netinformers.IngressInformer
ignoreIngressTLSSpec bool ignoreIngressTLSSpec bool
ignoreIngressRulesSpec bool ignoreIngressRulesSpec bool
labelSelector labels.Selector
} }
// NewIngressSource creates a new ingressSource with the given config. // NewIngressSource creates a new ingressSource with the given config.
func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, ignoreHostnameAnnotation bool, ignoreIngressTLSSpec bool, ignoreIngressRulesSpec bool, ingressClassNames []string) (Source, error) { func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, ignoreHostnameAnnotation bool, ignoreIngressTLSSpec bool, ignoreIngressRulesSpec bool, labelSelector labels.Selector, ingressClassNames []string) (Source, error) {
tmpl, err := parseTemplate(fqdnTemplate) tmpl, err := parseTemplate(fqdnTemplate)
if err != nil { if err != nil {
return nil, err return nil, err
@ -119,6 +120,7 @@ func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilt
ingressInformer: ingressInformer, ingressInformer: ingressInformer,
ignoreIngressTLSSpec: ignoreIngressTLSSpec, ignoreIngressTLSSpec: ignoreIngressTLSSpec,
ignoreIngressRulesSpec: ignoreIngressRulesSpec, ignoreIngressRulesSpec: ignoreIngressRulesSpec,
labelSelector: labelSelector,
} }
return sc, nil return sc, nil
} }
@ -126,7 +128,7 @@ func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilt
// Endpoints returns endpoint objects for each host-target combination that should be processed. // Endpoints returns endpoint objects for each host-target combination that should be processed.
// Retrieves all ingress resources on all namespaces // Retrieves all ingress resources on all namespaces
func (sc *ingressSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { func (sc *ingressSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
ingresses, err := sc.ingressInformer.Lister().Ingresses(sc.namespace).List(labels.Everything()) ingresses, err := sc.ingressInformer.Lister().Ingresses(sc.namespace).List(sc.labelSelector)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -26,6 +26,7 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
networkv1 "k8s.io/api/networking/v1" networkv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
@ -63,6 +64,7 @@ func (suite *IngressSuite) SetupTest() {
false, false,
false, false,
false, false,
labels.Everything(),
[]string{}, []string{},
) )
suite.NoError(err, "should initialize ingress source") suite.NoError(err, "should initialize ingress source")
@ -157,6 +159,7 @@ func TestNewIngressSource(t *testing.T) {
false, false,
false, false,
false, false,
labels.Everything(),
ti.ingressClassNames, ti.ingressClassNames,
) )
if ti.expectError { if ti.expectError {
@ -372,6 +375,7 @@ func testIngressEndpoints(t *testing.T) {
ignoreHostnameAnnotation bool ignoreHostnameAnnotation bool
ignoreIngressTLSSpec bool ignoreIngressTLSSpec bool
ignoreIngressRulesSpec bool ignoreIngressRulesSpec bool
ingressLabelSelector labels.Selector
ingressClassNames []string ingressClassNames []string
}{ }{
{ {
@ -1234,6 +1238,41 @@ func testIngressEndpoints(t *testing.T) {
Targets: endpoint.Targets{"4.5.6.7"}, Targets: endpoint.Targets{"4.5.6.7"},
}, },
}, },
},
{
ingressLabelSelector: labels.SelectorFromSet(labels.Set{"app": "web-external"}),
title: "ingress with matching labels",
targetNamespace: "",
ingressItems: []fakeIngress{
{
name: "fake1",
namespace: namespace,
dnsnames: []string{"example.org"},
ips: []string{"8.8.8.8"},
labels: map[string]string{"app": "web-external", "name": "reverse-proxy"},
},
},
expected: []*endpoint.Endpoint{
{
DNSName: "example.org",
Targets: endpoint.Targets{"8.8.8.8"},
},
},
},
{
ingressLabelSelector: labels.SelectorFromSet(labels.Set{"app": "web-external"}),
title: "ingress without matching labels",
targetNamespace: "",
ingressItems: []fakeIngress{
{
name: "fake1",
namespace: namespace,
dnsnames: []string{"example.org"},
ips: []string{"8.8.8.8"},
labels: map[string]string{"app": "web-internal", "name": "reverse-proxy"},
},
},
expected: []*endpoint.Endpoint{},
}, },
} { } {
ti := ti ti := ti
@ -1246,6 +1285,11 @@ func testIngressEndpoints(t *testing.T) {
_, err := fakeClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{}) _, err := fakeClient.NetworkingV1().Ingresses(ingress.Namespace).Create(context.Background(), ingress, metav1.CreateOptions{})
require.NoError(t, err) require.NoError(t, err)
} }
if ti.ingressLabelSelector == nil {
ti.ingressLabelSelector = labels.Everything()
}
source, _ := NewIngressSource( source, _ := NewIngressSource(
fakeClient, fakeClient,
ti.targetNamespace, ti.targetNamespace,
@ -1255,6 +1299,7 @@ func testIngressEndpoints(t *testing.T) {
ti.ignoreHostnameAnnotation, ti.ignoreHostnameAnnotation,
ti.ignoreIngressTLSSpec, ti.ignoreIngressTLSSpec,
ti.ignoreIngressRulesSpec, ti.ignoreIngressRulesSpec,
ti.ingressLabelSelector,
ti.ingressClassNames, ti.ingressClassNames,
) )
// Informer cache has all of the ingresses. Retrieve and validate their endpoints. // Informer cache has all of the ingresses. Retrieve and validate their endpoints.
@ -1278,6 +1323,7 @@ type fakeIngress struct {
namespace string namespace string
name string name string
annotations map[string]string annotations map[string]string
labels map[string]string
ingressClassName string ingressClassName string
} }
@ -1287,6 +1333,7 @@ func (ing fakeIngress) Ingress() *networkv1.Ingress {
Namespace: ing.namespace, Namespace: ing.namespace,
Name: ing.name, Name: ing.name,
Annotations: ing.annotations, Annotations: ing.annotations,
Labels: ing.labels,
}, },
Spec: networkv1.IngressSpec{ Spec: networkv1.IngressSpec{
Rules: []networkv1.IngressRule{}, Rules: []networkv1.IngressRule{},

View File

@ -48,6 +48,7 @@ type ocpRouteSource struct {
combineFQDNAnnotation bool combineFQDNAnnotation bool
ignoreHostnameAnnotation bool ignoreHostnameAnnotation bool
routeInformer routeInformer.RouteInformer routeInformer routeInformer.RouteInformer
labelSelector labels.Selector
} }
// NewOcpRouteSource creates a new ocpRouteSource with the given config. // NewOcpRouteSource creates a new ocpRouteSource with the given config.
@ -58,6 +59,7 @@ func NewOcpRouteSource(
fqdnTemplate string, fqdnTemplate string,
combineFQDNAnnotation bool, combineFQDNAnnotation bool,
ignoreHostnameAnnotation bool, ignoreHostnameAnnotation bool,
labelSelector labels.Selector,
) (Source, error) { ) (Source, error) {
tmpl, err := parseTemplate(fqdnTemplate) tmpl, err := parseTemplate(fqdnTemplate)
if err != nil { if err != nil {
@ -66,11 +68,11 @@ func NewOcpRouteSource(
// Use a shared informer to listen for add/update/delete of Routes in the specified namespace. // Use a shared informer to listen for add/update/delete of Routes in the specified namespace.
// Set resync period to 0, to prevent processing when nothing has changed. // Set resync period to 0, to prevent processing when nothing has changed.
informerFactory := extInformers.NewFilteredSharedInformerFactory(ocpClient, 0, namespace, nil) informerFactory := extInformers.NewSharedInformerFactoryWithOptions(ocpClient, 0, extInformers.WithNamespace(namespace))
routeInformer := informerFactory.Route().V1().Routes() informer := informerFactory.Route().V1().Routes()
// Add default resource event handlers to properly initialize informer. // Add default resource event handlers to properly initialize informer.
routeInformer.Informer().AddEventHandler( informer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{ cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { AddFunc: func(obj interface{}) {
}, },
@ -92,7 +94,8 @@ func NewOcpRouteSource(
fqdnTemplate: tmpl, fqdnTemplate: tmpl,
combineFQDNAnnotation: combineFQDNAnnotation, combineFQDNAnnotation: combineFQDNAnnotation,
ignoreHostnameAnnotation: ignoreHostnameAnnotation, ignoreHostnameAnnotation: ignoreHostnameAnnotation,
routeInformer: routeInformer, routeInformer: informer,
labelSelector: labelSelector,
}, nil }, nil
} }
@ -104,7 +107,7 @@ func (ors *ocpRouteSource) AddEventHandler(ctx context.Context, handler func())
// Retrieves all OpenShift Route resources on all namespaces, unless an explicit namespace // Retrieves all OpenShift Route resources on all namespaces, unless an explicit namespace
// is specified in ocpRouteSource. // is specified in ocpRouteSource.
func (ors *ocpRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { func (ors *ocpRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
ocpRoutes, err := ors.routeInformer.Lister().Routes(ors.namespace).List(labels.Everything()) ocpRoutes, err := ors.routeInformer.Lister().Routes(ors.namespace).List(ors.labelSelector)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"k8s.io/apimachinery/pkg/labels"
routev1 "github.com/openshift/api/route/v1" routev1 "github.com/openshift/api/route/v1"
fake "github.com/openshift/client-go/route/clientset/versioned/fake" fake "github.com/openshift/client-go/route/clientset/versioned/fake"
@ -48,6 +49,7 @@ func (suite *OCPRouteSuite) SetupTest() {
"{{.Name}}", "{{.Name}}",
false, false,
false, false,
labels.Everything(),
) )
suite.routeWithTargets = &routev1.Route{ suite.routeWithTargets = &routev1.Route{
@ -104,6 +106,7 @@ func testOcpRouteSourceNewOcpRouteSource(t *testing.T) {
annotationFilter string annotationFilter string
fqdnTemplate string fqdnTemplate string
expectError bool expectError bool
labelFilter string
}{ }{
{ {
title: "invalid template", title: "invalid template",
@ -124,8 +127,15 @@ func testOcpRouteSourceNewOcpRouteSource(t *testing.T) {
expectError: false, expectError: false,
annotationFilter: "kubernetes.io/ingress.class=nginx", annotationFilter: "kubernetes.io/ingress.class=nginx",
}, },
{
title: "valid label selector",
expectError: false,
labelFilter: "app=web-external",
},
} { } {
ti := ti ti := ti
labelSelector, err := labels.Parse(ti.labelFilter)
require.NoError(t, err)
t.Run(ti.title, func(t *testing.T) { t.Run(ti.title, func(t *testing.T) {
t.Parallel() t.Parallel()
@ -136,6 +146,7 @@ func testOcpRouteSourceNewOcpRouteSource(t *testing.T) {
ti.fqdnTemplate, ti.fqdnTemplate,
false, false,
false, false,
labelSelector,
) )
if ti.expectError { if ti.expectError {
@ -160,6 +171,7 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
ocpRoute *routev1.Route ocpRoute *routev1.Route
expected []*endpoint.Endpoint expected []*endpoint.Endpoint
expectError bool expectError bool
labelFilter string
}{ }{
{ {
title: "route with basic hostname and route status target", title: "route with basic hostname and route status target",
@ -240,6 +252,61 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
}, },
expectError: false, expectError: false,
}, },
{
title: "route with matching labels",
labelFilter: "app=web-external",
ignoreHostnameAnnotation: false,
ocpRoute: &routev1.Route{
Spec: routev1.RouteSpec{
Host: "my-annotation-domain.com",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "route-with-matching-labels",
Annotations: map[string]string{
"external-dns.alpha.kubernetes.io/target": "my.site.foo.com",
},
Labels: map[string]string{
"app": "web-external",
"name": "service-frontend",
},
},
},
expected: []*endpoint.Endpoint{
{
DNSName: "my-annotation-domain.com",
Targets: []string{
"my.site.foo.com",
},
},
},
expectError: false,
},
{
title: "route without matching labels",
labelFilter: "app=web-external",
ignoreHostnameAnnotation: false,
ocpRoute: &routev1.Route{
Spec: routev1.RouteSpec{
Host: "my-annotation-domain.com",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "route-without-matching-labels",
Annotations: map[string]string{
"external-dns.alpha.kubernetes.io/target": "my.site.foo.com",
},
Labels: map[string]string{
"app": "web-internal",
"name": "service-frontend",
},
},
},
expected: []*endpoint.Endpoint{},
expectError: false,
},
} { } {
tc := tc tc := tc
t.Run(tc.title, func(t *testing.T) { t.Run(tc.title, func(t *testing.T) {
@ -251,6 +318,9 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
_, err := fakeClient.RouteV1().Routes(tc.ocpRoute.Namespace).Create(context.Background(), tc.ocpRoute, metav1.CreateOptions{}) _, err := fakeClient.RouteV1().Routes(tc.ocpRoute.Namespace).Create(context.Background(), tc.ocpRoute, metav1.CreateOptions{})
require.NoError(t, err) require.NoError(t, err)
labelSelector, err := labels.Parse(tc.labelFilter)
require.NoError(t, err)
source, err := NewOcpRouteSource( source, err := NewOcpRouteSource(
fakeClient, fakeClient,
"", "",
@ -258,6 +328,7 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
"{{.Name}}", "{{.Name}}",
false, false,
false, false,
labelSelector,
) )
require.NoError(t, err) require.NoError(t, err)

View File

@ -63,10 +63,11 @@ type serviceSource struct {
podInformer coreinformers.PodInformer podInformer coreinformers.PodInformer
nodeInformer coreinformers.NodeInformer nodeInformer coreinformers.NodeInformer
serviceTypeFilter map[string]struct{} serviceTypeFilter map[string]struct{}
labelSelector labels.Selector
} }
// NewServiceSource creates a new serviceSource with the given config. // NewServiceSource creates a new serviceSource with the given config.
func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal bool, publishHostIP bool, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool) (Source, error) { func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal bool, publishHostIP bool, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector) (Source, error) {
tmpl, err := parseTemplate(fqdnTemplate) tmpl, err := parseTemplate(fqdnTemplate)
if err != nil { if err != nil {
return nil, err return nil, err
@ -137,12 +138,13 @@ func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilt
podInformer: podInformer, podInformer: podInformer,
nodeInformer: nodeInformer, nodeInformer: nodeInformer,
serviceTypeFilter: serviceTypes, serviceTypeFilter: serviceTypes,
labelSelector: labelSelector,
}, nil }, nil
} }
// Endpoints returns endpoint objects for each service that should be processed. // Endpoints returns endpoint objects for each service that should be processed.
func (sc *serviceSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { func (sc *serviceSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
services, err := sc.serviceInformer.Lister().Services(sc.namespace).List(labels.Everything()) services, err := sc.serviceInformer.Lister().Services(sc.namespace).List(sc.labelSelector)
if err != nil { if err != nil {
return nil, err return nil, err
} }

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
istioclient "istio.io/client-go/pkg/clientset/versioned" istioclient "istio.io/client-go/pkg/clientset/versioned"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
@ -42,7 +43,7 @@ var ErrSourceNotFound = errors.New("source not found")
type Config struct { type Config struct {
Namespace string Namespace string
AnnotationFilter string AnnotationFilter string
LabelFilter string LabelFilter labels.Selector
IngressClassNames []string IngressClassNames []string
FQDNTemplate string FQDNTemplate string
CombineFQDNAndAnnotation bool CombineFQDNAndAnnotation bool
@ -184,13 +185,13 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewServiceSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation) return NewServiceSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.Compatibility, cfg.PublishInternal, cfg.PublishHostIP, cfg.AlwaysPublishNotReadyAddresses, cfg.ServiceTypeFilter, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter)
case "ingress": case "ingress":
client, err := p.KubeClient() client, err := p.KubeClient()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewIngressSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.IgnoreIngressTLSSpec, cfg.IgnoreIngressRulesSpec, cfg.IngressClassNames) return NewIngressSource(client, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.IgnoreIngressTLSSpec, cfg.IgnoreIngressRulesSpec, cfg.LabelFilter, cfg.IngressClassNames)
case "pod": case "pod":
client, err := p.KubeClient() client, err := p.KubeClient()
if err != nil { if err != nil {
@ -254,7 +255,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewOcpRouteSource(ocpClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation) return NewOcpRouteSource(ocpClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter)
case "fake": case "fake":
return NewFakeSource(cfg.FQDNTemplate) return NewFakeSource(cfg.FQDNTemplate)
case "connector": case "connector":