mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 17:46:57 +02:00
Merge remote-tracking branch 'origin/master' into ingress-class-filtering
This commit is contained in:
commit
1ceec80ec5
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: Set up Go 1.x
|
- name: Set up Go 1.x
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ^1.16
|
go-version: ^1.17
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
# builder image
|
# builder image
|
||||||
ARG ARCH
|
ARG ARCH
|
||||||
FROM golang:1.16 as builder
|
FROM golang:1.17 as builder
|
||||||
ARG ARCH
|
ARG ARCH
|
||||||
|
|
||||||
WORKDIR /sigs.k8s.io/external-dns
|
WORKDIR /sigs.k8s.io/external-dns
|
||||||
@ -27,7 +27,7 @@ COPY . .
|
|||||||
RUN make test build.$ARCH
|
RUN make test build.$ARCH
|
||||||
|
|
||||||
# final image
|
# final image
|
||||||
FROM $ARCH/alpine:3.14
|
FROM $ARCH/alpine:3.15
|
||||||
|
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
COPY --from=builder /sigs.k8s.io/external-dns/build/external-dns /bin/external-dns
|
COPY --from=builder /sigs.k8s.io/external-dns/build/external-dns /bin/external-dns
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
FROM golang:1.16 as builder
|
FROM golang:1.17 as builder
|
||||||
|
|
||||||
WORKDIR /sigs.k8s.io/external-dns
|
WORKDIR /sigs.k8s.io/external-dns
|
||||||
|
|
||||||
|
2
Makefile
2
Makefile
@ -105,7 +105,7 @@ build.push/multiarch:
|
|||||||
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.14 ;\
|
docker pull $${arch}/alpine:3.14 ;\
|
||||||
docker pull golang:1.16 ;\
|
docker pull golang:1.17 ;\
|
||||||
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} ;\
|
||||||
arch_specific_tags+=( "--amend $${image}" ) ;\
|
arch_specific_tags+=( "--amend $${image}" ) ;\
|
||||||
|
11
README.md
11
README.md
@ -52,6 +52,7 @@ ExternalDNS' allows you to keep selected zones (via `--domain-filter`) synchroni
|
|||||||
* [Akamai Edge DNS](https://learn.akamai.com/en-us/products/cloud_security/edge_dns.html)
|
* [Akamai Edge DNS](https://learn.akamai.com/en-us/products/cloud_security/edge_dns.html)
|
||||||
* [GoDaddy](https://www.godaddy.com)
|
* [GoDaddy](https://www.godaddy.com)
|
||||||
* [Gandi](https://www.gandi.net)
|
* [Gandi](https://www.gandi.net)
|
||||||
|
* [UKFast SafeDNS](https://my.ukfast.co.uk/safedns/)
|
||||||
|
|
||||||
From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
|
From this release, ExternalDNS can become aware of the records it is managing (enabled via `--registry=txt`), therefore ExternalDNS can safely manage non-empty hosted zones. We strongly encourage you to use `v0.5` (or greater) with `--registry=txt` enabled and `--txt-owner-id` set to a unique value that doesn't change for the lifetime of your cluster. You might also want to run ExternalDNS in a dry run mode (`--dry-run` flag) to see the changes to be submitted to your DNS Provider API.
|
||||||
|
|
||||||
@ -109,13 +110,17 @@ The following table clarifies the current status of the providers according to t
|
|||||||
| UltraDNS | Alpha | |
|
| UltraDNS | Alpha | |
|
||||||
| GoDaddy | Alpha | |
|
| GoDaddy | Alpha | |
|
||||||
| Gandi | Alpha | @packi |
|
| Gandi | Alpha | @packi |
|
||||||
|
| SafeDNS | Alpha | @assureddt |
|
||||||
|
|
||||||
## Kubernetes version compatibility
|
## Kubernetes version compatibility
|
||||||
|
|
||||||
|
A [breaking change](https://github.com/kubernetes-sigs/external-dns/pull/2281) was added in external-dns v0.10.0.
|
||||||
|
|
||||||
| ExternalDNS | <= 0.9.x | >= 0.10.0 |
|
| ExternalDNS | <= 0.9.x | >= 0.10.0 |
|
||||||
| ------------------ | :----------------: | :----------------: |
|
| ------------------------------ | :----------------: | :----------------: |
|
||||||
| Kubernetes <= 1.18 | :white_check_mark: | :x: |
|
| Kubernetes <= 1.18 | :white_check_mark: | :x: |
|
||||||
| Kubernetes >= 1.19 | :x: | :white_check_mark: |
|
| Kubernetes >= 1.19 and <= 1.21 | :white_check_mark: | :white_check_mark: |
|
||||||
|
| Kubernetes >= 1.22 | :x: | :white_check_mark: |
|
||||||
|
|
||||||
## Running ExternalDNS:
|
## Running ExternalDNS:
|
||||||
|
|
||||||
@ -128,6 +133,7 @@ The are two ways of running ExternalDNS:
|
|||||||
|
|
||||||
The following tutorials are provided:
|
The following tutorials are provided:
|
||||||
|
|
||||||
|
* [Akamai Edge DNS](docs/tutorials/akamai-edgedns.md)
|
||||||
* [Alibaba Cloud](docs/tutorials/alibabacloud.md)
|
* [Alibaba Cloud](docs/tutorials/alibabacloud.md)
|
||||||
* AWS
|
* AWS
|
||||||
* [ALB Ingress Controller](docs/tutorials/alb-ingress.md)
|
* [ALB Ingress Controller](docs/tutorials/alb-ingress.md)
|
||||||
@ -171,6 +177,7 @@ The following tutorials are provided:
|
|||||||
* [UltraDNS](docs/tutorials/ultradns.md)
|
* [UltraDNS](docs/tutorials/ultradns.md)
|
||||||
* [GoDaddy](docs/tutorials/godaddy.md)
|
* [GoDaddy](docs/tutorials/godaddy.md)
|
||||||
* [Gandi](docs/tutorials/gandi.md)
|
* [Gandi](docs/tutorials/gandi.md)
|
||||||
|
* [SafeDNS](docs/tutorials/safedns.md)
|
||||||
|
|
||||||
### Running Locally
|
### Running Locally
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ 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.7.0
|
version: 1.7.1
|
||||||
appVersion: 0.10.2
|
appVersion: 0.10.2
|
||||||
keywords:
|
keywords:
|
||||||
- kubernetes
|
- kubernetes
|
||||||
|
@ -33,6 +33,7 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart
|
|||||||
| `serviceAccount.name` | Service account to be used. If not set and `serviceAccount.create` is `true`, a name is generated using the full name template. | `""` |
|
| `serviceAccount.name` | Service account to be used. If not set and `serviceAccount.create` is `true`, a name is generated using the full name template. | `""` |
|
||||||
| `rbac.create` | If `true`, create the RBAC resources. | `true` |
|
| `rbac.create` | If `true`, create the RBAC resources. | `true` |
|
||||||
| `rbac.additionalPermissions` | Additional permissions to be added to the cluster role. | `{}` |
|
| `rbac.additionalPermissions` | Additional permissions to be added to the cluster role. | `{}` |
|
||||||
|
| `deploymentAnnotations` | Annotations to add to the Deployment. | `{}` |
|
||||||
| `podLabels` | Labels to add to the pod. | `{}` |
|
| `podLabels` | Labels to add to the pod. | `{}` |
|
||||||
| `podAnnotations` | Annotations to add to the pod. | `{}` |
|
| `podAnnotations` | Annotations to add to the pod. | `{}` |
|
||||||
| `podSecurityContext` | Security context for the pod, this supports the full [PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core) API. | _see values.yaml_ |
|
| `podSecurityContext` | Security context for the pod, this supports the full [PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core) API. | _see values.yaml_ |
|
||||||
@ -54,6 +55,7 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart
|
|||||||
| `nodeSelector` | Node labels for pod assignment. | `{}` |
|
| `nodeSelector` | Node labels for pod assignment. | `{}` |
|
||||||
| `tolerations` | Tolerations for pod assignment, this supports the full [Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core) API. | `[]` |
|
| `tolerations` | Tolerations for pod assignment, this supports the full [Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core) API. | `[]` |
|
||||||
| `affinity` | Affinity settings for pod assignment, this supports the full [Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core) API. | `{}` |
|
| `affinity` | Affinity settings for pod assignment, this supports the full [Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core) API. | `{}` |
|
||||||
|
| `topologySpreadConstraints` | TopologySpreadConstraint settings for pod assignment, this supports the full [TopologySpreadConstraints](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#topologyspreadconstraint-v1-core) API. | `[]` |
|
||||||
| `logLevel` | Verbosity of the logs, available values are: `panic`, `debug`, `info`, `warn`, `error`, `fatal`. | `info` |
|
| `logLevel` | Verbosity of the logs, available values are: `panic`, `debug`, `info`, `warn`, `error`, `fatal`. | `info` |
|
||||||
| `logFormat` | Formats of the logs, available values are: `text`, `json`. | `text` |
|
| `logFormat` | Formats of the logs, available values are: `text`, `json`. | `text` |
|
||||||
| `interval` | The interval for DNS updates. | `1m` |
|
| `interval` | The interval for DNS updates. | `1m` |
|
||||||
|
@ -30,7 +30,7 @@ rules:
|
|||||||
verbs: ["get","watch","list"]
|
verbs: ["get","watch","list"]
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if has "istio-gateway" .Values.sources }}
|
{{- if or (has "istio-gateway" .Values.sources) (has "istio-virtualservice" .Values.sources) }}
|
||||||
- apiGroups: ["networking.istio.io"]
|
- apiGroups: ["networking.istio.io"]
|
||||||
resources: ["gateways"]
|
resources: ["gateways"]
|
||||||
verbs: ["get","watch","list"]
|
verbs: ["get","watch","list"]
|
||||||
|
@ -4,6 +4,10 @@ metadata:
|
|||||||
name: {{ include "external-dns.fullname" . }}
|
name: {{ include "external-dns.fullname" . }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "external-dns.labels" . | nindent 4 }}
|
{{- include "external-dns.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.deploymentAnnotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
@ -67,6 +71,9 @@ spec:
|
|||||||
{{- if .Values.txtPrefix }}
|
{{- if .Values.txtPrefix }}
|
||||||
- --txt-prefix={{ .Values.txtPrefix }}
|
- --txt-prefix={{ .Values.txtPrefix }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if and (eq .Values.txtPrefix "") (ne .Values.txtSuffix "") }}
|
||||||
|
- --txt-suffix={{ .Values.txtSuffix }}
|
||||||
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- range .Values.domainFilters }}
|
{{- range .Values.domainFilters }}
|
||||||
- --domain-filter={{ . }}
|
- --domain-filter={{ . }}
|
||||||
@ -103,6 +110,10 @@ spec:
|
|||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- with .Values.topologySpreadConstraints }}
|
||||||
|
topologySpreadConstraints:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
{{- with .Values.tolerations }}
|
{{- with .Values.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
@ -26,8 +26,12 @@ rbac:
|
|||||||
create: true
|
create: true
|
||||||
additionalPermissions: {}
|
additionalPermissions: {}
|
||||||
|
|
||||||
|
# Annotations to add to the Deployment
|
||||||
|
deploymentAnnotations: {}
|
||||||
|
|
||||||
podLabels: {}
|
podLabels: {}
|
||||||
|
|
||||||
|
# Annotations to add to the Pod
|
||||||
podAnnotations: {}
|
podAnnotations: {}
|
||||||
|
|
||||||
podSecurityContext:
|
podSecurityContext:
|
||||||
@ -88,6 +92,8 @@ tolerations: []
|
|||||||
|
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|
||||||
|
topologySpreadConstraints: []
|
||||||
|
|
||||||
logLevel: info
|
logLevel: info
|
||||||
logFormat: text
|
logFormat: text
|
||||||
|
|
||||||
@ -103,6 +109,7 @@ policy: upsert-only
|
|||||||
registry: txt
|
registry: txt
|
||||||
txtOwnerId: ""
|
txtOwnerId: ""
|
||||||
txtPrefix: ""
|
txtPrefix: ""
|
||||||
|
txtSuffix: ""
|
||||||
|
|
||||||
domainFilters: []
|
domainFilters: []
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Quick Start
|
# Quick Start
|
||||||
|
|
||||||
- [Git](https://git-scm.com/downloads)
|
- [Git](https://git-scm.com/downloads)
|
||||||
- [Go 1.16+](https://golang.org/dl/)
|
- [Go 1.17+](https://golang.org/dl/)
|
||||||
- [Go modules](https://github.com/golang/go/wiki/Modules)
|
- [Go modules](https://github.com/golang/go/wiki/Modules)
|
||||||
- [golangci-lint](https://github.com/golangci/golangci-lint)
|
- [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||||
- [Docker](https://docs.docker.com/install/)
|
- [Docker](https://docs.docker.com/install/)
|
||||||
|
@ -122,8 +122,4 @@ A single loop iteration of external-dns operation:
|
|||||||
1. DNS Provider should use batch operations
|
1. DNS Provider should use batch operations
|
||||||
2. DNS Provider should be called with CREATE operation (not UPSERT!) when the record does not yet exist!
|
2. DNS Provider should be called with CREATE operation (not UPSERT!) when the record does not yet exist!
|
||||||
|
|
||||||
### Resources:
|
|
||||||
|
|
||||||
1. Registry implementation draft:
|
|
||||||
|
|
||||||
[Flow](https://lh3.googleusercontent.com/BNUZZQ8XivYkXyYVPDgPCoZpwYv0pOyoyfBKbOnYJGsqueeB-EUXfzBZLk7xP-E_GDo7YHiTlA4XgPEs6ao_Ex0TY2SN66-yg5iRmn5Tc2EXVqs_yS9CtumhE1T4krZc4Z8_1gHOirDxCegU-Fk0K3fvg-J3UpzdKmGDG-JZwdzRyP4WyORWUQilJO9jErh-HP8AtM8p2ZjiqN9B3-VXdYuHbsiR6EHNFw43aOQAk52muDf2AgjqX2YUSbN9eO0Akt39ien3euT2HsZJlPvm5s8v2a_ZqTSW0DVcGaRhLQbZXcogSEP-ebbuGunuVbz45Ws8X6zJhZpASNQ-jknhGZEhZkSAQdwvihZpTsDdUuJx9RFDXNwA0lEaE_xediW119uJGywSNc6w8hnJZ6Xo49YQStuGbJKRAieQMvEhZXofiqCKyOUXSlsO7j9iE-rzis0JRSHWB8acA3AlcXqBj9D70AHfRHC_HfBLw9lcusy4dInmK2OCzGqXV11PoqibiZPqh-oNED31pToZQk4NB1xbOuUC_Tjf8UR_xAyhJ3yKzS09K898uCf-87Ra4iqRDCz3N35b=w2560-h1260)
|
|
||||||
|
210
docs/tutorials/UKFast_SafeDNS.md
Normal file
210
docs/tutorials/UKFast_SafeDNS.md
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
# Setting up ExternalDNS for Services on UKFast's SafeDNS
|
||||||
|
|
||||||
|
This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using SafeDNS.
|
||||||
|
|
||||||
|
Make sure to use **>=0.11.0** version of ExternalDNS for this tutorial.
|
||||||
|
|
||||||
|
## Managing DNS with SafeDNS
|
||||||
|
|
||||||
|
If you want to learn about how to use the SafeDNS service read the following tutorials:
|
||||||
|
To learn more about the use of SafeDNS in general, see the following page:
|
||||||
|
|
||||||
|
[UKFast's SafeDNS documentation](https://docs.ukfast.co.uk/domains/safedns/index.html).
|
||||||
|
|
||||||
|
## Creating SafeDNS credentials
|
||||||
|
|
||||||
|
Generate a fresh API token for use with ExternalDNS, following the instructions
|
||||||
|
at the UKFast developer [Getting-Started](https://developers.ukfast.io/getting-started)
|
||||||
|
page. You will need to grant read/write access to the SafeDNS API. No access to
|
||||||
|
any other UKFast service is required.
|
||||||
|
|
||||||
|
The environment variable `SAFEDNS_TOKEN` must have a value of this token to run
|
||||||
|
ExternalDNS with SafeDNS integration.
|
||||||
|
|
||||||
|
## Deploy ExternalDNS
|
||||||
|
|
||||||
|
Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
|
||||||
|
Then apply one of the following manifests file to deploy ExternalDNS.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
# You will need to check what the latest version is yourself:
|
||||||
|
# https://github.com/kubernetes-sigs/external-dns/releases
|
||||||
|
image: k8s.gcr.io/external-dns/external-dns:vX.Y.Z
|
||||||
|
args:
|
||||||
|
- --source=service # ingress is also possible
|
||||||
|
# (optional) limit to only example.com domains; change to match the
|
||||||
|
# zone created above.
|
||||||
|
- --domain-filter=example.com
|
||||||
|
- --provider=safedns
|
||||||
|
env:
|
||||||
|
- name: SAFEDNS_TOKEN
|
||||||
|
value: "SAFEDNSTOKENSAFEDNSTOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manifest (for clusters with RBAC enabled)
|
||||||
|
|
||||||
|
```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: ["extensions","networking.k8s.io"]
|
||||||
|
resources: ["ingresses"]
|
||||||
|
verbs: ["get","watch","list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["nodes"]
|
||||||
|
verbs: ["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
|
||||||
|
image: k8s.gcr.io/external-dns/external-dns:v0.11.0
|
||||||
|
args:
|
||||||
|
- --source=service # ingress is also possible
|
||||||
|
# (optional) limit to only example.com domains; change to match the
|
||||||
|
# zone created above.
|
||||||
|
- --domain-filter=example.com
|
||||||
|
- --provider=safedns
|
||||||
|
env:
|
||||||
|
- name: SAFEDNS_TOKEN
|
||||||
|
value: "SAFEDNSTOKENSAFEDNSTOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deploying an Nginx Service
|
||||||
|
|
||||||
|
Create a service file called 'nginx.yaml' with the following contents:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
annotations:
|
||||||
|
external-dns.alpha.kubernetes.io/hostname: my-app.example.com
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
type: LoadBalancer
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 80
|
||||||
|
targetPort: 80
|
||||||
|
```
|
||||||
|
|
||||||
|
Note the annotation on the service; use a hostname that matches the domain
|
||||||
|
filter specified above.
|
||||||
|
|
||||||
|
ExternalDNS uses this annotation to determine what services should be registered
|
||||||
|
with DNS. Removing the annotation will cause ExternalDNS to remove the
|
||||||
|
corresponding DNS records.
|
||||||
|
|
||||||
|
Create the deployment and service:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ kubectl create -f nginx.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Depending where you run your service it can take a little while for your cloud
|
||||||
|
provider to create an external IP for the service.
|
||||||
|
|
||||||
|
Once the service has an external IP assigned, ExternalDNS will notice the new
|
||||||
|
service IP address and synchronize the SafeDNS records.
|
||||||
|
|
||||||
|
## Verifying SafeDNS records
|
||||||
|
|
||||||
|
Check your [SafeDNS UI](https://my.ukfast.co.uk/safedns/index.php) and select
|
||||||
|
the appropriate domain to view the records for your SafeDNS zone.
|
||||||
|
|
||||||
|
This should show the external IP address of the service as the A record for your
|
||||||
|
domain.
|
||||||
|
|
||||||
|
Alternatively, you can perform a DNS lookup for the hostname specified:
|
||||||
|
```console
|
||||||
|
$ dig +short my-app.example.com
|
||||||
|
an.ip.addr.ess
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cleanup
|
||||||
|
|
||||||
|
Now that we have verified that ExternalDNS will automatically manage SafeDNS
|
||||||
|
records, we can delete the tutorial's example:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ kubectl delete service -f nginx.yaml
|
||||||
|
$ kubectl delete service -f externaldns.yaml
|
||||||
|
```
|
@ -383,7 +383,7 @@ metadata:
|
|||||||
name: nginx
|
name: nginx
|
||||||
annotations:
|
annotations:
|
||||||
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com
|
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com
|
||||||
external-dns.alpha.kubernetes.io/ttl: 60
|
external-dns.alpha.kubernetes.io/ttl: "60"
|
||||||
spec:
|
spec:
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
@ -7,8 +7,72 @@ Install the BlueCat Gateway product and deploy the [community gateway workflows]
|
|||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
The options for configuring the Bluecat Provider are available through the json file provided to External-DNS via the flag `--bluecat-config-file`. The
|
There are two ways to pass configuration options to the Bluecat Provider JSON configuration file and command line flags. The JSON configuration file option
|
||||||
BlueCat Gateway username and password can be supplied using the configuration file or environment variables `BLUECAT_USERNAME` and `BLUECAT_PASSWORD`.
|
is deprecated and will eventually be removed.
|
||||||
|
|
||||||
|
BlueCat provider supports getting the proxy URL from the environment variables. The format is the one specified by golang's [http.ProxyFromEnvironment](https://pkg.go.dev/net/http#ProxyFromEnvironment).
|
||||||
|
|
||||||
|
### Using CLI Flags
|
||||||
|
When using CLI flags to configure the Bluecat Provider the BlueCat Gateway credentials are passed in using environment variables `BLUECAT_USERNAME` and `BLUECAT_PASSWORD`.
|
||||||
|
|
||||||
|
#### Deploy
|
||||||
|
Setup up namespace, deployment, and service account:
|
||||||
|
```
|
||||||
|
kubectl create namespace bluecat-example
|
||||||
|
kubectl create secret generic bluecat-credentials --from-literal=username=bluecatuser --from-literal=password=bluecatpassword -n bluecat-example
|
||||||
|
cat << EOF > ~/bluecat.yml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: external-dns
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: external-dns
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: external-dns
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: external-dns
|
||||||
|
spec:
|
||||||
|
serviceAccountName: external-dns
|
||||||
|
containers:
|
||||||
|
- name: external-dns
|
||||||
|
image: k8s.gcr.io/external-dns/external-dns:v0.8.0
|
||||||
|
args:
|
||||||
|
- --log-level=debug
|
||||||
|
- --source=service
|
||||||
|
- --provider=bluecat
|
||||||
|
- --txt-owner-id=bluecat-example
|
||||||
|
- --bluecat-dns-configuration=Example
|
||||||
|
- --bluecat-dns-view=Internal
|
||||||
|
- --bluecat-gateway-host=https://bluecatgw.example.com
|
||||||
|
- --bluecat-root-zone=example.com
|
||||||
|
env:
|
||||||
|
- name: BLUECAT_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: bluecat-credentials
|
||||||
|
key: username
|
||||||
|
- name: BLUECAT_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: bluecat-credentials
|
||||||
|
key: password
|
||||||
|
EOF
|
||||||
|
kubectl apply -f ~/bluecat.yml -n bluecat-example
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Using JSON Configuration file (DEPRECATED)
|
||||||
|
The options for configuring the Bluecat Provider are available through the JSON file provided to External-DNS via the flag `--bluecat-config-file`.
|
||||||
|
|
||||||
| Key | Required |
|
| Key | Required |
|
||||||
| ----------------- | ------------------ |
|
| ----------------- | ------------------ |
|
||||||
@ -18,13 +82,11 @@ BlueCat Gateway username and password can be supplied using the configuration fi
|
|||||||
| dnsConfiguration | Yes |
|
| dnsConfiguration | Yes |
|
||||||
| dnsView | Yes |
|
| dnsView | Yes |
|
||||||
| rootZone | Yes |
|
| rootZone | Yes |
|
||||||
|
| dnsServerName | No |
|
||||||
|
| dnsDeployType | No |
|
||||||
| skipTLSVerify | No (default false) |
|
| skipTLSVerify | No (default false) |
|
||||||
|
|
||||||
### HTTP proxy
|
#### Deploy
|
||||||
|
|
||||||
BlueCat provider supports getting the proxy URL from the environment variables. The format is the one specified by golang's [http.ProxyFromEnvironment](https://pkg.go.dev/net/http#ProxyFromEnvironment).
|
|
||||||
|
|
||||||
## Deploy
|
|
||||||
Setup configuration file as k8s `Secret`.
|
Setup configuration file as k8s `Secret`.
|
||||||
```
|
```
|
||||||
cat << EOF > ~/bluecat.json
|
cat << EOF > ~/bluecat.json
|
||||||
|
148
go.mod
148
go.mod
@ -1,84 +1,164 @@
|
|||||||
module sigs.k8s.io/external-dns
|
module sigs.k8s.io/external-dns
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.97.0
|
cloud.google.com/go/compute v1.2.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 v61.4.0+incompatible
|
||||||
github.com/Azure/go-autorest/autorest v0.11.21
|
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/StackExchange/dnscontrol v0.2.8
|
github.com/StackExchange/dnscontrol v0.2.8
|
||||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1
|
||||||
github.com/alecthomas/colour v0.1.0 // indirect
|
|
||||||
github.com/alecthomas/kingpin v2.2.5+incompatible
|
github.com/alecthomas/kingpin v2.2.5+incompatible
|
||||||
github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c // indirect
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1483
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357
|
github.com/aws/aws-sdk-go v1.42.52
|
||||||
github.com/aws/aws-sdk-go v1.40.53
|
|
||||||
github.com/bodgit/tsig v0.0.2
|
github.com/bodgit/tsig v0.0.2
|
||||||
github.com/cloudflare/cloudflare-go v0.25.0
|
github.com/cloudflare/cloudflare-go v0.25.0
|
||||||
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.69.1
|
github.com/digitalocean/godo v1.75.0
|
||||||
github.com/dnsimple/dnsimple-go v0.60.0
|
github.com/dnsimple/dnsimple-go v0.71.1
|
||||||
github.com/exoscale/egoscale v0.80.1
|
github.com/exoscale/egoscale v1.19.0
|
||||||
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
|
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
|
||||||
github.com/go-gandi/go-gandi v0.0.0-20200921091836-0d8a64b9cc09
|
github.com/go-gandi/go-gandi v0.0.0-20200921091836-0d8a64b9cc09
|
||||||
github.com/go-logr/logr v1.1.0 // indirect
|
|
||||||
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f
|
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f
|
||||||
github.com/google/go-cmp v0.5.6
|
github.com/google/go-cmp v0.5.7
|
||||||
github.com/gophercloud/gophercloud v0.22.0
|
github.com/gophercloud/gophercloud v0.22.0
|
||||||
github.com/hooklift/gowsdl v0.5.0
|
github.com/hooklift/gowsdl v0.5.0
|
||||||
github.com/infobloxopen/infoblox-go-client v1.1.1
|
github.com/infobloxopen/infoblox-go-client v1.1.1
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
|
||||||
github.com/linki/instrumented_http v0.3.0
|
github.com/linki/instrumented_http v0.3.0
|
||||||
github.com/linode/linodego v0.32.2
|
github.com/linode/linodego v0.32.2
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
|
||||||
github.com/maxatome/go-testdeep v1.10.1
|
github.com/maxatome/go-testdeep v1.10.1
|
||||||
github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1
|
github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1
|
||||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
|
||||||
github.com/nesv/go-dynect v0.6.0
|
github.com/nesv/go-dynect v0.6.0
|
||||||
github.com/nic-at/rc0go v1.1.1
|
github.com/nic-at/rc0go v1.1.1
|
||||||
github.com/onsi/gomega v1.14.0 // indirect
|
|
||||||
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae
|
github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae
|
||||||
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73
|
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73
|
||||||
github.com/oracle/oci-go-sdk v21.4.0+incompatible
|
github.com/oracle/oci-go-sdk v24.3.0+incompatible
|
||||||
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
|
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/projectcontour/contour v1.18.2
|
github.com/projectcontour/contour v1.20.0
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f
|
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/smartystreets/gunit v1.3.4 // indirect
|
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/terra-farm/udnssdk v1.3.5 // indirect
|
|
||||||
github.com/transip/gotransip/v6 v6.6.2
|
github.com/transip/gotransip/v6 v6.6.2
|
||||||
|
github.com/ukfast/sdk-go v1.4.23
|
||||||
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.9.0
|
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
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
|
google.golang.org/api v0.66.0
|
||||||
golang.org/x/text v0.3.7 // indirect
|
|
||||||
google.golang.org/api v0.58.0
|
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1
|
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
istio.io/api v0.0.0-20210128181506-0c4b8e54850f
|
istio.io/api v0.0.0-20210128181506-0c4b8e54850f
|
||||||
istio.io/client-go v0.0.0-20210128182905-ee2edd059e02
|
istio.io/client-go v0.0.0-20210128182905-ee2edd059e02
|
||||||
k8s.io/api v0.22.2
|
k8s.io/api v0.23.1
|
||||||
k8s.io/apimachinery v0.22.2
|
k8s.io/apimachinery v0.23.3
|
||||||
k8s.io/client-go v0.22.2
|
k8s.io/client-go v0.23.1
|
||||||
k8s.io/klog/v2 v2.20.0 // indirect
|
)
|
||||||
k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e // indirect
|
|
||||||
|
require (
|
||||||
|
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
|
||||||
|
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||||
|
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||||
|
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||||
|
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||||
|
github.com/Masterminds/semver v1.4.2 // indirect
|
||||||
|
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
||||||
|
github.com/alecthomas/colour v0.1.0 // indirect
|
||||||
|
github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c // indirect
|
||||||
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||||
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
||||||
|
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect
|
||||||
|
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||||
|
github.com/coreos/go-semver v0.3.0 // indirect
|
||||||
|
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||||
|
github.com/fatih/structs v1.1.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.12.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.16.0 // indirect
|
||||||
|
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect
|
||||||
|
github.com/gofrs/uuid v3.2.0+incompatible // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.0.0 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/google/uuid v1.1.2 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
|
||||||
|
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||||
|
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
|
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
|
||||||
|
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
|
||||||
|
github.com/jcmturner/gofork v1.0.0 // indirect
|
||||||
|
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
|
||||||
|
github.com/jcmturner/gokrb5/v8 v8.4.1 // indirect
|
||||||
|
github.com/jcmturner/rpc/v2 v2.0.2 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/leodido/go-urn v1.1.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b // indirect
|
||||||
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
|
github.com/prometheus/common v0.28.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.6.0 // indirect
|
||||||
|
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||||
|
github.com/smartystreets/gunit v1.3.4 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/stretchr/objx v0.3.0 // indirect
|
||||||
|
github.com/terra-farm/udnssdk v1.3.5 // indirect
|
||||||
|
github.com/ukfast/go-durationstring v1.0.0 // indirect
|
||||||
|
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
|
||||||
|
go.opencensus.io v0.23.0 // indirect
|
||||||
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
|
go.uber.org/zap v1.19.1 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
|
||||||
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44 // indirect
|
||||||
|
google.golang.org/grpc v1.42.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
|
gopkg.in/go-playground/validator.v9 v9.27.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||||
|
gopkg.in/resty.v1 v1.12.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a // indirect
|
||||||
|
k8s.io/klog/v2 v2.30.0 // indirect
|
||||||
|
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
|
||||||
|
k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect
|
||||||
|
sigs.k8s.io/controller-runtime v0.11.0 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
|
||||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
10
main.go
10
main.go
@ -62,6 +62,7 @@ import (
|
|||||||
"sigs.k8s.io/external-dns/provider/rcode0"
|
"sigs.k8s.io/external-dns/provider/rcode0"
|
||||||
"sigs.k8s.io/external-dns/provider/rdns"
|
"sigs.k8s.io/external-dns/provider/rdns"
|
||||||
"sigs.k8s.io/external-dns/provider/rfc2136"
|
"sigs.k8s.io/external-dns/provider/rfc2136"
|
||||||
|
"sigs.k8s.io/external-dns/provider/safedns"
|
||||||
"sigs.k8s.io/external-dns/provider/scaleway"
|
"sigs.k8s.io/external-dns/provider/scaleway"
|
||||||
"sigs.k8s.io/external-dns/provider/transip"
|
"sigs.k8s.io/external-dns/provider/transip"
|
||||||
"sigs.k8s.io/external-dns/provider/ultradns"
|
"sigs.k8s.io/external-dns/provider/ultradns"
|
||||||
@ -136,7 +137,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lookup all the selected sources by names and pass them the desired configuration.
|
// Lookup all the selected sources by names and pass them the desired configuration.
|
||||||
sources, err := source.ByNames(&source.SingletonClientGenerator{
|
sources, err := source.ByNames(ctx, &source.SingletonClientGenerator{
|
||||||
KubeConfig: cfg.KubeConfig,
|
KubeConfig: cfg.KubeConfig,
|
||||||
APIServerURL: cfg.APIServerURL,
|
APIServerURL: cfg.APIServerURL,
|
||||||
// If update events are enabled, disable timeout.
|
// If update events are enabled, disable timeout.
|
||||||
@ -206,13 +207,13 @@ func main() {
|
|||||||
log.Infof("Registry \"%s\" cannot be used with AWS Cloud Map. Switching to \"aws-sd\".", cfg.Registry)
|
log.Infof("Registry \"%s\" cannot be used with AWS Cloud Map. Switching to \"aws-sd\".", cfg.Registry)
|
||||||
cfg.Registry = "aws-sd"
|
cfg.Registry = "aws-sd"
|
||||||
}
|
}
|
||||||
p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.AWSAssumeRole, cfg.DryRun)
|
p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.AWSAssumeRole, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID)
|
||||||
case "azure-dns", "azure":
|
case "azure-dns", "azure":
|
||||||
p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
||||||
case "azure-private-dns":
|
case "azure-private-dns":
|
||||||
p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
||||||
case "bluecat":
|
case "bluecat":
|
||||||
p, err = bluecat.NewBluecatProvider(cfg.BluecatConfigFile, domainFilter, zoneIDFilter, cfg.DryRun)
|
p, err = bluecat.NewBluecatProvider(cfg.BluecatConfigFile, cfg.BluecatDNSConfiguration, cfg.BluecatDNSServerName, cfg.BluecatDNSDeployType, cfg.BluecatDNSView, cfg.BluecatGatewayHost, cfg.BluecatRootZone, domainFilter, zoneIDFilter, cfg.DryRun, cfg.BluecatSkipTLSVerify)
|
||||||
case "vinyldns":
|
case "vinyldns":
|
||||||
p, err = vinyldns.NewVinylDNSProvider(domainFilter, zoneIDFilter, cfg.DryRun)
|
p, err = vinyldns.NewVinylDNSProvider(domainFilter, zoneIDFilter, cfg.DryRun)
|
||||||
case "vultr":
|
case "vultr":
|
||||||
@ -251,6 +252,7 @@ func main() {
|
|||||||
DryRun: cfg.DryRun,
|
DryRun: cfg.DryRun,
|
||||||
FQDNRexEx: cfg.InfobloxFQDNRegEx,
|
FQDNRexEx: cfg.InfobloxFQDNRegEx,
|
||||||
CreatePTR: cfg.InfobloxCreatePTR,
|
CreatePTR: cfg.InfobloxCreatePTR,
|
||||||
|
CacheDuration: cfg.InfobloxCacheDuration,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
case "dyn":
|
case "dyn":
|
||||||
@ -324,6 +326,8 @@ func main() {
|
|||||||
p, err = godaddy.NewGoDaddyProvider(ctx, domainFilter, cfg.GoDaddyTTL, cfg.GoDaddyAPIKey, cfg.GoDaddySecretKey, cfg.GoDaddyOTE, cfg.DryRun)
|
p, err = godaddy.NewGoDaddyProvider(ctx, domainFilter, cfg.GoDaddyTTL, cfg.GoDaddyAPIKey, cfg.GoDaddySecretKey, cfg.GoDaddyOTE, cfg.DryRun)
|
||||||
case "gandi":
|
case "gandi":
|
||||||
p, err = gandi.NewGandiProvider(ctx, domainFilter, cfg.DryRun)
|
p, err = gandi.NewGandiProvider(ctx, domainFilter, cfg.DryRun)
|
||||||
|
case "safedns":
|
||||||
|
p, err = safedns.NewSafeDNSProvider(domainFilter, cfg.DryRun)
|
||||||
default:
|
default:
|
||||||
log.Fatalf("unknown dns provider: %s", cfg.Provider)
|
log.Fatalf("unknown dns provider: %s", cfg.Provider)
|
||||||
}
|
}
|
||||||
|
@ -88,11 +88,19 @@ type Config struct {
|
|||||||
AWSAPIRetries int
|
AWSAPIRetries int
|
||||||
AWSPreferCNAME bool
|
AWSPreferCNAME bool
|
||||||
AWSZoneCacheDuration time.Duration
|
AWSZoneCacheDuration time.Duration
|
||||||
|
AWSSDServiceCleanup bool
|
||||||
AzureConfigFile string
|
AzureConfigFile string
|
||||||
AzureResourceGroup string
|
AzureResourceGroup string
|
||||||
AzureSubscriptionID string
|
AzureSubscriptionID string
|
||||||
AzureUserAssignedIdentityClientID string
|
AzureUserAssignedIdentityClientID string
|
||||||
|
BluecatDNSConfiguration string
|
||||||
BluecatConfigFile string
|
BluecatConfigFile string
|
||||||
|
BluecatDNSView string
|
||||||
|
BluecatGatewayHost string
|
||||||
|
BluecatRootZone string
|
||||||
|
BluecatDNSServerName string
|
||||||
|
BluecatDNSDeployType string
|
||||||
|
BluecatSkipTLSVerify bool
|
||||||
CloudflareProxied bool
|
CloudflareProxied bool
|
||||||
CloudflareZonesPerPage int
|
CloudflareZonesPerPage int
|
||||||
CoreDNSPrefix string
|
CoreDNSPrefix string
|
||||||
@ -113,6 +121,7 @@ type Config struct {
|
|||||||
InfobloxMaxResults int
|
InfobloxMaxResults int
|
||||||
InfobloxFQDNRegEx string
|
InfobloxFQDNRegEx string
|
||||||
InfobloxCreatePTR bool
|
InfobloxCreatePTR bool
|
||||||
|
InfobloxCacheDuration int
|
||||||
DynCustomerName string
|
DynCustomerName string
|
||||||
DynUsername string
|
DynUsername string
|
||||||
DynPassword string `secure:"yes"`
|
DynPassword string `secure:"yes"`
|
||||||
@ -220,10 +229,12 @@ var defaultConfig = &Config{
|
|||||||
AWSAPIRetries: 3,
|
AWSAPIRetries: 3,
|
||||||
AWSPreferCNAME: false,
|
AWSPreferCNAME: false,
|
||||||
AWSZoneCacheDuration: 0 * time.Second,
|
AWSZoneCacheDuration: 0 * time.Second,
|
||||||
|
AWSSDServiceCleanup: false,
|
||||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||||
AzureResourceGroup: "",
|
AzureResourceGroup: "",
|
||||||
AzureSubscriptionID: "",
|
AzureSubscriptionID: "",
|
||||||
BluecatConfigFile: "/etc/kubernetes/bluecat.json",
|
BluecatConfigFile: "/etc/kubernetes/bluecat.json",
|
||||||
|
BluecatDNSDeployType: "no-deploy",
|
||||||
CloudflareProxied: false,
|
CloudflareProxied: false,
|
||||||
CloudflareZonesPerPage: 50,
|
CloudflareZonesPerPage: 50,
|
||||||
CoreDNSPrefix: "/skydns/",
|
CoreDNSPrefix: "/skydns/",
|
||||||
@ -244,6 +255,7 @@ var defaultConfig = &Config{
|
|||||||
InfobloxMaxResults: 0,
|
InfobloxMaxResults: 0,
|
||||||
InfobloxFQDNRegEx: "",
|
InfobloxFQDNRegEx: "",
|
||||||
InfobloxCreatePTR: false,
|
InfobloxCreatePTR: false,
|
||||||
|
InfobloxCacheDuration: 0,
|
||||||
OCIConfigFile: "/etc/kubernetes/oci.yaml",
|
OCIConfigFile: "/etc/kubernetes/oci.yaml",
|
||||||
InMemoryZones: []string{},
|
InMemoryZones: []string{},
|
||||||
OVHEndpoint: "ovh-eu",
|
OVHEndpoint: "ovh-eu",
|
||||||
@ -388,7 +400,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
|||||||
app.Flag("default-targets", "Set globally default IP address that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets)
|
app.Flag("default-targets", "Set globally default IP address that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets)
|
||||||
|
|
||||||
// Flags related to providers
|
// Flags related to providers
|
||||||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, bluecat, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns, gandi)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy", "bluecat", "gandi")
|
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, bluecat, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns, gandi, safedns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy", "bluecat", "gandi", "safedns")
|
||||||
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
|
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
|
||||||
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
|
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
|
||||||
app.Flag("regex-domain-filter", "Limit possible domains and target zones by a Regex filter; Overrides domain-filter (optional)").Default(defaultConfig.RegexDomainFilter.String()).RegexpVar(&cfg.RegexDomainFilter)
|
app.Flag("regex-domain-filter", "Limit possible domains and target zones by a Regex filter; Overrides domain-filter (optional)").Default(defaultConfig.RegexDomainFilter.String()).RegexpVar(&cfg.RegexDomainFilter)
|
||||||
@ -410,11 +422,22 @@ func (cfg *Config) ParseFlags(args []string) error {
|
|||||||
app.Flag("aws-api-retries", "When using the AWS provider, set the maximum number of retries for API calls before giving up.").Default(strconv.Itoa(defaultConfig.AWSAPIRetries)).IntVar(&cfg.AWSAPIRetries)
|
app.Flag("aws-api-retries", "When using the AWS provider, set the maximum number of retries for API calls before giving up.").Default(strconv.Itoa(defaultConfig.AWSAPIRetries)).IntVar(&cfg.AWSAPIRetries)
|
||||||
app.Flag("aws-prefer-cname", "When using the AWS provider, prefer using CNAME instead of ALIAS (default: disabled)").BoolVar(&cfg.AWSPreferCNAME)
|
app.Flag("aws-prefer-cname", "When using the AWS provider, prefer using CNAME instead of ALIAS (default: disabled)").BoolVar(&cfg.AWSPreferCNAME)
|
||||||
app.Flag("aws-zones-cache-duration", "When using the AWS provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AWSZoneCacheDuration.String()).DurationVar(&cfg.AWSZoneCacheDuration)
|
app.Flag("aws-zones-cache-duration", "When using the AWS provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AWSZoneCacheDuration.String()).DurationVar(&cfg.AWSZoneCacheDuration)
|
||||||
|
app.Flag("aws-sd-service-cleanup", "When using the AWS CloudMap provider, delete empty Services without endpoints (default: disabled)").BoolVar(&cfg.AWSSDServiceCleanup)
|
||||||
app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile)
|
app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile)
|
||||||
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (required when --provider=azure-private-dns)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
|
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (required when --provider=azure-private-dns)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
|
||||||
app.Flag("azure-subscription-id", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure-private-dns)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID)
|
app.Flag("azure-subscription-id", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure-private-dns)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID)
|
||||||
app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID)
|
app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID)
|
||||||
app.Flag("bluecat-config-file", "When using the Bluecat provider, specify the Bluecat configuration file (required when --provider=bluecat").Default(defaultConfig.BluecatConfigFile).StringVar(&cfg.BluecatConfigFile)
|
|
||||||
|
// Flags related to BlueCat provider
|
||||||
|
app.Flag("bluecat-dns-configuration", "When using the Bluecat provider, specify the Bluecat DNS configuration string (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatDNSConfiguration)
|
||||||
|
app.Flag("bluecat-config-file", "When using the Bluecat provider, specify the Bluecat configuration file (optional when --provider=bluecat)").Default(defaultConfig.BluecatConfigFile).StringVar(&cfg.BluecatConfigFile)
|
||||||
|
app.Flag("bluecat-dns-view", "When using the Bluecat provider, specify the Bluecat DNS view string (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatDNSView)
|
||||||
|
app.Flag("bluecat-gateway-host", "When using the Bluecat provider, specify the Bluecat Gateway Host (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatGatewayHost)
|
||||||
|
app.Flag("bluecat-root-zone", "When using the Bluecat provider, specify the Bluecat root zone (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatRootZone)
|
||||||
|
app.Flag("bluecat-skip-tls-verify", "When using the Bluecat provider, specify to skip TLS verification (optional when --provider=bluecat) (default: false)").BoolVar(&cfg.BluecatSkipTLSVerify)
|
||||||
|
app.Flag("bluecat-dns-server-name", "When using the Bluecat provider, specify the Bluecat DNS Server to initiate deploys against. This is only used if --bluecat-dns-deploy-type is not 'no-deploy' (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatDNSServerName)
|
||||||
|
app.Flag("bluecat-dns-deploy-type", "When using the Bluecat provider, specify the type of DNS deployment to initiate after records are updated. Valid options are 'full-deploy' and 'no-deploy'. Deploy will only execute if --bluecat-dns-server-name is set (optional when --provider=bluecat)").Default(defaultConfig.BluecatDNSDeployType).StringVar(&cfg.BluecatDNSDeployType)
|
||||||
|
|
||||||
app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied)
|
app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied)
|
||||||
app.Flag("cloudflare-zones-per-page", "When using the Cloudflare provider, specify how many zones per page listed, max. possible 50 (default: 50)").Default(strconv.Itoa(defaultConfig.CloudflareZonesPerPage)).IntVar(&cfg.CloudflareZonesPerPage)
|
app.Flag("cloudflare-zones-per-page", "When using the Cloudflare provider, specify how many zones per page listed, max. possible 50 (default: 50)").Default(strconv.Itoa(defaultConfig.CloudflareZonesPerPage)).IntVar(&cfg.CloudflareZonesPerPage)
|
||||||
app.Flag("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix)
|
app.Flag("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix)
|
||||||
@ -434,6 +457,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
|||||||
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("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("infoblox-cache-duration", "When using the Infoblox provider, set the record TTL (0s to disable).").Default(strconv.Itoa(defaultConfig.InfobloxCacheDuration)).IntVar(&cfg.InfobloxCacheDuration)
|
||||||
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)
|
||||||
|
@ -63,10 +63,18 @@ var (
|
|||||||
AWSAPIRetries: 3,
|
AWSAPIRetries: 3,
|
||||||
AWSPreferCNAME: false,
|
AWSPreferCNAME: false,
|
||||||
AWSZoneCacheDuration: 0 * time.Second,
|
AWSZoneCacheDuration: 0 * time.Second,
|
||||||
|
AWSSDServiceCleanup: false,
|
||||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||||
AzureResourceGroup: "",
|
AzureResourceGroup: "",
|
||||||
AzureSubscriptionID: "",
|
AzureSubscriptionID: "",
|
||||||
|
BluecatDNSConfiguration: "",
|
||||||
|
BluecatDNSServerName: "",
|
||||||
BluecatConfigFile: "/etc/kubernetes/bluecat.json",
|
BluecatConfigFile: "/etc/kubernetes/bluecat.json",
|
||||||
|
BluecatDNSView: "",
|
||||||
|
BluecatGatewayHost: "",
|
||||||
|
BluecatRootZone: "",
|
||||||
|
BluecatDNSDeployType: defaultConfig.BluecatDNSDeployType,
|
||||||
|
BluecatSkipTLSVerify: false,
|
||||||
CloudflareProxied: false,
|
CloudflareProxied: false,
|
||||||
CloudflareZonesPerPage: 50,
|
CloudflareZonesPerPage: 50,
|
||||||
CoreDNSPrefix: "/skydns/",
|
CoreDNSPrefix: "/skydns/",
|
||||||
@ -153,10 +161,18 @@ var (
|
|||||||
AWSAPIRetries: 13,
|
AWSAPIRetries: 13,
|
||||||
AWSPreferCNAME: true,
|
AWSPreferCNAME: true,
|
||||||
AWSZoneCacheDuration: 10 * time.Second,
|
AWSZoneCacheDuration: 10 * time.Second,
|
||||||
|
AWSSDServiceCleanup: true,
|
||||||
AzureConfigFile: "azure.json",
|
AzureConfigFile: "azure.json",
|
||||||
AzureResourceGroup: "arg",
|
AzureResourceGroup: "arg",
|
||||||
AzureSubscriptionID: "arg",
|
AzureSubscriptionID: "arg",
|
||||||
|
BluecatDNSConfiguration: "arg",
|
||||||
|
BluecatDNSServerName: "arg",
|
||||||
BluecatConfigFile: "bluecat.json",
|
BluecatConfigFile: "bluecat.json",
|
||||||
|
BluecatDNSView: "arg",
|
||||||
|
BluecatGatewayHost: "arg",
|
||||||
|
BluecatRootZone: "arg",
|
||||||
|
BluecatDNSDeployType: "full-deploy",
|
||||||
|
BluecatSkipTLSVerify: true,
|
||||||
CloudflareProxied: true,
|
CloudflareProxied: true,
|
||||||
CloudflareZonesPerPage: 20,
|
CloudflareZonesPerPage: 20,
|
||||||
CoreDNSPrefix: "/coredns/",
|
CoreDNSPrefix: "/coredns/",
|
||||||
@ -209,7 +225,7 @@ var (
|
|||||||
TransIPAccountName: "transip",
|
TransIPAccountName: "transip",
|
||||||
TransIPPrivateKeyFile: "/path/to/transip.key",
|
TransIPPrivateKeyFile: "/path/to/transip.key",
|
||||||
DigitalOceanAPIPageSize: 100,
|
DigitalOceanAPIPageSize: 100,
|
||||||
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME, endpoint.RecordTypeNS},
|
||||||
RFC2136BatchChangeSize: 100,
|
RFC2136BatchChangeSize: 100,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -257,7 +273,14 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"--azure-config-file=azure.json",
|
"--azure-config-file=azure.json",
|
||||||
"--azure-resource-group=arg",
|
"--azure-resource-group=arg",
|
||||||
"--azure-subscription-id=arg",
|
"--azure-subscription-id=arg",
|
||||||
|
"--bluecat-dns-configuration=arg",
|
||||||
"--bluecat-config-file=bluecat.json",
|
"--bluecat-config-file=bluecat.json",
|
||||||
|
"--bluecat-dns-view=arg",
|
||||||
|
"--bluecat-dns-server-name=arg",
|
||||||
|
"--bluecat-gateway-host=arg",
|
||||||
|
"--bluecat-root-zone=arg",
|
||||||
|
"--bluecat-dns-deploy-type=full-deploy",
|
||||||
|
"--bluecat-skip-tls-verify",
|
||||||
"--cloudflare-proxied",
|
"--cloudflare-proxied",
|
||||||
"--cloudflare-zones-per-page=20",
|
"--cloudflare-zones-per-page=20",
|
||||||
"--coredns-prefix=/coredns/",
|
"--coredns-prefix=/coredns/",
|
||||||
@ -304,6 +327,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"--aws-api-retries=13",
|
"--aws-api-retries=13",
|
||||||
"--aws-prefer-cname",
|
"--aws-prefer-cname",
|
||||||
"--aws-zones-cache-duration=10s",
|
"--aws-zones-cache-duration=10s",
|
||||||
|
"--aws-sd-service-cleanup",
|
||||||
"--no-aws-evaluate-target-health",
|
"--no-aws-evaluate-target-health",
|
||||||
"--policy=upsert-only",
|
"--policy=upsert-only",
|
||||||
"--registry=noop",
|
"--registry=noop",
|
||||||
@ -330,6 +354,9 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"--transip-account=transip",
|
"--transip-account=transip",
|
||||||
"--transip-keyfile=/path/to/transip.key",
|
"--transip-keyfile=/path/to/transip.key",
|
||||||
"--digitalocean-api-page-size=100",
|
"--digitalocean-api-page-size=100",
|
||||||
|
"--managed-record-types=A",
|
||||||
|
"--managed-record-types=CNAME",
|
||||||
|
"--managed-record-types=NS",
|
||||||
"--rfc2136-batch-change-size=100",
|
"--rfc2136-batch-change-size=100",
|
||||||
},
|
},
|
||||||
envVars: map[string]string{},
|
envVars: map[string]string{},
|
||||||
@ -360,7 +387,14 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"EXTERNAL_DNS_AZURE_CONFIG_FILE": "azure.json",
|
"EXTERNAL_DNS_AZURE_CONFIG_FILE": "azure.json",
|
||||||
"EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg",
|
"EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg",
|
||||||
"EXTERNAL_DNS_AZURE_SUBSCRIPTION_ID": "arg",
|
"EXTERNAL_DNS_AZURE_SUBSCRIPTION_ID": "arg",
|
||||||
|
"EXTERNAL_DNS_BLUECAT_DNS_CONFIGURATION": "arg",
|
||||||
|
"EXTERNAL_DNS_BLUECAT_DNS_SERVER_NAME": "arg",
|
||||||
|
"EXTERNAL_DNS_BLUECAT_DNS_DEPLOY_TYPE": "full-deploy",
|
||||||
"EXTERNAL_DNS_BLUECAT_CONFIG_FILE": "bluecat.json",
|
"EXTERNAL_DNS_BLUECAT_CONFIG_FILE": "bluecat.json",
|
||||||
|
"EXTERNAL_DNS_BLUECAT_DNS_VIEW": "arg",
|
||||||
|
"EXTERNAL_DNS_BLUECAT_GATEWAY_HOST": "arg",
|
||||||
|
"EXTERNAL_DNS_BLUECAT_ROOT_ZONE": "arg",
|
||||||
|
"EXTERNAL_DNS_BLUECAT_SKIP_TLS_VERIFY": "1",
|
||||||
"EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1",
|
"EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1",
|
||||||
"EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20",
|
"EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20",
|
||||||
"EXTERNAL_DNS_COREDNS_PREFIX": "/coredns/",
|
"EXTERNAL_DNS_COREDNS_PREFIX": "/coredns/",
|
||||||
@ -404,6 +438,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"EXTERNAL_DNS_AWS_API_RETRIES": "13",
|
"EXTERNAL_DNS_AWS_API_RETRIES": "13",
|
||||||
"EXTERNAL_DNS_AWS_PREFER_CNAME": "true",
|
"EXTERNAL_DNS_AWS_PREFER_CNAME": "true",
|
||||||
"EXTERNAL_DNS_AWS_ZONES_CACHE_DURATION": "10s",
|
"EXTERNAL_DNS_AWS_ZONES_CACHE_DURATION": "10s",
|
||||||
|
"EXTERNAL_DNS_AWS_SD_SERVICE_CLEANUP": "true",
|
||||||
"EXTERNAL_DNS_POLICY": "upsert-only",
|
"EXTERNAL_DNS_POLICY": "upsert-only",
|
||||||
"EXTERNAL_DNS_REGISTRY": "noop",
|
"EXTERNAL_DNS_REGISTRY": "noop",
|
||||||
"EXTERNAL_DNS_TXT_OWNER_ID": "owner-1",
|
"EXTERNAL_DNS_TXT_OWNER_ID": "owner-1",
|
||||||
@ -429,6 +464,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"EXTERNAL_DNS_TRANSIP_ACCOUNT": "transip",
|
"EXTERNAL_DNS_TRANSIP_ACCOUNT": "transip",
|
||||||
"EXTERNAL_DNS_TRANSIP_KEYFILE": "/path/to/transip.key",
|
"EXTERNAL_DNS_TRANSIP_KEYFILE": "/path/to/transip.key",
|
||||||
"EXTERNAL_DNS_DIGITALOCEAN_API_PAGE_SIZE": "100",
|
"EXTERNAL_DNS_DIGITALOCEAN_API_PAGE_SIZE": "100",
|
||||||
|
"EXTERNAL_DNS_MANAGED_RECORD_TYPES": "A\nCNAME\nNS",
|
||||||
"EXTERNAL_DNS_RFC2136_BATCH_CHANGE_SIZE": "100",
|
"EXTERNAL_DNS_RFC2136_BATCH_CHANGE_SIZE": "100",
|
||||||
},
|
},
|
||||||
expected: overriddenConfig,
|
expected: overriddenConfig,
|
||||||
|
@ -223,7 +223,7 @@ func (p AkamaiProvider) Records(context.Context) (endpoints []*endpoint.Endpoint
|
|||||||
return endpoints, err
|
return endpoints, err
|
||||||
}
|
}
|
||||||
for _, zone := range zones.Zones {
|
for _, zone := range zones.Zones {
|
||||||
recordsets, err := p.client.GetRecordsets(zone.Zone, dns.RecordsetQueryArgs{})
|
recordsets, err := p.client.GetRecordsets(zone.Zone, dns.RecordsetQueryArgs{ShowAll: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Recordsets retrieval for zone: '%s' failed! %s", zone.Zone, err.Error())
|
log.Errorf("Recordsets retrieval for zone: '%s' failed! %s", zone.Zone, err.Error())
|
||||||
continue
|
continue
|
||||||
@ -242,7 +242,7 @@ func (p AkamaiProvider) Records(context.Context) (endpoints []*endpoint.Endpoint
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var temp interface{} = int64(recordset.TTL)
|
var temp interface{} = int64(recordset.TTL)
|
||||||
var ttl endpoint.TTL = endpoint.TTL(temp.(int64))
|
var ttl = endpoint.TTL(temp.(int64))
|
||||||
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(recordset.Name,
|
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(recordset.Name,
|
||||||
recordset.Type,
|
recordset.Type,
|
||||||
ttl,
|
ttl,
|
||||||
|
@ -94,7 +94,7 @@ func (m *MockAlibabaCloudDNSAPI) UpdateDomainRecord(request *alidns.UpdateDomain
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockAlibabaCloudDNSAPI) DescribeDomains(request *alidns.DescribeDomainsRequest) (response *alidns.DescribeDomainsResponse, err error) {
|
func (m *MockAlibabaCloudDNSAPI) DescribeDomains(request *alidns.DescribeDomainsRequest) (response *alidns.DescribeDomainsResponse, err error) {
|
||||||
var result alidns.Domains
|
var result alidns.DomainsInDescribeDomains
|
||||||
for _, record := range m.records {
|
for _, record := range m.records {
|
||||||
domain := alidns.Domain{}
|
domain := alidns.Domain{}
|
||||||
domain.DomainName = record.DomainName
|
domain.DomainName = record.DomainName
|
||||||
@ -209,7 +209,7 @@ func (m *MockAlibabaCloudPrivateZoneAPI) DescribeZoneInfo(request *pvtz.Describe
|
|||||||
response = pvtz.CreateDescribeZoneInfoResponse()
|
response = pvtz.CreateDescribeZoneInfoResponse()
|
||||||
response.ZoneId = m.zone.ZoneId
|
response.ZoneId = m.zone.ZoneId
|
||||||
response.ZoneName = m.zone.ZoneName
|
response.ZoneName = m.zone.ZoneName
|
||||||
response.BindVpcs = pvtz.BindVpcs{Vpc: m.zone.Vpcs.Vpc}
|
response.BindVpcs = pvtz.BindVpcsInDescribeZoneInfo{Vpc: m.zone.Vpcs.Vpc}
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +119,8 @@ var (
|
|||||||
"elb.af-south-1.amazonaws.com": "Z203XCE67M25HM",
|
"elb.af-south-1.amazonaws.com": "Z203XCE67M25HM",
|
||||||
// Global Accelerator
|
// Global Accelerator
|
||||||
"awsglobalaccelerator.com": "Z2BJ6XQ5FK7U4H",
|
"awsglobalaccelerator.com": "Z2BJ6XQ5FK7U4H",
|
||||||
|
// Cloudfront
|
||||||
|
"cloudfront.net": "Z2FDTNDATAQYW2",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -290,10 +292,7 @@ func (p *AWSProvider) Zones(ctx context.Context) (map[string]*route53.HostedZone
|
|||||||
// wildcardUnescape converts \\052.abc back to *.abc
|
// wildcardUnescape converts \\052.abc back to *.abc
|
||||||
// Route53 stores wildcards escaped: http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html?shortFooter=true#domain-name-format-asterisk
|
// Route53 stores wildcards escaped: http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DomainNameFormat.html?shortFooter=true#domain-name-format-asterisk
|
||||||
func wildcardUnescape(s string) string {
|
func wildcardUnescape(s string) string {
|
||||||
if strings.Contains(s, "\\052") {
|
return strings.Replace(s, "\\052", "*", 1)
|
||||||
s = strings.Replace(s, "\\052", "*", 1)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Records returns the list of records in a given hosted zone.
|
// Records returns the list of records in a given hosted zone.
|
||||||
@ -312,9 +311,6 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*route53.Hos
|
|||||||
for _, r := range resp.ResourceRecordSets {
|
for _, r := range resp.ResourceRecordSets {
|
||||||
newEndpoints := make([]*endpoint.Endpoint, 0)
|
newEndpoints := make([]*endpoint.Endpoint, 0)
|
||||||
|
|
||||||
// TODO(linki, ownership): Remove once ownership system is in place.
|
|
||||||
// See: https://github.com/kubernetes-sigs/external-dns/pull/122/files/74e2c3d3e237411e619aefc5aab694742001cdec#r109863370
|
|
||||||
|
|
||||||
if !provider.SupportedRecordType(aws.StringValue(r.Type)) {
|
if !provider.SupportedRecordType(aws.StringValue(r.Type)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -889,8 +885,5 @@ func canonicalHostedZone(hostname string) string {
|
|||||||
|
|
||||||
// cleanZoneID removes the "/hostedzone/" prefix
|
// cleanZoneID removes the "/hostedzone/" prefix
|
||||||
func cleanZoneID(id string) string {
|
func cleanZoneID(id string) string {
|
||||||
if strings.HasPrefix(id, "/hostedzone/") {
|
return strings.TrimPrefix(id, "/hostedzone/")
|
||||||
id = strings.TrimPrefix(id, "/hostedzone/")
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
}
|
}
|
||||||
|
@ -64,12 +64,12 @@ var (
|
|||||||
type AWSSDClient interface {
|
type AWSSDClient interface {
|
||||||
CreateService(input *sd.CreateServiceInput) (*sd.CreateServiceOutput, error)
|
CreateService(input *sd.CreateServiceInput) (*sd.CreateServiceOutput, error)
|
||||||
DeregisterInstance(input *sd.DeregisterInstanceInput) (*sd.DeregisterInstanceOutput, error)
|
DeregisterInstance(input *sd.DeregisterInstanceInput) (*sd.DeregisterInstanceOutput, error)
|
||||||
GetService(input *sd.GetServiceInput) (*sd.GetServiceOutput, error)
|
|
||||||
ListInstancesPages(input *sd.ListInstancesInput, fn func(*sd.ListInstancesOutput, bool) bool) error
|
ListInstancesPages(input *sd.ListInstancesInput, fn func(*sd.ListInstancesOutput, bool) bool) error
|
||||||
ListNamespacesPages(input *sd.ListNamespacesInput, fn func(*sd.ListNamespacesOutput, bool) bool) error
|
ListNamespacesPages(input *sd.ListNamespacesInput, fn func(*sd.ListNamespacesOutput, bool) bool) error
|
||||||
ListServicesPages(input *sd.ListServicesInput, fn func(*sd.ListServicesOutput, bool) bool) error
|
ListServicesPages(input *sd.ListServicesInput, fn func(*sd.ListServicesOutput, bool) bool) error
|
||||||
RegisterInstance(input *sd.RegisterInstanceInput) (*sd.RegisterInstanceOutput, error)
|
RegisterInstance(input *sd.RegisterInstanceInput) (*sd.RegisterInstanceOutput, error)
|
||||||
UpdateService(input *sd.UpdateServiceInput) (*sd.UpdateServiceOutput, error)
|
UpdateService(input *sd.UpdateServiceInput) (*sd.UpdateServiceOutput, error)
|
||||||
|
DeleteService(input *sd.DeleteServiceInput) (*sd.DeleteServiceOutput, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AWSSDProvider is an implementation of Provider for AWS Cloud Map.
|
// AWSSDProvider is an implementation of Provider for AWS Cloud Map.
|
||||||
@ -81,10 +81,14 @@ type AWSSDProvider struct {
|
|||||||
namespaceFilter endpoint.DomainFilter
|
namespaceFilter endpoint.DomainFilter
|
||||||
// filter namespace by type (private or public)
|
// filter namespace by type (private or public)
|
||||||
namespaceTypeFilter *sd.NamespaceFilter
|
namespaceTypeFilter *sd.NamespaceFilter
|
||||||
|
// enables service without instances cleanup
|
||||||
|
cleanEmptyService bool
|
||||||
|
// filter services for removal
|
||||||
|
ownerID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAWSSDProvider initializes a new AWS Cloud Map based Provider.
|
// NewAWSSDProvider initializes a new AWS Cloud Map based Provider.
|
||||||
func NewAWSSDProvider(domainFilter endpoint.DomainFilter, namespaceType string, assumeRole string, dryRun bool) (*AWSSDProvider, error) {
|
func NewAWSSDProvider(domainFilter endpoint.DomainFilter, namespaceType string, assumeRole string, dryRun, cleanEmptyService bool, ownerID string) (*AWSSDProvider, error) {
|
||||||
config := aws.NewConfig()
|
config := aws.NewConfig()
|
||||||
|
|
||||||
config = config.WithHTTPClient(
|
config = config.WithHTTPClient(
|
||||||
@ -113,9 +117,11 @@ func NewAWSSDProvider(domainFilter endpoint.DomainFilter, namespaceType string,
|
|||||||
|
|
||||||
provider := &AWSSDProvider{
|
provider := &AWSSDProvider{
|
||||||
client: sd.New(sess),
|
client: sd.New(sess),
|
||||||
|
dryRun: dryRun,
|
||||||
namespaceFilter: domainFilter,
|
namespaceFilter: domainFilter,
|
||||||
namespaceTypeFilter: newSdNamespaceFilter(namespaceType),
|
namespaceTypeFilter: newSdNamespaceFilter(namespaceType),
|
||||||
dryRun: dryRun,
|
cleanEmptyService: cleanEmptyService,
|
||||||
|
ownerID: ownerID,
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider, nil
|
return provider, nil
|
||||||
@ -162,6 +168,12 @@ func (p *AWSSDProvider) Records(ctx context.Context) (endpoints []*endpoint.Endp
|
|||||||
ep := p.instancesToEndpoint(ns, srv, instances)
|
ep := p.instancesToEndpoint(ns, srv, instances)
|
||||||
endpoints = append(endpoints, ep)
|
endpoints = append(endpoints, ep)
|
||||||
}
|
}
|
||||||
|
if len(instances) == 0 {
|
||||||
|
err = p.DeleteService(srv)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to delete service \"%s\", error: %s", aws.StringValue(srv.Name), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,9 +297,8 @@ func (p *AWSSDProvider) submitCreates(namespaces []*sd.NamespaceSummary, changes
|
|||||||
}
|
}
|
||||||
// update local list of services
|
// update local list of services
|
||||||
services[*srv.Name] = srv
|
services[*srv.Name] = srv
|
||||||
} else if (ch.RecordTTL.IsConfigured() && *srv.DnsConfig.DnsRecords[0].TTL != int64(ch.RecordTTL)) ||
|
} else if ch.RecordTTL.IsConfigured() && *srv.DnsConfig.DnsRecords[0].TTL != int64(ch.RecordTTL) {
|
||||||
aws.StringValue(srv.Description) != ch.Labels[endpoint.AWSSDDescriptionLabel] {
|
// update service when TTL differ
|
||||||
// update service when TTL or Description differ
|
|
||||||
err = p.UpdateService(srv, ch)
|
err = p.UpdateService(srv, ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -359,13 +370,10 @@ func (p *AWSSDProvider) ListNamespaces() ([]*sd.NamespaceSummary, error) {
|
|||||||
|
|
||||||
// ListServicesByNamespaceID returns list of services in given namespace. Returns map[srv_name]*sd.Service
|
// ListServicesByNamespaceID returns list of services in given namespace. Returns map[srv_name]*sd.Service
|
||||||
func (p *AWSSDProvider) ListServicesByNamespaceID(namespaceID *string) (map[string]*sd.Service, error) {
|
func (p *AWSSDProvider) ListServicesByNamespaceID(namespaceID *string) (map[string]*sd.Service, error) {
|
||||||
serviceIds := make([]*string, 0)
|
services := make([]*sd.ServiceSummary, 0)
|
||||||
|
|
||||||
f := func(resp *sd.ListServicesOutput, lastPage bool) bool {
|
f := func(resp *sd.ListServicesOutput, lastPage bool) bool {
|
||||||
for _, srv := range resp.Services {
|
services = append(services, resp.Services...)
|
||||||
serviceIds = append(serviceIds, srv.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,35 +382,31 @@ func (p *AWSSDProvider) ListServicesByNamespaceID(namespaceID *string) (map[stri
|
|||||||
Name: aws.String(sd.ServiceFilterNameNamespaceId),
|
Name: aws.String(sd.ServiceFilterNameNamespaceId),
|
||||||
Values: []*string{namespaceID},
|
Values: []*string{namespaceID},
|
||||||
}},
|
}},
|
||||||
|
MaxResults: aws.Int64(100),
|
||||||
}, f)
|
}, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get detail of each listed service
|
servicesMap := make(map[string]*sd.Service)
|
||||||
services := make(map[string]*sd.Service)
|
for _, serviceSummary := range services {
|
||||||
for _, serviceID := range serviceIds {
|
service := &sd.Service{
|
||||||
service, err := p.GetServiceDetail(serviceID)
|
Arn: serviceSummary.Arn,
|
||||||
if err != nil {
|
CreateDate: serviceSummary.CreateDate,
|
||||||
return nil, err
|
Description: serviceSummary.Description,
|
||||||
|
DnsConfig: serviceSummary.DnsConfig,
|
||||||
|
HealthCheckConfig: serviceSummary.HealthCheckConfig,
|
||||||
|
HealthCheckCustomConfig: serviceSummary.HealthCheckCustomConfig,
|
||||||
|
Id: serviceSummary.Id,
|
||||||
|
InstanceCount: serviceSummary.InstanceCount,
|
||||||
|
Name: serviceSummary.Name,
|
||||||
|
NamespaceId: namespaceID,
|
||||||
|
Type: serviceSummary.Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
services[aws.StringValue(service.Name)] = service
|
servicesMap[aws.StringValue(service.Name)] = service
|
||||||
}
|
}
|
||||||
|
return servicesMap, nil
|
||||||
return services, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServiceDetail returns detail of given service
|
|
||||||
func (p *AWSSDProvider) GetServiceDetail(serviceID *string) (*sd.Service, error) {
|
|
||||||
output, err := p.client.GetService(&sd.GetServiceInput{
|
|
||||||
Id: serviceID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.Service, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListInstancesByServiceID returns list of instances registered in given service.
|
// ListInstancesByServiceID returns list of instances registered in given service.
|
||||||
@ -491,6 +495,27 @@ func (p *AWSSDProvider) UpdateService(service *sd.Service, ep *endpoint.Endpoint
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteService deletes empty Service from AWS API if its owner id match
|
||||||
|
func (p *AWSSDProvider) DeleteService(service *sd.Service) error {
|
||||||
|
log.Debugf("Check if service \"%s\" owner id match and it can be deleted", *service.Name)
|
||||||
|
if !p.dryRun && p.cleanEmptyService {
|
||||||
|
// convert ownerID string to service description format
|
||||||
|
label := endpoint.NewLabels()
|
||||||
|
label[endpoint.OwnerLabelKey] = p.ownerID
|
||||||
|
label[endpoint.AWSSDDescriptionLabel] = label.Serialize(false)
|
||||||
|
|
||||||
|
if aws.StringValue(service.Description) == label[endpoint.AWSSDDescriptionLabel] {
|
||||||
|
log.Infof("Deleting service \"%s\"", *service.Name)
|
||||||
|
_, err := p.client.DeleteService(&sd.DeleteServiceInput{
|
||||||
|
Id: aws.String(*service.Id),
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("Skipping service removal %s because owner id does not match, found: \"%s\", required: \"%s\"", aws.StringValue(service.Name), aws.StringValue(service.Description), label[endpoint.AWSSDDescriptionLabel])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterInstance creates a new instance in given service.
|
// RegisterInstance creates a new instance in given service.
|
||||||
func (p *AWSSDProvider) RegisterInstance(service *sd.Service, ep *endpoint.Endpoint) error {
|
func (p *AWSSDProvider) RegisterInstance(service *sd.Service, ep *endpoint.Endpoint) error {
|
||||||
for _, target := range ep.Targets {
|
for _, target := range ep.Targets {
|
||||||
@ -578,11 +603,16 @@ func serviceToServiceSummary(service *sd.Service) *sd.ServiceSummary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &sd.ServiceSummary{
|
return &sd.ServiceSummary{
|
||||||
Name: service.Name,
|
|
||||||
Id: service.Id,
|
|
||||||
Arn: service.Arn,
|
Arn: service.Arn,
|
||||||
|
CreateDate: service.CreateDate,
|
||||||
Description: service.Description,
|
Description: service.Description,
|
||||||
|
DnsConfig: service.DnsConfig,
|
||||||
|
HealthCheckConfig: service.HealthCheckConfig,
|
||||||
|
HealthCheckCustomConfig: service.HealthCheckCustomConfig,
|
||||||
|
Id: service.Id,
|
||||||
InstanceCount: service.InstanceCount,
|
InstanceCount: service.InstanceCount,
|
||||||
|
Name: service.Name,
|
||||||
|
Type: service.Type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,12 +181,27 @@ func (s *AWSSDClientStub) UpdateService(input *sd.UpdateServiceInput) (*sd.Updat
|
|||||||
return &sd.UpdateServiceOutput{}, nil
|
return &sd.UpdateServiceOutput{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestAWSSDProvider(api AWSSDClient, domainFilter endpoint.DomainFilter, namespaceTypeFilter string) *AWSSDProvider {
|
func (s *AWSSDClientStub) DeleteService(input *sd.DeleteServiceInput) (*sd.DeleteServiceOutput, error) {
|
||||||
|
out, err := s.GetService(&sd.GetServiceInput{Id: input.Id})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
service := out.Service
|
||||||
|
namespace := s.services[*service.NamespaceId]
|
||||||
|
delete(namespace, *input.Id)
|
||||||
|
|
||||||
|
return &sd.DeleteServiceOutput{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestAWSSDProvider(api AWSSDClient, domainFilter endpoint.DomainFilter, namespaceTypeFilter, ownerID string) *AWSSDProvider {
|
||||||
return &AWSSDProvider{
|
return &AWSSDProvider{
|
||||||
client: api,
|
client: api,
|
||||||
|
dryRun: false,
|
||||||
namespaceFilter: domainFilter,
|
namespaceFilter: domainFilter,
|
||||||
namespaceTypeFilter: newSdNamespaceFilter(namespaceTypeFilter),
|
namespaceTypeFilter: newSdNamespaceFilter(namespaceTypeFilter),
|
||||||
dryRun: false,
|
cleanEmptyService: true,
|
||||||
|
ownerID: ownerID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +303,7 @@ func TestAWSSDProvider_Records(t *testing.T) {
|
|||||||
instances: instances,
|
instances: instances,
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "")
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
|
||||||
|
|
||||||
endpoints, _ := provider.Records(context.Background())
|
endpoints, _ := provider.Records(context.Background())
|
||||||
|
|
||||||
@ -316,7 +331,7 @@ func TestAWSSDProvider_ApplyChanges(t *testing.T) {
|
|||||||
{DNSName: "service3.private.com", Targets: endpoint.Targets{"cname.target.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 100},
|
{DNSName: "service3.private.com", Targets: endpoint.Targets{"cname.target.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 100},
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "")
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
@ -376,7 +391,7 @@ func TestAWSSDProvider_ListNamespaces(t *testing.T) {
|
|||||||
{"domain filter", endpoint.NewDomainFilter([]string{"public.com"}), "", []*sd.NamespaceSummary{namespaceToNamespaceSummary(namespaces["public"])}},
|
{"domain filter", endpoint.NewDomainFilter([]string{"public.com"}), "", []*sd.NamespaceSummary{namespaceToNamespaceSummary(namespaces["public"])}},
|
||||||
{"non-existing domain", endpoint.NewDomainFilter([]string{"xxx.com"}), "", []*sd.NamespaceSummary{}},
|
{"non-existing domain", endpoint.NewDomainFilter([]string{"xxx.com"}), "", []*sd.NamespaceSummary{}},
|
||||||
} {
|
} {
|
||||||
provider := newTestAWSSDProvider(api, tc.domainFilter, tc.namespaceTypeFilter)
|
provider := newTestAWSSDProvider(api, tc.domainFilter, tc.namespaceTypeFilter, "")
|
||||||
|
|
||||||
result, err := provider.ListNamespaces()
|
result, err := provider.ListNamespaces()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -415,16 +430,19 @@ func TestAWSSDProvider_ListServicesByNamespace(t *testing.T) {
|
|||||||
"srv1": {
|
"srv1": {
|
||||||
Id: aws.String("srv1"),
|
Id: aws.String("srv1"),
|
||||||
Name: aws.String("service1"),
|
Name: aws.String("service1"),
|
||||||
|
NamespaceId: aws.String("private"),
|
||||||
},
|
},
|
||||||
"srv2": {
|
"srv2": {
|
||||||
Id: aws.String("srv2"),
|
Id: aws.String("srv2"),
|
||||||
Name: aws.String("service2"),
|
Name: aws.String("service2"),
|
||||||
|
NamespaceId: aws.String("private"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"public": {
|
"public": {
|
||||||
"srv3": {
|
"srv3": {
|
||||||
Id: aws.String("srv3"),
|
Id: aws.String("srv3"),
|
||||||
Name: aws.String("service3"),
|
Name: aws.String("service3"),
|
||||||
|
NamespaceId: aws.String("public"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -439,14 +457,11 @@ func TestAWSSDProvider_ListServicesByNamespace(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{map[string]*sd.Service{"service1": services["private"]["srv1"], "service2": services["private"]["srv2"]}},
|
{map[string]*sd.Service{"service1": services["private"]["srv1"], "service2": services["private"]["srv2"]}},
|
||||||
} {
|
} {
|
||||||
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "")
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
|
||||||
|
|
||||||
result, err := provider.ListServicesByNamespaceID(namespaces["private"].Id)
|
result, err := provider.ListServicesByNamespaceID(namespaces["private"].Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.expectedServices, result)
|
||||||
if !reflect.DeepEqual(result, tc.expectedServices) {
|
|
||||||
t.Errorf("AWSSDProvider.ListServicesByNamespaceID() error = %v, wantErr %v", result, tc.expectedServices)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,7 +510,7 @@ func TestAWSSDProvider_ListInstancesByService(t *testing.T) {
|
|||||||
instances: instances,
|
instances: instances,
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "")
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
|
||||||
|
|
||||||
result, err := provider.ListInstancesByServiceID(services["private"]["srv1"].Id)
|
result, err := provider.ListInstancesByServiceID(services["private"]["srv1"].Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -532,7 +547,7 @@ func TestAWSSDProvider_CreateService(t *testing.T) {
|
|||||||
|
|
||||||
expectedServices := make(map[string]*sd.Service)
|
expectedServices := make(map[string]*sd.Service)
|
||||||
|
|
||||||
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "")
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
|
||||||
|
|
||||||
// A type
|
// A type
|
||||||
provider.CreateService(aws.String("private"), aws.String("A-srv"), &endpoint.Endpoint{
|
provider.CreateService(aws.String("private"), aws.String("A-srv"), &endpoint.Endpoint{
|
||||||
@ -636,7 +651,7 @@ func TestAWSSDProvider_UpdateService(t *testing.T) {
|
|||||||
services: services,
|
services: services,
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "")
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
|
||||||
|
|
||||||
// update service with different TTL
|
// update service with different TTL
|
||||||
provider.UpdateService(services["private"]["srv1"], &endpoint.Endpoint{
|
provider.UpdateService(services["private"]["srv1"], &endpoint.Endpoint{
|
||||||
@ -647,6 +662,56 @@ func TestAWSSDProvider_UpdateService(t *testing.T) {
|
|||||||
assert.Equal(t, int64(100), *api.services["private"]["srv1"].DnsConfig.DnsRecords[0].TTL)
|
assert.Equal(t, int64(100), *api.services["private"]["srv1"].DnsConfig.DnsRecords[0].TTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAWSSDProvider_DeleteService(t *testing.T) {
|
||||||
|
namespaces := map[string]*sd.Namespace{
|
||||||
|
"private": {
|
||||||
|
Id: aws.String("private"),
|
||||||
|
Name: aws.String("private.com"),
|
||||||
|
Type: aws.String(sd.NamespaceTypeDnsPrivate),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
services := map[string]map[string]*sd.Service{
|
||||||
|
"private": {
|
||||||
|
"srv1": {
|
||||||
|
Id: aws.String("srv1"),
|
||||||
|
Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"),
|
||||||
|
Name: aws.String("service1"),
|
||||||
|
NamespaceId: aws.String("private"),
|
||||||
|
},
|
||||||
|
"srv2": {
|
||||||
|
Id: aws.String("srv2"),
|
||||||
|
Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"),
|
||||||
|
Name: aws.String("service2"),
|
||||||
|
NamespaceId: aws.String("private"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
api := &AWSSDClientStub{
|
||||||
|
namespaces: namespaces,
|
||||||
|
services: services,
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "owner-id")
|
||||||
|
|
||||||
|
// delete fist service
|
||||||
|
err := provider.DeleteService(services["private"]["srv1"])
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, api.services["private"], 1)
|
||||||
|
|
||||||
|
expectedServices := map[string]*sd.Service{
|
||||||
|
"srv2": {
|
||||||
|
Id: aws.String("srv2"),
|
||||||
|
Description: aws.String("heritage=external-dns,external-dns/owner=owner-id"),
|
||||||
|
Name: aws.String("service2"),
|
||||||
|
NamespaceId: aws.String("private"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, expectedServices, api.services["private"])
|
||||||
|
}
|
||||||
|
|
||||||
func TestAWSSDProvider_RegisterInstance(t *testing.T) {
|
func TestAWSSDProvider_RegisterInstance(t *testing.T) {
|
||||||
namespaces := map[string]*sd.Namespace{
|
namespaces := map[string]*sd.Namespace{
|
||||||
"private": {
|
"private": {
|
||||||
@ -703,7 +768,7 @@ func TestAWSSDProvider_RegisterInstance(t *testing.T) {
|
|||||||
instances: make(map[string]map[string]*sd.Instance),
|
instances: make(map[string]map[string]*sd.Instance),
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "")
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
|
||||||
|
|
||||||
expectedInstances := make(map[string]*sd.Instance)
|
expectedInstances := make(map[string]*sd.Instance)
|
||||||
|
|
||||||
@ -820,7 +885,7 @@ func TestAWSSDProvider_DeregisterInstance(t *testing.T) {
|
|||||||
instances: instances,
|
instances: instances,
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "")
|
provider := newTestAWSSDProvider(api, endpoint.NewDomainFilter([]string{}), "", "")
|
||||||
|
|
||||||
provider.DeregisterInstance(services["private"]["srv1"], endpoint.NewEndpoint("srv1.private.com.", endpoint.RecordTypeA, "1.2.3.4"))
|
provider.DeregisterInstance(services["private"]["srv1"], endpoint.NewEndpoint("srv1.private.com.", endpoint.RecordTypeA, "1.2.3.4"))
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ func newMockedAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneID
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockZoneListResultPage := privatedns.NewPrivateZoneListResultPage(pageIterator.getNextPage)
|
mockZoneListResultPage := privatedns.NewPrivateZoneListResultPage(privatedns.PrivateZoneListResult{}, pageIterator.getNextPage)
|
||||||
mockZoneClientIterator := privatedns.NewPrivateZoneListResultIterator(mockZoneListResultPage)
|
mockZoneClientIterator := privatedns.NewPrivateZoneListResultIterator(mockZoneListResultPage)
|
||||||
zonesClient := mockPrivateZonesClient{
|
zonesClient := mockPrivateZonesClient{
|
||||||
mockZonesClientIterator: &mockZoneClientIterator,
|
mockZonesClientIterator: &mockZoneClientIterator,
|
||||||
@ -230,7 +230,8 @@ func newMockedAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneID
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
mockRecordSetListResultPage := privatedns.NewRecordSetListResultPage(resultPageIterator.getNextPage)
|
|
||||||
|
mockRecordSetListResultPage := privatedns.NewRecordSetListResultPage(privatedns.RecordSetListResult{}, resultPageIterator.getNextPage)
|
||||||
mockRecordSetListIterator := privatedns.NewRecordSetListResultIterator(mockRecordSetListResultPage)
|
mockRecordSetListIterator := privatedns.NewRecordSetListResultIterator(mockRecordSetListResultPage)
|
||||||
recordSetsClient := mockPrivateRecordSetsClient{
|
recordSetsClient := mockPrivateRecordSetsClient{
|
||||||
mockRecordSetListIterator: &mockRecordSetListIterator,
|
mockRecordSetListIterator: &mockRecordSetListIterator,
|
||||||
@ -370,7 +371,7 @@ func testAzurePrivateDNSApplyChangesInternal(t *testing.T, dryRun bool, client P
|
|||||||
zlr,
|
zlr,
|
||||||
}
|
}
|
||||||
|
|
||||||
mockZoneListResultPage := privatedns.NewPrivateZoneListResultPage(func(ctxParam context.Context, zlrParam privatedns.PrivateZoneListResult) (privatedns.PrivateZoneListResult, error) {
|
mockZoneListResultPage := privatedns.NewPrivateZoneListResultPage(privatedns.PrivateZoneListResult{}, func(ctxParam context.Context, zlrParam privatedns.PrivateZoneListResult) (privatedns.PrivateZoneListResult, error) {
|
||||||
if len(results) > 0 {
|
if len(results) > 0 {
|
||||||
result := results[0]
|
result := results[0]
|
||||||
results = nil
|
results = nil
|
||||||
|
@ -215,7 +215,7 @@ func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter e
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockZoneListResultPage := dns.NewZoneListResultPage(pageIterator.getNextPage)
|
mockZoneListResultPage := dns.NewZoneListResultPage(dns.ZoneListResult{}, pageIterator.getNextPage)
|
||||||
mockZoneClientIterator := dns.NewZoneListResultIterator(mockZoneListResultPage)
|
mockZoneClientIterator := dns.NewZoneListResultIterator(mockZoneListResultPage)
|
||||||
zonesClient := mockZonesClient{
|
zonesClient := mockZonesClient{
|
||||||
mockZonesClientIterator: &mockZoneClientIterator,
|
mockZonesClientIterator: &mockZoneClientIterator,
|
||||||
@ -229,7 +229,8 @@ func newMockedAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter e
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
mockRecordSetListResultPage := dns.NewRecordSetListResultPage(resultPageIterator.getNextPage)
|
|
||||||
|
mockRecordSetListResultPage := dns.NewRecordSetListResultPage(dns.RecordSetListResult{}, resultPageIterator.getNextPage)
|
||||||
mockRecordSetListIterator := dns.NewRecordSetListResultIterator(mockRecordSetListResultPage)
|
mockRecordSetListIterator := dns.NewRecordSetListResultIterator(mockRecordSetListResultPage)
|
||||||
recordSetsClient := mockRecordSetsClient{
|
recordSetsClient := mockRecordSetsClient{
|
||||||
mockRecordSetListIterator: &mockRecordSetListIterator,
|
mockRecordSetListIterator: &mockRecordSetListIterator,
|
||||||
@ -377,7 +378,7 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
|
|||||||
zlr,
|
zlr,
|
||||||
}
|
}
|
||||||
|
|
||||||
mockZoneListResultPage := dns.NewZoneListResultPage(func(ctxParam context.Context, zlrParam dns.ZoneListResult) (dns.ZoneListResult, error) {
|
mockZoneListResultPage := dns.NewZoneListResultPage(dns.ZoneListResult{}, func(ctxParam context.Context, zlrParam dns.ZoneListResult) (dns.ZoneListResult, error) {
|
||||||
if len(results) > 0 {
|
if len(results) > 0 {
|
||||||
result := results[0]
|
result := results[0]
|
||||||
results = nil
|
results = nil
|
||||||
@ -512,7 +513,7 @@ func testAzureApplyChangesInternalZoneName(t *testing.T, dryRun bool, client Rec
|
|||||||
zlr,
|
zlr,
|
||||||
}
|
}
|
||||||
|
|
||||||
mockZoneListResultPage := dns.NewZoneListResultPage(func(ctxParam context.Context, zlrParam dns.ZoneListResult) (dns.ZoneListResult, error) {
|
mockZoneListResultPage := dns.NewZoneListResultPage(dns.ZoneListResult{}, func(ctxParam context.Context, zlrParam dns.ZoneListResult) (dns.ZoneListResult, error) {
|
||||||
if len(results) > 0 {
|
if len(results) > 0 {
|
||||||
result := results[0]
|
result := results[0]
|
||||||
results = nil
|
results = nil
|
||||||
|
@ -44,6 +44,8 @@ type bluecatConfig struct {
|
|||||||
GatewayUsername string `json:"gatewayUsername,omitempty"`
|
GatewayUsername string `json:"gatewayUsername,omitempty"`
|
||||||
GatewayPassword string `json:"gatewayPassword,omitempty"`
|
GatewayPassword string `json:"gatewayPassword,omitempty"`
|
||||||
DNSConfiguration string `json:"dnsConfiguration"`
|
DNSConfiguration string `json:"dnsConfiguration"`
|
||||||
|
DNSServerName string `json:"dnsServerName"`
|
||||||
|
DNSDeployType string `json:"dnsDeployType"`
|
||||||
View string `json:"dnsView"`
|
View string `json:"dnsView"`
|
||||||
RootZone string `json:"rootZone"`
|
RootZone string `json:"rootZone"`
|
||||||
SkipTLSVerify bool `json:"skipTLSVerify"`
|
SkipTLSVerify bool `json:"skipTLSVerify"`
|
||||||
@ -57,6 +59,8 @@ type BluecatProvider struct {
|
|||||||
dryRun bool
|
dryRun bool
|
||||||
RootZone string
|
RootZone string
|
||||||
DNSConfiguration string
|
DNSConfiguration string
|
||||||
|
DNSServerName string
|
||||||
|
DNSDeployType string
|
||||||
View string
|
View string
|
||||||
gatewayClient GatewayClient
|
gatewayClient GatewayClient
|
||||||
}
|
}
|
||||||
@ -76,6 +80,7 @@ type GatewayClient interface {
|
|||||||
getTXTRecord(name string, record *BluecatTXTRecord) error
|
getTXTRecord(name string, record *BluecatTXTRecord) error
|
||||||
createTXTRecord(zone string, req *bluecatCreateTXTRecordRequest) (res interface{}, err error)
|
createTXTRecord(zone string, req *bluecatCreateTXTRecordRequest) (res interface{}, err error)
|
||||||
deleteTXTRecord(name string, zone string) error
|
deleteTXTRecord(name string, zone string) error
|
||||||
|
serverFullDeploy() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GatewayClientConfig defines new client on bluecat gateway
|
// GatewayClientConfig defines new client on bluecat gateway
|
||||||
@ -86,6 +91,7 @@ type GatewayClientConfig struct {
|
|||||||
DNSConfiguration string
|
DNSConfiguration string
|
||||||
View string
|
View string
|
||||||
RootZone string
|
RootZone string
|
||||||
|
DNSServerName string
|
||||||
SkipTLSVerify bool
|
SkipTLSVerify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,26 +150,48 @@ type bluecatCreateTXTRecordRequest struct {
|
|||||||
Text string `json:"txt"`
|
Text string `json:"txt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bluecatServerFullDeployRequest struct {
|
||||||
|
ServerName string `json:"server_name"`
|
||||||
|
}
|
||||||
|
|
||||||
// NewBluecatProvider creates a new Bluecat provider.
|
// NewBluecatProvider creates a new Bluecat provider.
|
||||||
//
|
//
|
||||||
// Returns a pointer to the provider or an error if a provider could not be created.
|
// Returns a pointer to the provider or an error if a provider could not be created.
|
||||||
func NewBluecatProvider(configFile string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool) (*BluecatProvider, error) {
|
func NewBluecatProvider(configFile, dnsConfiguration, dnsServerName, dnsDeployType, dnsView, gatewayHost, rootZone string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun, skipTLSVerify bool) (*BluecatProvider, error) {
|
||||||
contents, err := ioutil.ReadFile(configFile)
|
cfg := bluecatConfig{}
|
||||||
|
contents, err := os.ReadFile(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
cfg = bluecatConfig{
|
||||||
|
GatewayHost: gatewayHost,
|
||||||
|
DNSConfiguration: dnsConfiguration,
|
||||||
|
DNSServerName: dnsServerName,
|
||||||
|
DNSDeployType: dnsDeployType,
|
||||||
|
View: dnsView,
|
||||||
|
RootZone: rootZone,
|
||||||
|
SkipTLSVerify: skipTLSVerify,
|
||||||
|
GatewayUsername: "",
|
||||||
|
GatewayPassword: "",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return nil, errors.Wrapf(err, "failed to read Bluecat config file %v", configFile)
|
return nil, errors.Wrapf(err, "failed to read Bluecat config file %v", configFile)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
cfg := bluecatConfig{}
|
|
||||||
err = json.Unmarshal(contents, &cfg)
|
err = json.Unmarshal(contents, &cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to read Bluecat config file %v", configFile)
|
return nil, errors.Wrapf(err, "failed to parse Bluecat JSON config file %v", configFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isValidDNSDeployType(cfg.DNSDeployType) {
|
||||||
|
return nil, errors.Errorf("%v is not a valid deployment type", cfg.DNSDeployType)
|
||||||
}
|
}
|
||||||
|
|
||||||
token, cookie, err := getBluecatGatewayToken(cfg)
|
token, cookie, err := getBluecatGatewayToken(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to get API token from Bluecat Gateway")
|
return nil, errors.Wrap(err, "failed to get API token from Bluecat Gateway")
|
||||||
}
|
}
|
||||||
gatewayClient := NewGatewayClient(cookie, token, cfg.GatewayHost, cfg.DNSConfiguration, cfg.View, cfg.RootZone, cfg.SkipTLSVerify)
|
gatewayClient := NewGatewayClient(cookie, token, cfg.GatewayHost, cfg.DNSConfiguration, cfg.View, cfg.RootZone, cfg.DNSServerName, cfg.SkipTLSVerify)
|
||||||
|
|
||||||
provider := &BluecatProvider{
|
provider := &BluecatProvider{
|
||||||
domainFilter: domainFilter,
|
domainFilter: domainFilter,
|
||||||
@ -171,6 +199,8 @@ func NewBluecatProvider(configFile string, domainFilter endpoint.DomainFilter, z
|
|||||||
dryRun: dryRun,
|
dryRun: dryRun,
|
||||||
gatewayClient: gatewayClient,
|
gatewayClient: gatewayClient,
|
||||||
DNSConfiguration: cfg.DNSConfiguration,
|
DNSConfiguration: cfg.DNSConfiguration,
|
||||||
|
DNSServerName: cfg.DNSServerName,
|
||||||
|
DNSDeployType: cfg.DNSDeployType,
|
||||||
View: cfg.View,
|
View: cfg.View,
|
||||||
RootZone: cfg.RootZone,
|
RootZone: cfg.RootZone,
|
||||||
}
|
}
|
||||||
@ -178,7 +208,9 @@ func NewBluecatProvider(configFile string, domainFilter endpoint.DomainFilter, z
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewGatewayClient creates and returns a new Bluecat gateway client
|
// NewGatewayClient creates and returns a new Bluecat gateway client
|
||||||
func NewGatewayClient(cookie http.Cookie, token, gatewayHost, dnsConfiguration, view, rootZone string, skipTLSVerify bool) GatewayClientConfig {
|
func NewGatewayClient(cookie http.Cookie, token, gatewayHost, dnsConfiguration, view, rootZone, dnsServerName string, skipTLSVerify bool) GatewayClientConfig {
|
||||||
|
// TODO: do not handle defaulting here
|
||||||
|
//
|
||||||
// Right now the Bluecat gateway doesn't seem to have a way to get the root zone from the API. If the user
|
// Right now the Bluecat gateway doesn't seem to have a way to get the root zone from the API. If the user
|
||||||
// doesn't provide one via the config file we'll assume it's 'com'
|
// doesn't provide one via the config file we'll assume it's 'com'
|
||||||
if rootZone == "" {
|
if rootZone == "" {
|
||||||
@ -189,6 +221,7 @@ func NewGatewayClient(cookie http.Cookie, token, gatewayHost, dnsConfiguration,
|
|||||||
Token: token,
|
Token: token,
|
||||||
Host: gatewayHost,
|
Host: gatewayHost,
|
||||||
DNSConfiguration: dnsConfiguration,
|
DNSConfiguration: dnsConfiguration,
|
||||||
|
DNSServerName: dnsServerName,
|
||||||
View: view,
|
View: view,
|
||||||
RootZone: rootZone,
|
RootZone: rootZone,
|
||||||
SkipTLSVerify: skipTLSVerify,
|
SkipTLSVerify: skipTLSVerify,
|
||||||
@ -278,8 +311,6 @@ func (p *BluecatProvider) Records(ctx context.Context) (endpoints []*endpoint.En
|
|||||||
}
|
}
|
||||||
endpoints = append(endpoints, ep)
|
endpoints = append(endpoints, ep)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add bluecat deploy API call here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("fetched %d records from Bluecat", len(endpoints))
|
log.Debugf("fetched %d records from Bluecat", len(endpoints))
|
||||||
@ -302,6 +333,20 @@ func (p *BluecatProvider) ApplyChanges(ctx context.Context, changes *plan.Change
|
|||||||
p.deleteRecords(deleted)
|
p.deleteRecords(deleted)
|
||||||
p.createRecords(created)
|
p.createRecords(created)
|
||||||
|
|
||||||
|
if p.DNSServerName != "" {
|
||||||
|
switch p.DNSDeployType {
|
||||||
|
case "full-deploy":
|
||||||
|
err := p.gatewayClient.serverFullDeploy()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case "no-deploy":
|
||||||
|
log.Debug("Not executing deploy because DNSDeployType is set to 'no-deploy'")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debug("Not executing deploy because server name was not provided")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -922,6 +967,41 @@ func (c GatewayClientConfig) deleteTXTRecord(name string, zone string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) serverFullDeploy() error {
|
||||||
|
log.Infof("Executing full deploy on server %s", c.DNSServerName)
|
||||||
|
httpClient := newHTTPClient(c.SkipTLSVerify)
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/server/full_deploy/"
|
||||||
|
requestBody := bluecatServerFullDeployRequest{
|
||||||
|
ServerName: c.DNSServerName,
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := json.Marshal(requestBody)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not marshal body for server full deploy")
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := c.buildHTTPRequest("POST", url, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Header.Add("Content-Type", "application/json")
|
||||||
|
response, err := httpClient.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error executing full deploy")
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode != http.StatusCreated {
|
||||||
|
responseBody, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to read full deploy response body")
|
||||||
|
}
|
||||||
|
return errors.Errorf("got HTTP response code %v, detailed message: %v", response.StatusCode, string(responseBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
//buildHTTPRequest builds a standard http Request and adds authentication headers required by Bluecat Gateway
|
//buildHTTPRequest builds a standard http Request and adds authentication headers required by Bluecat Gateway
|
||||||
func (c GatewayClientConfig) buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error) {
|
func (c GatewayClientConfig) buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error) {
|
||||||
req, err := http.NewRequest(method, url, body)
|
req, err := http.NewRequest(method, url, body)
|
||||||
@ -947,6 +1027,17 @@ func splitProperties(props string) map[string]string {
|
|||||||
return propMap
|
return propMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isValidDNSDeployType validates the deployment type provided by a users configuration is supported by the Bluecat Provider.
|
||||||
|
func isValidDNSDeployType(deployType string) bool {
|
||||||
|
validDNSDeployTypes := []string{"no-deploy", "full-deploy"}
|
||||||
|
for _, t := range validDNSDeployTypes {
|
||||||
|
if t == deployType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
//expandZone takes an absolute domain name such as 'example.com' and returns a zone hierarchy used by Bluecat Gateway,
|
//expandZone takes an absolute domain name such as 'example.com' and returns a zone hierarchy used by Bluecat Gateway,
|
||||||
//such as '/zones/com/zones/example/zones/'
|
//such as '/zones/com/zones/example/zones/'
|
||||||
func expandZone(zone string) string {
|
func expandZone(zone string) string {
|
||||||
|
@ -109,6 +109,9 @@ func (g mockGatewayClient) deleteTXTRecord(name string, zone string) error {
|
|||||||
*g.mockBluecatTXTs = nil
|
*g.mockBluecatTXTs = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (g mockGatewayClient) serverFullDeploy() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (g mockGatewayClient) buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error) {
|
func (g mockGatewayClient) buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error) {
|
||||||
request, _ := http.NewRequest("GET", fmt.Sprintf("%s/users", "http://some.com/api/v1"), nil)
|
request, _ := http.NewRequest("GET", fmt.Sprintf("%s/users", "http://some.com/api/v1"), nil)
|
||||||
@ -374,11 +377,12 @@ func TestBluecatNewGatewayClient(t *testing.T) {
|
|||||||
testToken := "exampleToken"
|
testToken := "exampleToken"
|
||||||
testgateWayHost := "exampleHost"
|
testgateWayHost := "exampleHost"
|
||||||
testDNSConfiguration := "exampleDNSConfiguration"
|
testDNSConfiguration := "exampleDNSConfiguration"
|
||||||
|
testDNSServer := "exampleServer"
|
||||||
testView := "testView"
|
testView := "testView"
|
||||||
testZone := "example.com"
|
testZone := "example.com"
|
||||||
testVerify := true
|
testVerify := true
|
||||||
|
|
||||||
client := NewGatewayClient(testCookie, testToken, testgateWayHost, testDNSConfiguration, testView, testZone, testVerify)
|
client := NewGatewayClient(testCookie, testToken, testgateWayHost, testDNSConfiguration, testView, testZone, testDNSServer, testVerify)
|
||||||
|
|
||||||
if client.Cookie.Value != testCookie.Value || client.Cookie.Name != testCookie.Name || client.Token != testToken || client.Host != testgateWayHost || client.DNSConfiguration != testDNSConfiguration || client.View != testView || client.RootZone != testZone || client.SkipTLSVerify != testVerify {
|
if client.Cookie.Value != testCookie.Value || client.Cookie.Name != testCookie.Name || client.Token != testToken || client.Host != testgateWayHost || client.DNSConfiguration != testDNSConfiguration || client.View != testView || client.RootZone != testZone || client.SkipTLSVerify != testVerify {
|
||||||
t.Fatal("Client values dont match")
|
t.Fatal("Client values dont match")
|
||||||
@ -475,6 +479,21 @@ func TestBluecatRecordset(t *testing.T) {
|
|||||||
assert.Equal(t, cnameActual.res, cnameExpected.res)
|
assert.Equal(t, cnameActual.res, cnameExpected.res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidDeployTypes(t *testing.T) {
|
||||||
|
validTypes := []string{"no-deploy", "full-deploy"}
|
||||||
|
invalidTypes := []string{"anything-else"}
|
||||||
|
for _, i := range validTypes {
|
||||||
|
if !isValidDNSDeployType(i) {
|
||||||
|
t.Fatalf("%s should be a valid deploy type", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, i := range invalidTypes {
|
||||||
|
if isValidDNSDeployType(i) {
|
||||||
|
t.Fatalf("%s should be a invalid deploy type", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func validateEndpoints(t *testing.T, actual, expected []*endpoint.Endpoint) {
|
func validateEndpoints(t *testing.T, actual, expected []*endpoint.Endpoint) {
|
||||||
assert.True(t, testutils.SameEndpoints(actual, expected), "actual and expected endpoints don't match. %s:%s", actual, expected)
|
assert.True(t, testutils.SameEndpoints(actual, expected), "actual and expected endpoints don't match. %s:%s", actual, expected)
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,10 @@ func (p *HetznerProvider) submitChanges(ctx context.Context, changes []*HetznerC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logMessage := "Changing record"
|
||||||
|
if p.DryRun {
|
||||||
|
logMessage = "Would change record"
|
||||||
|
}
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"id": change.ResourceRecordSet.ID,
|
"id": change.ResourceRecordSet.ID,
|
||||||
"record": change.ResourceRecordSet.Name,
|
"record": change.ResourceRecordSet.Name,
|
||||||
@ -150,7 +154,10 @@ func (p *HetznerProvider) submitChanges(ctx context.Context, changes []*HetznerC
|
|||||||
"action": change.Action,
|
"action": change.Action,
|
||||||
"zone": change.ZoneName,
|
"zone": change.ZoneName,
|
||||||
"zone_id": change.ZoneID,
|
"zone_id": change.ZoneID,
|
||||||
}).Info("Changing record")
|
}).Info(logMessage)
|
||||||
|
if p.DryRun {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
switch change.Action {
|
switch change.Action {
|
||||||
case hetznerCreate:
|
case hetznerCreate:
|
||||||
|
@ -55,6 +55,7 @@ type InfobloxConfig struct {
|
|||||||
MaxResults int
|
MaxResults int
|
||||||
FQDNRexEx string
|
FQDNRexEx string
|
||||||
CreatePTR bool
|
CreatePTR bool
|
||||||
|
CacheDuration int
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfobloxProvider implements the DNS provider for Infoblox.
|
// InfobloxProvider implements the DNS provider for Infoblox.
|
||||||
@ -67,6 +68,7 @@ type InfobloxProvider struct {
|
|||||||
dryRun bool
|
dryRun bool
|
||||||
fqdnRegEx string
|
fqdnRegEx string
|
||||||
createPTR bool
|
createPTR bool
|
||||||
|
cacheDuration int
|
||||||
}
|
}
|
||||||
|
|
||||||
type infobloxRecordSet struct {
|
type infobloxRecordSet struct {
|
||||||
@ -153,6 +155,7 @@ func NewInfobloxProvider(infobloxConfig InfobloxConfig) (*InfobloxProvider, erro
|
|||||||
view: infobloxConfig.View,
|
view: infobloxConfig.View,
|
||||||
fqdnRegEx: infobloxConfig.FQDNRexEx,
|
fqdnRegEx: infobloxConfig.FQDNRexEx,
|
||||||
createPTR: infobloxConfig.CreatePTR,
|
createPTR: infobloxConfig.CreatePTR,
|
||||||
|
cacheDuration: infobloxConfig.CacheDuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider, nil
|
return provider, nil
|
||||||
@ -216,6 +219,8 @@ 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 {
|
||||||
|
logrus.Debugf("Record='%s' A(H):'%s'", res.Name, ip.Ipv4Addr)
|
||||||
|
|
||||||
// host record is an abstraction in infoblox that combines A and PTR records
|
// 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
|
// 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)
|
newEndpoint := endpoint.NewEndpoint(res.Name, endpoint.RecordTypeA, ip.Ipv4Addr)
|
||||||
@ -238,6 +243,7 @@ func (p *InfobloxProvider) Records(ctx context.Context) (endpoints []*endpoint.E
|
|||||||
return nil, fmt.Errorf("could not fetch CNAME records from zone '%s': %s", zone.Fqdn, err)
|
return nil, fmt.Errorf("could not fetch CNAME records from zone '%s': %s", zone.Fqdn, err)
|
||||||
}
|
}
|
||||||
for _, res := range resC {
|
for _, res := range resC {
|
||||||
|
logrus.Debugf("Record='%s' CNAME:'%s'", res.Name, res.Canonical)
|
||||||
endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeCNAME, res.Canonical))
|
endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeCNAME, res.Canonical))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,6 +287,8 @@ func (p *InfobloxProvider) Records(ctx context.Context) (endpoints []*endpoint.E
|
|||||||
if _, err := strconv.Unquote(res.Text); err != nil {
|
if _, err := strconv.Unquote(res.Text); err != nil {
|
||||||
res.Text = strconv.Quote(res.Text)
|
res.Text = strconv.Quote(res.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("Record='%s' TXT:'%s'", res.Name, res.Text)
|
||||||
endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeTXT, res.Text))
|
endpoints = append(endpoints, endpoint.NewEndpoint(res.Name, endpoint.RecordTypeTXT, res.Text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,6 +328,11 @@ func (p *InfobloxProvider) Records(ctx context.Context) (endpoints []*endpoint.E
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *InfobloxProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) []*endpoint.Endpoint {
|
func (p *InfobloxProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) []*endpoint.Endpoint {
|
||||||
|
// Update user specified TTL (0 == disabled)
|
||||||
|
for i := range endpoints {
|
||||||
|
endpoints[i].RecordTTL = endpoint.TTL(p.cacheDuration)
|
||||||
|
}
|
||||||
|
|
||||||
if !p.createPTR {
|
if !p.createPTR {
|
||||||
return endpoints
|
return endpoints
|
||||||
}
|
}
|
||||||
@ -559,12 +572,14 @@ func (p *InfobloxProvider) recordSet(ep *endpoint.Endpoint, getObject bool, targ
|
|||||||
func (p *InfobloxProvider) createRecords(created infobloxChangeMap) {
|
func (p *InfobloxProvider) createRecords(created infobloxChangeMap) {
|
||||||
for zone, endpoints := range created {
|
for zone, endpoints := range created {
|
||||||
for _, ep := range endpoints {
|
for _, ep := range endpoints {
|
||||||
|
for targetIndex := range ep.Targets {
|
||||||
if p.dryRun {
|
if p.dryRun {
|
||||||
logrus.Infof(
|
logrus.Infof(
|
||||||
|
|
||||||
"Would create %s record named '%s' to '%s' for Infoblox DNS zone '%s'.",
|
"Would create %s record named '%s' to '%s' for Infoblox DNS zone '%s'.",
|
||||||
ep.RecordType,
|
ep.RecordType,
|
||||||
ep.DNSName,
|
ep.DNSName,
|
||||||
ep.Targets,
|
ep.Targets[targetIndex],
|
||||||
zone,
|
zone,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
@ -574,11 +589,10 @@ func (p *InfobloxProvider) createRecords(created infobloxChangeMap) {
|
|||||||
"Creating %s record named '%s' to '%s' for Infoblox DNS zone '%s'.",
|
"Creating %s record named '%s' to '%s' for Infoblox DNS zone '%s'.",
|
||||||
ep.RecordType,
|
ep.RecordType,
|
||||||
ep.DNSName,
|
ep.DNSName,
|
||||||
ep.Targets,
|
ep.Targets[targetIndex],
|
||||||
zone,
|
zone,
|
||||||
)
|
)
|
||||||
|
|
||||||
for targetIndex := range ep.Targets {
|
|
||||||
recordSet, err := p.recordSet(ep, false, targetIndex)
|
recordSet, err := p.recordSet(ep, false, targetIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf(
|
logrus.Errorf(
|
||||||
@ -611,10 +625,6 @@ func (p *InfobloxProvider) deleteRecords(deleted infobloxChangeMap) {
|
|||||||
// Delete records first
|
// Delete records first
|
||||||
for zone, endpoints := range deleted {
|
for zone, endpoints := range deleted {
|
||||||
for _, ep := range endpoints {
|
for _, ep := range endpoints {
|
||||||
if p.dryRun {
|
|
||||||
logrus.Infof("Would delete %s record named '%s' for Infoblox DNS zone '%s'.", ep.RecordType, ep.DNSName, zone)
|
|
||||||
} else {
|
|
||||||
logrus.Infof("Deleting %s record named '%s' for Infoblox DNS zone '%s'.", ep.RecordType, ep.DNSName, zone)
|
|
||||||
for targetIndex := range ep.Targets {
|
for targetIndex := range ep.Targets {
|
||||||
recordSet, err := p.recordSet(ep, true, targetIndex)
|
recordSet, err := p.recordSet(ep, true, targetIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -631,26 +641,47 @@ func (p *InfobloxProvider) deleteRecords(deleted infobloxChangeMap) {
|
|||||||
switch ep.RecordType {
|
switch ep.RecordType {
|
||||||
case endpoint.RecordTypeA:
|
case endpoint.RecordTypeA:
|
||||||
for _, record := range *recordSet.res.(*[]ibclient.RecordA) {
|
for _, record := range *recordSet.res.(*[]ibclient.RecordA) {
|
||||||
|
if p.dryRun {
|
||||||
|
logrus.Infof("Would delete %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "A", record.Name, record.Ipv4Addr, record.Zone)
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("Deleting %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "A", record.Name, record.Ipv4Addr, record.Zone)
|
||||||
_, err = p.client.DeleteObject(record.Ref)
|
_, err = p.client.DeleteObject(record.Ref)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case endpoint.RecordTypePTR:
|
case endpoint.RecordTypePTR:
|
||||||
for _, record := range *recordSet.res.(*[]ibclient.RecordPTR) {
|
for _, record := range *recordSet.res.(*[]ibclient.RecordPTR) {
|
||||||
|
if p.dryRun {
|
||||||
|
logrus.Infof("Would delete %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "PTR", record.PtrdName, record.Ipv4Addr, record.Zone)
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("Deleting %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "PTR", record.PtrdName, record.Ipv4Addr, record.Zone)
|
||||||
_, err = p.client.DeleteObject(record.Ref)
|
_, 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) {
|
||||||
|
if p.dryRun {
|
||||||
|
logrus.Infof("Would delete %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "CNAME", record.Name, record.Canonical, record.Zone)
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("Deleting %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "CNAME", record.Name, record.Canonical, record.Zone)
|
||||||
_, err = p.client.DeleteObject(record.Ref)
|
_, err = p.client.DeleteObject(record.Ref)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case endpoint.RecordTypeTXT:
|
case endpoint.RecordTypeTXT:
|
||||||
for _, record := range *recordSet.res.(*[]ibclient.RecordTXT) {
|
for _, record := range *recordSet.res.(*[]ibclient.RecordTXT) {
|
||||||
|
if p.dryRun {
|
||||||
|
logrus.Infof("Would delete %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "TXT", record.Name, record.Text, record.Zone)
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("Deleting %s record named '%s' to '%s' for Infoblox DNS zone '%s'.", "TXT", record.Name, record.Text, record.Zone)
|
||||||
_, err = p.client.DeleteObject(record.Ref)
|
_, err = p.client.DeleteObject(record.Ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf(
|
logrus.Errorf(
|
||||||
"Failed to delete %s record named '%s' for Infoblox DNS zone '%s': %v",
|
"Failed to delete %s record named '%s' to '%s' for Infoblox DNS zone '%s': %v",
|
||||||
ep.RecordType,
|
ep.RecordType,
|
||||||
ep.DNSName,
|
ep.DNSName,
|
||||||
|
ep.Targets[targetIndex],
|
||||||
zone,
|
zone,
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
@ -658,7 +689,6 @@ func (p *InfobloxProvider) deleteRecords(deleted infobloxChangeMap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupEnvAtoi(key string, fallback int) (i int) {
|
func lookupEnvAtoi(key string, fallback int) (i int) {
|
||||||
|
242
provider/safedns/safedns.go
Normal file
242
provider/safedns/safedns.go
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 safedns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
ukfClient "github.com/ukfast/sdk-go/pkg/client"
|
||||||
|
ukfConnection "github.com/ukfast/sdk-go/pkg/connection"
|
||||||
|
"github.com/ukfast/sdk-go/pkg/service/safedns"
|
||||||
|
|
||||||
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
|
"sigs.k8s.io/external-dns/plan"
|
||||||
|
"sigs.k8s.io/external-dns/provider"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SafeDNS is an interface that is a subset of the SafeDNS service API that are actually used.
|
||||||
|
// Signatures must match exactly.
|
||||||
|
type SafeDNS interface {
|
||||||
|
CreateZoneRecord(zoneName string, req safedns.CreateRecordRequest) (int, error)
|
||||||
|
DeleteZoneRecord(zoneName string, recordID int) error
|
||||||
|
GetZone(zoneName string) (safedns.Zone, error)
|
||||||
|
GetZoneRecord(zoneName string, recordID int) (safedns.Record, error)
|
||||||
|
GetZoneRecords(zoneName string, parameters ukfConnection.APIRequestParameters) ([]safedns.Record, error)
|
||||||
|
GetZones(parameters ukfConnection.APIRequestParameters) ([]safedns.Zone, error)
|
||||||
|
PatchZoneRecord(zoneName string, recordID int, patch safedns.PatchRecordRequest) (int, error)
|
||||||
|
UpdateZoneRecord(zoneName string, record safedns.Record) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeDNSProvider implements the DNS provider spec for UKFast SafeDNS.
|
||||||
|
type SafeDNSProvider struct {
|
||||||
|
provider.BaseProvider
|
||||||
|
Client SafeDNS
|
||||||
|
// Only consider hosted zones managing domains ending in this suffix
|
||||||
|
domainFilter endpoint.DomainFilter
|
||||||
|
DryRun bool
|
||||||
|
APIRequestParams ukfConnection.APIRequestParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneRecord is a datatype to simplify management of a record in a zone.
|
||||||
|
type ZoneRecord struct {
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
Type safedns.RecordType
|
||||||
|
TTL safedns.RecordTTL
|
||||||
|
Zone string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSafeDNSProvider(domainFilter endpoint.DomainFilter, dryRun bool) (*SafeDNSProvider, error) {
|
||||||
|
token, ok := os.LookupEnv("SAFEDNS_TOKEN")
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("no SAFEDNS_TOKEN found in environment")
|
||||||
|
}
|
||||||
|
|
||||||
|
ukfAPIConnection := ukfConnection.NewAPIKeyCredentialsAPIConnection(token)
|
||||||
|
ukfClient := ukfClient.NewClient(ukfAPIConnection)
|
||||||
|
safeDNS := ukfClient.SafeDNSService()
|
||||||
|
|
||||||
|
provider := &SafeDNSProvider{
|
||||||
|
Client: safeDNS,
|
||||||
|
domainFilter: domainFilter,
|
||||||
|
DryRun: dryRun,
|
||||||
|
APIRequestParams: *ukfConnection.NewAPIRequestParameters(),
|
||||||
|
}
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zones returns the list of hosted zones in the SafeDNS account
|
||||||
|
func (p *SafeDNSProvider) Zones(ctx context.Context) ([]safedns.Zone, error) {
|
||||||
|
var zones []safedns.Zone
|
||||||
|
|
||||||
|
allZones, err := p.Client.GetZones(p.APIRequestParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each found zone to see whether they match the domain filter provided. If they do, append it to the array of
|
||||||
|
// zones defined above. If not, continue to the next item in the loop.
|
||||||
|
for _, zone := range allZones {
|
||||||
|
if p.domainFilter.Match(zone.Name) {
|
||||||
|
zones = append(zones, zone)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zones, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SafeDNSProvider) ZoneRecords(ctx context.Context) ([]ZoneRecord, error) {
|
||||||
|
zones, err := p.Zones(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var zoneRecords []ZoneRecord
|
||||||
|
for _, zone := range zones {
|
||||||
|
// For each zone in the zonelist, get all records of an ExternalDNS supported type.
|
||||||
|
records, err := p.Client.GetZoneRecords(zone.Name, p.APIRequestParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, r := range records {
|
||||||
|
zoneRecord := ZoneRecord{
|
||||||
|
ID: r.ID,
|
||||||
|
Name: r.Name,
|
||||||
|
Type: r.Type,
|
||||||
|
TTL: r.TTL,
|
||||||
|
Zone: zone.Name,
|
||||||
|
Content: r.Content,
|
||||||
|
}
|
||||||
|
zoneRecords = append(zoneRecords, zoneRecord)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zoneRecords, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Records returns a list of Endpoint resources created from all records in supported zones.
|
||||||
|
func (p *SafeDNSProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||||
|
var endpoints []*endpoint.Endpoint
|
||||||
|
zoneRecords, err := p.ZoneRecords(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, r := range zoneRecords {
|
||||||
|
if provider.SupportedRecordType(string(r.Type)) {
|
||||||
|
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(r.Name, string(r.Type), endpoint.TTL(r.TTL), r.Content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return endpoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyChanges applies a given set of changes in a given zone.
|
||||||
|
func (p *SafeDNSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||||
|
// Identify the zone name for each record
|
||||||
|
zoneNameIDMapper := provider.ZoneIDName{}
|
||||||
|
|
||||||
|
zones, err := p.Zones(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, zone := range zones {
|
||||||
|
zoneNameIDMapper.Add(zone.Name, zone.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
zoneRecords, err := p.ZoneRecords(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, endpoint := range changes.Create {
|
||||||
|
_, ZoneName := zoneNameIDMapper.FindZone(endpoint.DNSName)
|
||||||
|
for _, target := range endpoint.Targets {
|
||||||
|
request := safedns.CreateRecordRequest{
|
||||||
|
Name: endpoint.DNSName,
|
||||||
|
Type: endpoint.RecordType,
|
||||||
|
Content: target,
|
||||||
|
}
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"zoneID": ZoneName,
|
||||||
|
"dnsName": endpoint.DNSName,
|
||||||
|
"recordType": endpoint.RecordType,
|
||||||
|
"Value": target,
|
||||||
|
}).Info("Creating record")
|
||||||
|
_, err := p.Client.CreateZoneRecord(ZoneName, request)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, endpoint := range changes.UpdateNew {
|
||||||
|
// Currently iterates over each zoneRecord in ZoneRecords for each Endpoint
|
||||||
|
// in UpdateNew; the same will go for Delete. As it's double-iteration,
|
||||||
|
// that's O(n^2), which isn't great. No performance issues have been noted
|
||||||
|
// thus far.
|
||||||
|
var zoneRecord ZoneRecord
|
||||||
|
for _, target := range endpoint.Targets {
|
||||||
|
for _, zr := range zoneRecords {
|
||||||
|
if zr.Name == endpoint.DNSName && zr.Content == target {
|
||||||
|
zoneRecord = zr
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newTTL := safedns.RecordTTL(int(endpoint.RecordTTL))
|
||||||
|
newRecord := safedns.PatchRecordRequest{
|
||||||
|
Name: endpoint.DNSName,
|
||||||
|
Content: target,
|
||||||
|
TTL: &newTTL,
|
||||||
|
Type: endpoint.RecordType,
|
||||||
|
}
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"zoneID": zoneRecord.Zone,
|
||||||
|
"dnsName": newRecord.Name,
|
||||||
|
"recordType": newRecord.Type,
|
||||||
|
"Value": newRecord.Content,
|
||||||
|
"Priority": newRecord.Priority,
|
||||||
|
}).Info("Patching record")
|
||||||
|
_, err = p.Client.PatchZoneRecord(zoneRecord.Zone, zoneRecord.ID, newRecord)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, endpoint := range changes.Delete {
|
||||||
|
// As above, currently iterates in O(n^2). May be a good start for optimisations.
|
||||||
|
var zoneRecord ZoneRecord
|
||||||
|
for _, zr := range zoneRecords {
|
||||||
|
if zr.Name == endpoint.DNSName && string(zr.Type) == endpoint.RecordType {
|
||||||
|
zoneRecord = zr
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"zoneID": zoneRecord.Zone,
|
||||||
|
"dnsName": zoneRecord.Name,
|
||||||
|
"recordType": zoneRecord.Type,
|
||||||
|
}).Info("Deleting record")
|
||||||
|
err := p.Client.DeleteZoneRecord(zoneRecord.Zone, zoneRecord.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
366
provider/safedns/safedns_test.go
Normal file
366
provider/safedns/safedns_test.go
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 safedns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
ukfConnection "github.com/ukfast/sdk-go/pkg/connection"
|
||||||
|
"github.com/ukfast/sdk-go/pkg/service/safedns"
|
||||||
|
|
||||||
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
|
"sigs.k8s.io/external-dns/plan"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create an implementation of the SafeDNS interface for Mocking
|
||||||
|
type MockSafeDNSService struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSafeDNSService) CreateZoneRecord(zoneName string, req safedns.CreateRecordRequest) (int, error) {
|
||||||
|
args := m.Called(zoneName, req)
|
||||||
|
return args.Int(0), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSafeDNSService) DeleteZoneRecord(zoneName string, recordID int) error {
|
||||||
|
args := m.Called(zoneName, recordID)
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSafeDNSService) GetZone(zoneName string) (safedns.Zone, error) {
|
||||||
|
args := m.Called(zoneName)
|
||||||
|
return args.Get(0).(safedns.Zone), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSafeDNSService) GetZoneRecord(zoneName string, recordID int) (safedns.Record, error) {
|
||||||
|
args := m.Called(zoneName, recordID)
|
||||||
|
return args.Get(0).(safedns.Record), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSafeDNSService) GetZoneRecords(zoneName string, parameters ukfConnection.APIRequestParameters) ([]safedns.Record, error) {
|
||||||
|
args := m.Called(zoneName, parameters)
|
||||||
|
return args.Get(0).([]safedns.Record), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSafeDNSService) GetZones(parameters ukfConnection.APIRequestParameters) ([]safedns.Zone, error) {
|
||||||
|
args := m.Called(parameters)
|
||||||
|
return args.Get(0).([]safedns.Zone), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSafeDNSService) PatchZoneRecord(zoneName string, recordID int, patch safedns.PatchRecordRequest) (int, error) {
|
||||||
|
args := m.Called(zoneName, recordID, patch)
|
||||||
|
return args.Int(0), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSafeDNSService) UpdateZoneRecord(zoneName string, record safedns.Record) (int, error) {
|
||||||
|
args := m.Called(zoneName, record)
|
||||||
|
return args.Int(0), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
func createZones() []safedns.Zone {
|
||||||
|
return []safedns.Zone{
|
||||||
|
{Name: "foo.com", Description: "Foo dot com"},
|
||||||
|
{Name: "bar.io", Description: ""},
|
||||||
|
{Name: "baz.org", Description: "Org"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFooRecords() []safedns.Record {
|
||||||
|
return []safedns.Record{
|
||||||
|
{
|
||||||
|
ID: 11,
|
||||||
|
Type: safedns.RecordTypeA,
|
||||||
|
Name: "foo.com",
|
||||||
|
Content: "targetFoo",
|
||||||
|
TTL: safedns.RecordTTL(3600),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 12,
|
||||||
|
Type: safedns.RecordTypeTXT,
|
||||||
|
Name: "foo.com",
|
||||||
|
Content: "text",
|
||||||
|
TTL: safedns.RecordTTL(3600),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 13,
|
||||||
|
Type: safedns.RecordTypeCAA,
|
||||||
|
Name: "foo.com",
|
||||||
|
Content: "",
|
||||||
|
TTL: safedns.RecordTTL(3600),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBarRecords() []safedns.Record {
|
||||||
|
return []safedns.Record{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBazRecords() []safedns.Record {
|
||||||
|
return []safedns.Record{
|
||||||
|
{
|
||||||
|
ID: 31,
|
||||||
|
Type: safedns.RecordTypeA,
|
||||||
|
Name: "baz.org",
|
||||||
|
Content: "targetBaz",
|
||||||
|
TTL: safedns.RecordTTL(3600),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 32,
|
||||||
|
Type: safedns.RecordTypeTXT,
|
||||||
|
Name: "baz.org",
|
||||||
|
Content: "text",
|
||||||
|
TTL: safedns.RecordTTL(3600),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 33,
|
||||||
|
Type: safedns.RecordTypeA,
|
||||||
|
Name: "api.baz.org",
|
||||||
|
Content: "targetBazAPI",
|
||||||
|
TTL: safedns.RecordTTL(3600),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 34,
|
||||||
|
Type: safedns.RecordTypeTXT,
|
||||||
|
Name: "api.baz.org",
|
||||||
|
Content: "text",
|
||||||
|
TTL: safedns.RecordTTL(3600),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual tests
|
||||||
|
func TestNewSafeDNSProvider(t *testing.T) {
|
||||||
|
_ = os.Setenv("SAFEDNS_TOKEN", "DUMMYVALUE")
|
||||||
|
_, err := NewSafeDNSProvider(endpoint.NewDomainFilter([]string{"ext-dns-test.zalando.to."}), true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_ = os.Unsetenv("SAFEDNS_TOKEN")
|
||||||
|
_, err = NewSafeDNSProvider(endpoint.NewDomainFilter([]string{"ext-dns-test.zalando.to."}), true)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecords(t *testing.T) {
|
||||||
|
mockSafeDNSService := MockSafeDNSService{}
|
||||||
|
|
||||||
|
provider := &SafeDNSProvider{
|
||||||
|
Client: &mockSafeDNSService,
|
||||||
|
domainFilter: endpoint.NewDomainFilter([]string{}),
|
||||||
|
DryRun: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZones",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createZones(), nil).Once()
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZoneRecords",
|
||||||
|
"foo.com",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createFooRecords(), nil).Once()
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZoneRecords",
|
||||||
|
"bar.io",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createBarRecords(), nil).Once()
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZoneRecords",
|
||||||
|
"baz.org",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createBazRecords(), nil).Once()
|
||||||
|
|
||||||
|
actual, err := provider.Records(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "foo.com",
|
||||||
|
Targets: []string{"targetFoo"},
|
||||||
|
RecordType: "A",
|
||||||
|
RecordTTL: 3600,
|
||||||
|
Labels: endpoint.NewLabels(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "foo.com",
|
||||||
|
Targets: []string{"text"},
|
||||||
|
RecordType: "TXT",
|
||||||
|
RecordTTL: 3600,
|
||||||
|
Labels: endpoint.NewLabels(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "baz.org",
|
||||||
|
Targets: []string{"targetBaz"},
|
||||||
|
RecordType: "A",
|
||||||
|
RecordTTL: 3600,
|
||||||
|
Labels: endpoint.NewLabels(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "baz.org",
|
||||||
|
Targets: []string{"text"},
|
||||||
|
RecordType: "TXT",
|
||||||
|
RecordTTL: 3600,
|
||||||
|
Labels: endpoint.NewLabels(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "api.baz.org",
|
||||||
|
Targets: []string{"targetBazAPI"},
|
||||||
|
RecordType: "A",
|
||||||
|
RecordTTL: 3600,
|
||||||
|
Labels: endpoint.NewLabels(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "api.baz.org",
|
||||||
|
Targets: []string{"text"},
|
||||||
|
RecordType: "TXT",
|
||||||
|
RecordTTL: 3600,
|
||||||
|
Labels: endpoint.NewLabels(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockSafeDNSService.AssertExpectations(t)
|
||||||
|
assert.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSafeDNSApplyChanges(t *testing.T) {
|
||||||
|
mockSafeDNSService := MockSafeDNSService{}
|
||||||
|
|
||||||
|
provider := &SafeDNSProvider{
|
||||||
|
Client: &mockSafeDNSService,
|
||||||
|
domainFilter: endpoint.NewDomainFilter([]string{}),
|
||||||
|
DryRun: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy data
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZones",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createZones(), nil).Once()
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZones",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createZones(), nil).Once()
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZoneRecords",
|
||||||
|
"foo.com",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createFooRecords(), nil).Once()
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZoneRecords",
|
||||||
|
"bar.io",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createBarRecords(), nil).Once()
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"GetZoneRecords",
|
||||||
|
"baz.org",
|
||||||
|
mock.Anything,
|
||||||
|
).Return(createBazRecords(), nil).Once()
|
||||||
|
|
||||||
|
// Apply actions
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"DeleteZoneRecord",
|
||||||
|
"baz.org",
|
||||||
|
33,
|
||||||
|
).Return(nil).Once()
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"DeleteZoneRecord",
|
||||||
|
"baz.org",
|
||||||
|
34,
|
||||||
|
).Return(nil).Once()
|
||||||
|
|
||||||
|
TTL300 := safedns.RecordTTL(300)
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"PatchZoneRecord",
|
||||||
|
"foo.com",
|
||||||
|
11,
|
||||||
|
safedns.PatchRecordRequest{
|
||||||
|
Type: "A",
|
||||||
|
Name: "foo.com",
|
||||||
|
Content: "targetFoo",
|
||||||
|
TTL: &TTL300,
|
||||||
|
},
|
||||||
|
).Return(123, nil).Once()
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"CreateZoneRecord",
|
||||||
|
"bar.io",
|
||||||
|
safedns.CreateRecordRequest{
|
||||||
|
Type: "A",
|
||||||
|
Name: "create.bar.io",
|
||||||
|
Content: "targetBar",
|
||||||
|
},
|
||||||
|
).Return(246, nil).Once()
|
||||||
|
|
||||||
|
mockSafeDNSService.On(
|
||||||
|
"CreateZoneRecord",
|
||||||
|
"bar.io",
|
||||||
|
safedns.CreateRecordRequest{
|
||||||
|
Type: "A",
|
||||||
|
Name: "bar.io",
|
||||||
|
Content: "targetBar",
|
||||||
|
},
|
||||||
|
).Return(369, nil).Once()
|
||||||
|
|
||||||
|
err := provider.ApplyChanges(context.Background(), &plan.Changes{
|
||||||
|
Create: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "create.bar.io",
|
||||||
|
RecordType: "A",
|
||||||
|
Targets: []string{"targetBar"},
|
||||||
|
RecordTTL: 3600,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "bar.io",
|
||||||
|
RecordType: "A",
|
||||||
|
Targets: []string{"targetBar"},
|
||||||
|
RecordTTL: 3600,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Delete: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "api.baz.org",
|
||||||
|
RecordType: "A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "api.baz.org",
|
||||||
|
RecordType: "TXT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UpdateNew: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "foo.com",
|
||||||
|
RecordType: "A",
|
||||||
|
RecordTTL: 300,
|
||||||
|
Targets: []string{"targetFoo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UpdateOld: []*endpoint.Endpoint{},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
mockSafeDNSService.AssertExpectations(t)
|
||||||
|
}
|
@ -30,7 +30,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"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/util/wait"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/dynamic/dynamicinformer"
|
"k8s.io/client-go/dynamic/dynamicinformer"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
@ -64,6 +63,7 @@ type ambassadorHostSource struct {
|
|||||||
|
|
||||||
// NewAmbassadorHostSource creates a new ambassadorHostSource with the given config.
|
// NewAmbassadorHostSource creates a new ambassadorHostSource with the given config.
|
||||||
func NewAmbassadorHostSource(
|
func NewAmbassadorHostSource(
|
||||||
|
ctx context.Context,
|
||||||
dynamicKubeClient dynamic.Interface,
|
dynamicKubeClient dynamic.Interface,
|
||||||
kubeClient kubernetes.Interface,
|
kubeClient kubernetes.Interface,
|
||||||
namespace string) (Source, error) {
|
namespace string) (Source, error) {
|
||||||
@ -82,8 +82,7 @@ func NewAmbassadorHostSource(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/dynamic/dynamicinformer"
|
"k8s.io/client-go/dynamic/dynamicinformer"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
@ -53,6 +52,7 @@ type httpProxySource struct {
|
|||||||
|
|
||||||
// NewContourHTTPProxySource creates a new contourHTTPProxySource with the given config.
|
// NewContourHTTPProxySource creates a new contourHTTPProxySource with the given config.
|
||||||
func NewContourHTTPProxySource(
|
func NewContourHTTPProxySource(
|
||||||
|
ctx context.Context,
|
||||||
dynamicKubeClient dynamic.Interface,
|
dynamicKubeClient dynamic.Interface,
|
||||||
namespace string,
|
namespace string,
|
||||||
annotationFilter string,
|
annotationFilter string,
|
||||||
@ -78,8 +78,7 @@ func NewContourHTTPProxySource(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -88,6 +88,7 @@ func (suite *HTTPProxySuite) SetupTest() {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
suite.source, err = NewContourHTTPProxySource(
|
suite.source, err = NewContourHTTPProxySource(
|
||||||
|
context.TODO(),
|
||||||
fakeDynamicClient,
|
fakeDynamicClient,
|
||||||
"default",
|
"default",
|
||||||
"",
|
"",
|
||||||
@ -184,6 +185,7 @@ func TestNewContourHTTPProxySource(t *testing.T) {
|
|||||||
fakeDynamicClient, _ := newDynamicKubernetesClient()
|
fakeDynamicClient, _ := newDynamicKubernetesClient()
|
||||||
|
|
||||||
_, err := NewContourHTTPProxySource(
|
_, err := NewContourHTTPProxySource(
|
||||||
|
context.TODO(),
|
||||||
fakeDynamicClient,
|
fakeDynamicClient,
|
||||||
"",
|
"",
|
||||||
ti.annotationFilter,
|
ti.annotationFilter,
|
||||||
@ -1033,6 +1035,7 @@ func testHTTPProxyEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
httpProxySource, err := NewContourHTTPProxySource(
|
httpProxySource, err := NewContourHTTPProxySource(
|
||||||
|
context.TODO(),
|
||||||
fakeDynamicClient,
|
fakeDynamicClient,
|
||||||
ti.targetNamespace,
|
ti.targetNamespace,
|
||||||
ti.annotationFilter,
|
ti.annotationFilter,
|
||||||
@ -1059,6 +1062,7 @@ func newTestHTTPProxySource() (*httpProxySource, error) {
|
|||||||
fakeDynamicClient, _ := newDynamicKubernetesClient()
|
fakeDynamicClient, _ := newDynamicKubernetesClient()
|
||||||
|
|
||||||
src, err := NewContourHTTPProxySource(
|
src, err := NewContourHTTPProxySource(
|
||||||
|
context.TODO(),
|
||||||
fakeDynamicClient,
|
fakeDynamicClient,
|
||||||
"default",
|
"default",
|
||||||
"",
|
"",
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
networkv1 "k8s.io/api/networking/v1"
|
networkv1 "k8s.io/api/networking/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/selection"
|
"k8s.io/apimachinery/pkg/selection"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
kubeinformers "k8s.io/client-go/informers"
|
kubeinformers "k8s.io/client-go/informers"
|
||||||
netinformers "k8s.io/client-go/informers/networking/v1"
|
netinformers "k8s.io/client-go/informers/networking/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@ -67,7 +66,7 @@ type ingressSource struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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, labelSelector labels.Selector, ingressClassNames []string) (Source, error) {
|
func NewIngressSource(ctx context.Context, 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
|
||||||
@ -101,8 +100,7 @@ func NewIngressSource(kubeClient kubernetes.Interface, namespace, annotationFilt
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -56,6 +56,7 @@ func (suite *IngressSuite) SetupTest() {
|
|||||||
suite.NoError(err, "should succeed")
|
suite.NoError(err, "should succeed")
|
||||||
|
|
||||||
suite.sc, err = NewIngressSource(
|
suite.sc, err = NewIngressSource(
|
||||||
|
context.TODO(),
|
||||||
fakeClient,
|
fakeClient,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
@ -151,6 +152,7 @@ func TestNewIngressSource(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, err := NewIngressSource(
|
_, err := NewIngressSource(
|
||||||
|
context.TODO(),
|
||||||
fake.NewSimpleClientset(),
|
fake.NewSimpleClientset(),
|
||||||
"",
|
"",
|
||||||
ti.annotationFilter,
|
ti.annotationFilter,
|
||||||
@ -1291,6 +1293,7 @@ func testIngressEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
source, _ := NewIngressSource(
|
source, _ := NewIngressSource(
|
||||||
|
context.TODO(),
|
||||||
fakeClient,
|
fakeClient,
|
||||||
ti.targetNamespace,
|
ti.targetNamespace,
|
||||||
ti.annotationFilter,
|
ti.annotationFilter,
|
||||||
|
@ -30,7 +30,6 @@ import (
|
|||||||
networkingv1alpha3informer "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3"
|
networkingv1alpha3informer "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3"
|
||||||
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/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
kubeinformers "k8s.io/client-go/informers"
|
kubeinformers "k8s.io/client-go/informers"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@ -56,6 +55,7 @@ type gatewaySource struct {
|
|||||||
|
|
||||||
// NewIstioGatewaySource creates a new gatewaySource with the given config.
|
// NewIstioGatewaySource creates a new gatewaySource with the given config.
|
||||||
func NewIstioGatewaySource(
|
func NewIstioGatewaySource(
|
||||||
|
ctx context.Context,
|
||||||
kubeClient kubernetes.Interface,
|
kubeClient kubernetes.Interface,
|
||||||
istioClient istioclient.Interface,
|
istioClient istioclient.Interface,
|
||||||
namespace string,
|
namespace string,
|
||||||
@ -93,9 +93,8 @@ func NewIstioGatewaySource(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
istioInformerFactory.Start(ctx.Done())
|
||||||
istioInformerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -69,6 +69,7 @@ func (suite *GatewaySuite) SetupTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suite.source, err = NewIstioGatewaySource(
|
suite.source, err = NewIstioGatewaySource(
|
||||||
|
context.TODO(),
|
||||||
fakeKubernetesClient,
|
fakeKubernetesClient,
|
||||||
fakeIstioClient,
|
fakeIstioClient,
|
||||||
"",
|
"",
|
||||||
@ -142,6 +143,7 @@ func TestNewIstioGatewaySource(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, err := NewIstioGatewaySource(
|
_, err := NewIstioGatewaySource(
|
||||||
|
context.TODO(),
|
||||||
fake.NewSimpleClientset(),
|
fake.NewSimpleClientset(),
|
||||||
istiofake.NewSimpleClientset(),
|
istiofake.NewSimpleClientset(),
|
||||||
"",
|
"",
|
||||||
@ -1165,6 +1167,7 @@ func testGatewayEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gatewaySource, err := NewIstioGatewaySource(
|
gatewaySource, err := NewIstioGatewaySource(
|
||||||
|
context.TODO(),
|
||||||
fakeKubernetesClient,
|
fakeKubernetesClient,
|
||||||
fakeIstioClient,
|
fakeIstioClient,
|
||||||
ti.targetNamespace,
|
ti.targetNamespace,
|
||||||
@ -1201,6 +1204,7 @@ func newTestGatewaySource(loadBalancerList []fakeIngressGatewayService) (*gatewa
|
|||||||
}
|
}
|
||||||
|
|
||||||
src, err := NewIstioGatewaySource(
|
src, err := NewIstioGatewaySource(
|
||||||
|
context.TODO(),
|
||||||
fakeKubernetesClient,
|
fakeKubernetesClient,
|
||||||
fakeIstioClient,
|
fakeIstioClient,
|
||||||
"",
|
"",
|
||||||
|
@ -31,7 +31,6 @@ import (
|
|||||||
networkingv1alpha3informer "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3"
|
networkingv1alpha3informer "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3"
|
||||||
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/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
kubeinformers "k8s.io/client-go/informers"
|
kubeinformers "k8s.io/client-go/informers"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@ -60,6 +59,7 @@ type virtualServiceSource struct {
|
|||||||
|
|
||||||
// NewIstioVirtualServiceSource creates a new virtualServiceSource with the given config.
|
// NewIstioVirtualServiceSource creates a new virtualServiceSource with the given config.
|
||||||
func NewIstioVirtualServiceSource(
|
func NewIstioVirtualServiceSource(
|
||||||
|
ctx context.Context,
|
||||||
kubeClient kubernetes.Interface,
|
kubeClient kubernetes.Interface,
|
||||||
istioClient istioclient.Interface,
|
istioClient istioclient.Interface,
|
||||||
namespace string,
|
namespace string,
|
||||||
@ -97,9 +97,8 @@ func NewIstioVirtualServiceSource(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
istioInformerFactory.Start(ctx.Done())
|
||||||
istioInformerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -89,6 +89,7 @@ func (suite *VirtualServiceSuite) SetupTest() {
|
|||||||
suite.NoError(err, "should succeed")
|
suite.NoError(err, "should succeed")
|
||||||
|
|
||||||
suite.source, err = NewIstioVirtualServiceSource(
|
suite.source, err = NewIstioVirtualServiceSource(
|
||||||
|
context.TODO(),
|
||||||
fakeKubernetesClient,
|
fakeKubernetesClient,
|
||||||
fakeIstioClient,
|
fakeIstioClient,
|
||||||
"",
|
"",
|
||||||
@ -165,6 +166,7 @@ func TestNewIstioVirtualServiceSource(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, err := NewIstioVirtualServiceSource(
|
_, err := NewIstioVirtualServiceSource(
|
||||||
|
context.TODO(),
|
||||||
fake.NewSimpleClientset(),
|
fake.NewSimpleClientset(),
|
||||||
istiofake.NewSimpleClientset(),
|
istiofake.NewSimpleClientset(),
|
||||||
"",
|
"",
|
||||||
@ -1482,6 +1484,7 @@ func testVirtualServiceEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtualServiceSource, err := NewIstioVirtualServiceSource(
|
virtualServiceSource, err := NewIstioVirtualServiceSource(
|
||||||
|
context.TODO(),
|
||||||
fakeKubernetesClient,
|
fakeKubernetesClient,
|
||||||
fakeIstioClient,
|
fakeIstioClient,
|
||||||
ti.targetNamespace,
|
ti.targetNamespace,
|
||||||
@ -1557,6 +1560,7 @@ func newTestVirtualServiceSource(loadBalancerList []fakeIngressGatewayService, g
|
|||||||
}
|
}
|
||||||
|
|
||||||
src, err := NewIstioVirtualServiceSource(
|
src, err := NewIstioVirtualServiceSource(
|
||||||
|
context.TODO(),
|
||||||
fakeKubernetesClient,
|
fakeKubernetesClient,
|
||||||
fakeIstioClient,
|
fakeIstioClient,
|
||||||
"",
|
"",
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"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/util/wait"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/dynamic/dynamicinformer"
|
"k8s.io/client-go/dynamic/dynamicinformer"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
@ -57,7 +56,7 @@ type kongTCPIngressSource struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewKongTCPIngressSource creates a new kongTCPIngressSource with the given config.
|
// NewKongTCPIngressSource creates a new kongTCPIngressSource with the given config.
|
||||||
func NewKongTCPIngressSource(dynamicKubeClient dynamic.Interface, kubeClient kubernetes.Interface, namespace string, annotationFilter string) (Source, error) {
|
func NewKongTCPIngressSource(ctx context.Context, dynamicKubeClient dynamic.Interface, kubeClient kubernetes.Interface, namespace string, annotationFilter string) (Source, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Use shared informer to listen for add/update/delete of Host in the specified namespace.
|
// Use shared informer to listen for add/update/delete of Host in the specified namespace.
|
||||||
@ -73,8 +72,7 @@ func NewKongTCPIngressSource(dynamicKubeClient dynamic.Interface, kubeClient kub
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForDynamicCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -241,7 +241,7 @@ func TestKongTCPIngressEndpoints(t *testing.T) {
|
|||||||
_, err = fakeDynamicClient.Resource(kongGroupdVersionResource).Namespace(defaultKongNamespace).Create(context.Background(), &tcpi, metav1.CreateOptions{})
|
_, err = fakeDynamicClient.Resource(kongGroupdVersionResource).Namespace(defaultKongNamespace).Create(context.Background(), &tcpi, metav1.CreateOptions{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
source, err := NewKongTCPIngressSource(fakeDynamicClient, fakeKubernetesClient, defaultKongNamespace, "kubernetes.io/ingress.class=kong")
|
source, err := NewKongTCPIngressSource(context.TODO(), fakeDynamicClient, fakeKubernetesClient, defaultKongNamespace, "kubernetes.io/ingress.class=kong")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, source)
|
assert.NotNil(t, source)
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
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/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
kubeinformers "k8s.io/client-go/informers"
|
kubeinformers "k8s.io/client-go/informers"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@ -42,7 +41,7 @@ type nodeSource struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewNodeSource creates a new nodeSource with the given config.
|
// NewNodeSource creates a new nodeSource with the given config.
|
||||||
func NewNodeSource(kubeClient kubernetes.Interface, annotationFilter, fqdnTemplate string) (Source, error) {
|
func NewNodeSource(ctx context.Context, kubeClient kubernetes.Interface, annotationFilter, fqdnTemplate string) (Source, error) {
|
||||||
tmpl, err := parseTemplate(fqdnTemplate)
|
tmpl, err := parseTemplate(fqdnTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -62,8 +61,7 @@ func NewNodeSource(kubeClient kubernetes.Interface, annotationFilter, fqdnTempla
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -71,6 +71,7 @@ func testNodeSourceNewNodeSource(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, err := NewNodeSource(
|
_, err := NewNodeSource(
|
||||||
|
context.TODO(),
|
||||||
fake.NewSimpleClientset(),
|
fake.NewSimpleClientset(),
|
||||||
ti.annotationFilter,
|
ti.annotationFilter,
|
||||||
ti.fqdnTemplate,
|
ti.fqdnTemplate,
|
||||||
@ -353,6 +354,7 @@ func testNodeSourceEndpoints(t *testing.T) {
|
|||||||
|
|
||||||
// Create our object under test and get the endpoints.
|
// Create our object under test and get the endpoints.
|
||||||
client, err := NewNodeSource(
|
client, err := NewNodeSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
tc.annotationFilter,
|
tc.annotationFilter,
|
||||||
tc.fqdnTemplate,
|
tc.fqdnTemplate,
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
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/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
"sigs.k8s.io/external-dns/endpoint"
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
@ -54,6 +53,7 @@ type ocpRouteSource struct {
|
|||||||
|
|
||||||
// NewOcpRouteSource creates a new ocpRouteSource with the given config.
|
// NewOcpRouteSource creates a new ocpRouteSource with the given config.
|
||||||
func NewOcpRouteSource(
|
func NewOcpRouteSource(
|
||||||
|
ctx context.Context,
|
||||||
ocpClient versioned.Interface,
|
ocpClient versioned.Interface,
|
||||||
namespace string,
|
namespace string,
|
||||||
annotationFilter string,
|
annotationFilter string,
|
||||||
@ -81,8 +81,7 @@ func NewOcpRouteSource(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -43,6 +43,7 @@ func (suite *OCPRouteSuite) SetupTest() {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
suite.sc, err = NewOcpRouteSource(
|
suite.sc, err = NewOcpRouteSource(
|
||||||
|
context.TODO(),
|
||||||
fakeClient,
|
fakeClient,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
@ -141,6 +142,7 @@ func testOcpRouteSourceNewOcpRouteSource(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, err := NewOcpRouteSource(
|
_, err := NewOcpRouteSource(
|
||||||
|
context.TODO(),
|
||||||
fake.NewSimpleClientset(),
|
fake.NewSimpleClientset(),
|
||||||
"",
|
"",
|
||||||
ti.annotationFilter,
|
ti.annotationFilter,
|
||||||
@ -439,6 +441,7 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
source, err := NewOcpRouteSource(
|
source, err := NewOcpRouteSource(
|
||||||
|
context.TODO(),
|
||||||
fakeClient,
|
fakeClient,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
kubeinformers "k8s.io/client-go/informers"
|
kubeinformers "k8s.io/client-go/informers"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@ -40,7 +39,7 @@ type podSource struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewPodSource creates a new podSource with the given config.
|
// NewPodSource creates a new podSource with the given config.
|
||||||
func NewPodSource(kubeClient kubernetes.Interface, namespace string, compatibility string) (Source, error) {
|
func NewPodSource(ctx context.Context, kubeClient kubernetes.Interface, namespace string, compatibility string) (Source, error) {
|
||||||
informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace))
|
informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace))
|
||||||
podInformer := informerFactory.Core().V1().Pods()
|
podInformer := informerFactory.Core().V1().Pods()
|
||||||
nodeInformer := informerFactory.Core().V1().Nodes()
|
nodeInformer := informerFactory.Core().V1().Nodes()
|
||||||
@ -58,7 +57,7 @@ func NewPodSource(kubeClient kubernetes.Interface, namespace string, compatibili
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
informerFactory.Start(wait.NeverStop)
|
informerFactory.Start(ctx.Done())
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -412,7 +412,7 @@ func TestPodSource(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := NewPodSource(kubernetes, tc.targetNamespace, tc.compatibility)
|
client, err := NewPodSource(context.TODO(), kubernetes, tc.targetNamespace, tc.compatibility)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
endpoints, err := client.Endpoints(ctx)
|
endpoints, err := client.Endpoints(ctx)
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
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/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
kubeinformers "k8s.io/client-go/informers"
|
kubeinformers "k8s.io/client-go/informers"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@ -67,7 +66,7 @@ type serviceSource struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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, labelSelector labels.Selector) (Source, error) {
|
func NewServiceSource(ctx context.Context, 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
|
||||||
@ -107,8 +106,7 @@ func NewServiceSource(kubeClient kubernetes.Interface, namespace, annotationFilt
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO informer is not explicitly stopped since controller is not passing in its channel.
|
informerFactory.Start(ctx.Done())
|
||||||
informerFactory.Start(wait.NeverStop)
|
|
||||||
|
|
||||||
// wait for the local cache to be populated.
|
// wait for the local cache to be populated.
|
||||||
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
|
||||||
|
@ -65,6 +65,7 @@ func (suite *ServiceSuite) SetupTest() {
|
|||||||
suite.NoError(err, "should successfully create service")
|
suite.NoError(err, "should successfully create service")
|
||||||
|
|
||||||
suite.sc, err = NewServiceSource(
|
suite.sc, err = NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
fakeClient,
|
fakeClient,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
@ -144,6 +145,7 @@ func testServiceSourceNewServiceSource(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, err := NewServiceSource(
|
_, err := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
fake.NewSimpleClientset(),
|
fake.NewSimpleClientset(),
|
||||||
"",
|
"",
|
||||||
ti.annotationFilter,
|
ti.annotationFilter,
|
||||||
@ -1039,6 +1041,7 @@ func testServiceSourceEndpoints(t *testing.T) {
|
|||||||
|
|
||||||
// Create our object under test and get the endpoints.
|
// Create our object under test and get the endpoints.
|
||||||
client, err := NewServiceSource(
|
client, err := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
tc.targetNamespace,
|
tc.targetNamespace,
|
||||||
tc.annotationFilter,
|
tc.annotationFilter,
|
||||||
@ -1227,6 +1230,7 @@ func testMultipleServicesEndpoints(t *testing.T) {
|
|||||||
|
|
||||||
// Create our object under test and get the endpoints.
|
// Create our object under test and get the endpoints.
|
||||||
client, err := NewServiceSource(
|
client, err := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
tc.targetNamespace,
|
tc.targetNamespace,
|
||||||
tc.annotationFilter,
|
tc.annotationFilter,
|
||||||
@ -1391,6 +1395,7 @@ func TestClusterIpServices(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// Create our object under test and get the endpoints.
|
// Create our object under test and get the endpoints.
|
||||||
client, _ := NewServiceSource(
|
client, _ := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
tc.targetNamespace,
|
tc.targetNamespace,
|
||||||
tc.annotationFilter,
|
tc.annotationFilter,
|
||||||
@ -1960,6 +1965,7 @@ func TestServiceSourceNodePortServices(t *testing.T) {
|
|||||||
|
|
||||||
// Create our object under test and get the endpoints.
|
// Create our object under test and get the endpoints.
|
||||||
client, _ := NewServiceSource(
|
client, _ := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
tc.targetNamespace,
|
tc.targetNamespace,
|
||||||
tc.annotationFilter,
|
tc.annotationFilter,
|
||||||
@ -2295,6 +2301,7 @@ func TestHeadlessServices(t *testing.T) {
|
|||||||
|
|
||||||
// Create our object under test and get the endpoints.
|
// Create our object under test and get the endpoints.
|
||||||
client, _ := NewServiceSource(
|
client, _ := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
tc.targetNamespace,
|
tc.targetNamespace,
|
||||||
"",
|
"",
|
||||||
@ -2651,6 +2658,7 @@ func TestHeadlessServicesHostIP(t *testing.T) {
|
|||||||
|
|
||||||
// Create our object under test and get the endpoints.
|
// Create our object under test and get the endpoints.
|
||||||
client, _ := NewServiceSource(
|
client, _ := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
tc.targetNamespace,
|
tc.targetNamespace,
|
||||||
"",
|
"",
|
||||||
@ -2762,6 +2770,7 @@ func TestExternalServices(t *testing.T) {
|
|||||||
|
|
||||||
// Create our object under test and get the endpoints.
|
// Create our object under test and get the endpoints.
|
||||||
client, _ := NewServiceSource(
|
client, _ := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
tc.targetNamespace,
|
tc.targetNamespace,
|
||||||
"",
|
"",
|
||||||
@ -2815,6 +2824,7 @@ func BenchmarkServiceEndpoints(b *testing.B) {
|
|||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
|
|
||||||
client, err := NewServiceSource(
|
client, err := NewServiceSource(
|
||||||
|
context.TODO(),
|
||||||
kubernetes,
|
kubernetes,
|
||||||
v1.NamespaceAll,
|
v1.NamespaceAll,
|
||||||
"",
|
"",
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package source
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -159,10 +160,10 @@ func (p *SingletonClientGenerator) OpenShiftClient() (openshift.Interface, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ByNames returns multiple Sources given multiple names.
|
// ByNames returns multiple Sources given multiple names.
|
||||||
func ByNames(p ClientGenerator, names []string, cfg *Config) ([]Source, error) {
|
func ByNames(ctx context.Context, p ClientGenerator, names []string, cfg *Config) ([]Source, error) {
|
||||||
sources := []Source{}
|
sources := []Source{}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
source, err := BuildWithConfig(name, p, cfg)
|
source, err := BuildWithConfig(ctx, name, p, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -173,32 +174,32 @@ func ByNames(p ClientGenerator, names []string, cfg *Config) ([]Source, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuildWithConfig allows to generate a Source implementation from the shared config
|
// BuildWithConfig allows to generate a Source implementation from the shared config
|
||||||
func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, error) {
|
func BuildWithConfig(ctx context.Context, source string, p ClientGenerator, cfg *Config) (Source, error) {
|
||||||
switch source {
|
switch source {
|
||||||
case "node":
|
case "node":
|
||||||
client, err := p.KubeClient()
|
client, err := p.KubeClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewNodeSource(client, cfg.AnnotationFilter, cfg.FQDNTemplate)
|
return NewNodeSource(ctx, client, cfg.AnnotationFilter, cfg.FQDNTemplate)
|
||||||
case "service":
|
case "service":
|
||||||
client, err := p.KubeClient()
|
client, err := p.KubeClient()
|
||||||
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, cfg.LabelFilter)
|
return NewServiceSource(ctx, 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.LabelFilter, cfg.IngressClassNames)
|
return NewIngressSource(ctx, 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 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewPodSource(client, cfg.Namespace, cfg.Compatibility)
|
return NewPodSource(ctx, client, cfg.Namespace, cfg.Compatibility)
|
||||||
case "istio-gateway":
|
case "istio-gateway":
|
||||||
kubernetesClient, err := p.KubeClient()
|
kubernetesClient, err := p.KubeClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -208,7 +209,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
return NewIstioGatewaySource(ctx, kubernetesClient, istioClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
||||||
case "istio-virtualservice":
|
case "istio-virtualservice":
|
||||||
kubernetesClient, err := p.KubeClient()
|
kubernetesClient, err := p.KubeClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -218,7 +219,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewIstioVirtualServiceSource(kubernetesClient, istioClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
return NewIstioVirtualServiceSource(ctx, kubernetesClient, istioClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
||||||
case "cloudfoundry":
|
case "cloudfoundry":
|
||||||
cfClient, err := p.CloudFoundryClient(cfg.CFAPIEndpoint, cfg.CFUsername, cfg.CFPassword)
|
cfClient, err := p.CloudFoundryClient(cfg.CFAPIEndpoint, cfg.CFUsername, cfg.CFPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -234,13 +235,13 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewAmbassadorHostSource(dynamicClient, kubernetesClient, cfg.Namespace)
|
return NewAmbassadorHostSource(ctx, dynamicClient, kubernetesClient, cfg.Namespace)
|
||||||
case "contour-httpproxy":
|
case "contour-httpproxy":
|
||||||
dynamicClient, err := p.DynamicKubernetesClient()
|
dynamicClient, err := p.DynamicKubernetesClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewContourHTTPProxySource(dynamicClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
return NewContourHTTPProxySource(ctx, dynamicClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
||||||
case "gloo-proxy":
|
case "gloo-proxy":
|
||||||
kubernetesClient, err := p.KubeClient()
|
kubernetesClient, err := p.KubeClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -256,7 +257,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, cfg.LabelFilter, cfg.OCPRouterName)
|
return NewOcpRouteSource(ctx, ocpClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter, cfg.OCPRouterName)
|
||||||
case "fake":
|
case "fake":
|
||||||
return NewFakeSource(cfg.FQDNTemplate)
|
return NewFakeSource(cfg.FQDNTemplate)
|
||||||
case "connector":
|
case "connector":
|
||||||
@ -291,7 +292,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewKongTCPIngressSource(dynamicClient, kubernetesClient, cfg.Namespace, cfg.AnnotationFilter)
|
return NewKongTCPIngressSource(ctx, dynamicClient, kubernetesClient, cfg.Namespace, cfg.AnnotationFilter)
|
||||||
}
|
}
|
||||||
return nil, ErrSourceNotFound
|
return nil, ErrSourceNotFound
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package source
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -115,7 +116,7 @@ func (suite *ByNamesTestSuite) TestAllInitialized() {
|
|||||||
}: "TCPIngressesList",
|
}: "TCPIngressesList",
|
||||||
}), nil)
|
}), nil)
|
||||||
|
|
||||||
sources, err := ByNames(mockClientGenerator, []string{"service", "ingress", "istio-gateway", "contour-httpproxy", "kong-tcpingress", "fake"}, minimalConfig)
|
sources, err := ByNames(context.TODO(), mockClientGenerator, []string{"service", "ingress", "istio-gateway", "contour-httpproxy", "kong-tcpingress", "fake"}, minimalConfig)
|
||||||
suite.NoError(err, "should not generate errors")
|
suite.NoError(err, "should not generate errors")
|
||||||
suite.Len(sources, 6, "should generate all six sources")
|
suite.Len(sources, 6, "should generate all six sources")
|
||||||
}
|
}
|
||||||
@ -124,7 +125,7 @@ func (suite *ByNamesTestSuite) TestOnlyFake() {
|
|||||||
mockClientGenerator := new(MockClientGenerator)
|
mockClientGenerator := new(MockClientGenerator)
|
||||||
mockClientGenerator.On("KubeClient").Return(fakeKube.NewSimpleClientset(), nil)
|
mockClientGenerator.On("KubeClient").Return(fakeKube.NewSimpleClientset(), nil)
|
||||||
|
|
||||||
sources, err := ByNames(mockClientGenerator, []string{"fake"}, minimalConfig)
|
sources, err := ByNames(context.TODO(), mockClientGenerator, []string{"fake"}, minimalConfig)
|
||||||
suite.NoError(err, "should not generate errors")
|
suite.NoError(err, "should not generate errors")
|
||||||
suite.Len(sources, 1, "should generate fake source")
|
suite.Len(sources, 1, "should generate fake source")
|
||||||
suite.Nil(mockClientGenerator.kubeClient, "client should not be created")
|
suite.Nil(mockClientGenerator.kubeClient, "client should not be created")
|
||||||
@ -134,7 +135,7 @@ func (suite *ByNamesTestSuite) TestSourceNotFound() {
|
|||||||
mockClientGenerator := new(MockClientGenerator)
|
mockClientGenerator := new(MockClientGenerator)
|
||||||
mockClientGenerator.On("KubeClient").Return(fakeKube.NewSimpleClientset(), nil)
|
mockClientGenerator.On("KubeClient").Return(fakeKube.NewSimpleClientset(), nil)
|
||||||
|
|
||||||
sources, err := ByNames(mockClientGenerator, []string{"foo"}, minimalConfig)
|
sources, err := ByNames(context.TODO(), mockClientGenerator, []string{"foo"}, minimalConfig)
|
||||||
suite.Equal(err, ErrSourceNotFound, "should return source not found")
|
suite.Equal(err, ErrSourceNotFound, "should return source not found")
|
||||||
suite.Len(sources, 0, "should not returns any source")
|
suite.Len(sources, 0, "should not returns any source")
|
||||||
}
|
}
|
||||||
@ -143,16 +144,16 @@ func (suite *ByNamesTestSuite) TestKubeClientFails() {
|
|||||||
mockClientGenerator := new(MockClientGenerator)
|
mockClientGenerator := new(MockClientGenerator)
|
||||||
mockClientGenerator.On("KubeClient").Return(nil, errors.New("foo"))
|
mockClientGenerator.On("KubeClient").Return(nil, errors.New("foo"))
|
||||||
|
|
||||||
_, err := ByNames(mockClientGenerator, []string{"service"}, minimalConfig)
|
_, err := ByNames(context.TODO(), mockClientGenerator, []string{"service"}, minimalConfig)
|
||||||
suite.Error(err, "should return an error if kubernetes client cannot be created")
|
suite.Error(err, "should return an error if kubernetes client cannot be created")
|
||||||
|
|
||||||
_, err = ByNames(mockClientGenerator, []string{"ingress"}, minimalConfig)
|
_, err = ByNames(context.TODO(), mockClientGenerator, []string{"ingress"}, minimalConfig)
|
||||||
suite.Error(err, "should return an error if kubernetes client cannot be created")
|
suite.Error(err, "should return an error if kubernetes client cannot be created")
|
||||||
|
|
||||||
_, err = ByNames(mockClientGenerator, []string{"istio-gateway"}, minimalConfig)
|
_, err = ByNames(context.TODO(), mockClientGenerator, []string{"istio-gateway"}, minimalConfig)
|
||||||
suite.Error(err, "should return an error if kubernetes client cannot be created")
|
suite.Error(err, "should return an error if kubernetes client cannot be created")
|
||||||
|
|
||||||
_, err = ByNames(mockClientGenerator, []string{"kong-tcpingress"}, minimalConfig)
|
_, err = ByNames(context.TODO(), mockClientGenerator, []string{"kong-tcpingress"}, minimalConfig)
|
||||||
suite.Error(err, "should return an error if kubernetes client cannot be created")
|
suite.Error(err, "should return an error if kubernetes client cannot be created")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,10 +163,10 @@ func (suite *ByNamesTestSuite) TestIstioClientFails() {
|
|||||||
mockClientGenerator.On("IstioClient").Return(nil, errors.New("foo"))
|
mockClientGenerator.On("IstioClient").Return(nil, errors.New("foo"))
|
||||||
mockClientGenerator.On("DynamicKubernetesClient").Return(nil, errors.New("foo"))
|
mockClientGenerator.On("DynamicKubernetesClient").Return(nil, errors.New("foo"))
|
||||||
|
|
||||||
_, err := ByNames(mockClientGenerator, []string{"istio-gateway"}, minimalConfig)
|
_, err := ByNames(context.TODO(), mockClientGenerator, []string{"istio-gateway"}, minimalConfig)
|
||||||
suite.Error(err, "should return an error if istio client cannot be created")
|
suite.Error(err, "should return an error if istio client cannot be created")
|
||||||
|
|
||||||
_, err = ByNames(mockClientGenerator, []string{"contour-httpproxy"}, minimalConfig)
|
_, err = ByNames(context.TODO(), mockClientGenerator, []string{"contour-httpproxy"}, minimalConfig)
|
||||||
suite.Error(err, "should return an error if contour client cannot be created")
|
suite.Error(err, "should return an error if contour client cannot be created")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user