diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 06cf09e1d5..a55ae68f05 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -23,7 +23,7 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v5
with:
- go-version: '1.22.2'
+ go-version: '1.22.4'
id: go
- name: Check out code into the Go module directory
diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml
index 554e1ed2ae..c44d4960cb 100644
--- a/.github/workflows/codeql-analysis.yaml
+++ b/.github/workflows/codeql-analysis.yaml
@@ -30,7 +30,7 @@ jobs:
- name: Install go version
uses: actions/setup-go@v5
with:
- go-version: '^1.22.2'
+ go-version: '^1.22.4'
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 8c46892a86..7c70de85b2 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -26,7 +26,7 @@ jobs:
- uses: actions/setup-go@v5
with:
- go-version: '^1.22.2'
+ go-version: '^1.22.4'
- run: |
pip install -r docs/scripts/requirements.txt
diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml
index a3b09abfc0..80fab4d469 100644
--- a/.github/workflows/json-yaml-validate.yml
+++ b/.github/workflows/json-yaml-validate.yml
@@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: json-yaml-validate
- uses: GrantBirki/json-yaml-validate@v2.7.1
+ uses: GrantBirki/json-yaml-validate@v3.0.0
with:
comment: "true" # enable comment mode
yaml_exclude_regex: "(charts/external-dns/templates.*|mkdocs.yml)"
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 00e9ff9bf6..e3c0091c4f 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -23,7 +23,7 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v5
with:
- go-version: '1.22.2'
+ go-version: '1.22.4'
id: go
- name: Check out code into the Go module directory
diff --git a/.github/workflows/staging-image-tester.yaml b/.github/workflows/staging-image-tester.yaml
index 97fe6eb3ac..d9f41d3128 100644
--- a/.github/workflows/staging-image-tester.yaml
+++ b/.github/workflows/staging-image-tester.yaml
@@ -23,7 +23,7 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v5
with:
- go-version: '1.22.2'
+ go-version: '1.22.4'
id: go
- name: Check out code into the Go module directory
diff --git a/Makefile b/Makefile
index 8607969339..be24ff476e 100644
--- a/Makefile
+++ b/Makefile
@@ -33,11 +33,7 @@ controller-gen:
ifeq (, $(shell which controller-gen))
@{ \
set -e ;\
- CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
- cd $$CONTROLLER_GEN_TMP_DIR ;\
- go mod init tmp ;\
- go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.14.0 ;\
- rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
+ go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.15.0 ;\
}
CONTROLLER_GEN=$(GOBIN)/controller-gen
else
diff --git a/README.md b/README.md
index 255bec361b..6efa7b4df9 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,6 @@ ExternalDNS allows you to keep selected zones (via `--domain-filter`) synchroniz
* [RcodeZero](https://www.rcodezero.at/)
* [DigitalOcean](https://www.digitalocean.com/products/networking)
* [DNSimple](https://dnsimple.com/)
-* [Infoblox](https://www.infoblox.com/products/dns/)
* [Dyn](https://dyn.com/dns/)
* [OpenStack Designate](https://docs.openstack.org/designate/latest/)
* [PowerDNS](https://www.powerdns.com/)
@@ -86,8 +85,10 @@ Known providers using webhooks:
| GleSYS | https://github.com/glesys/external-dns-glesys |
| Hetzner | https://github.com/mconfalonieri/external-dns-hetzner-webhook |
| IONOS | https://github.com/ionos-cloud/external-dns-ionos-webhook |
+| Infoblox | https://github.com/AbsaOSS/external-dns-infoblox-webhook |
| Netcup | https://github.com/mrueg/external-dns-netcup-webhook |
| STACKIT | https://github.com/stackitcloud/external-dns-stackit-webhook |
+| Unifi | https://github.com/kashalls/external-dns-unifi-webhook |
## Status of in-tree providers
@@ -116,7 +117,6 @@ The following table clarifies the current status of the providers according to t
| RcodeZero | Alpha | |
| DigitalOcean | Alpha | |
| DNSimple | Alpha | |
-| Infoblox | Alpha | @saileshgiri |
| Dyn | Alpha | |
| OpenStack Designate | Alpha | |
| PowerDNS | Alpha | |
@@ -185,7 +185,6 @@ The following tutorials are provided:
* [Using Google's Default Ingress Controller](docs/tutorials/gke.md)
* [Using the Nginx Ingress Controller](docs/tutorials/nginx-ingress.md)
* [Headless Services](docs/tutorials/hostport.md)
-* [Infoblox](docs/tutorials/infoblox.md)
* [Istio Gateway Source](docs/tutorials/istio.md)
* [Kubernetes Security Context](docs/tutorials/security-context.md)
* [Linode](docs/tutorials/linode.md)
diff --git a/cloudbuild.yaml b/cloudbuild.yaml
index dce985c927..63f96d0768 100644
--- a/cloudbuild.yaml
+++ b/cloudbuild.yaml
@@ -4,7 +4,7 @@ options:
substitution_option: ALLOW_LOOSE
machineType: 'N1_HIGHCPU_8'
steps:
- - name: 'docker.io/library/golang:1.22.2-bookworm'
+ - name: 'docker.io/library/golang:1.22.4-bookworm'
entrypoint: make
env:
- VERSION=$_GIT_TAG
diff --git a/docs/contributing/crd-source/crd-manifest.yaml b/docs/contributing/crd-source/crd-manifest.yaml
index f5cf971195..5d4a441593 100644
--- a/docs/contributing/crd-source/crd-manifest.yaml
+++ b/docs/contributing/crd-source/crd-manifest.yaml
@@ -3,7 +3,8 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/external-dns/pull/2007
+ controller-gen.kubebuilder.io/version: v0.15.0
name: dnsendpoints.externaldns.k8s.io
spec:
group: externaldns.k8s.io
diff --git a/docs/overrides/partials/copyright.html b/docs/overrides/partials/copyright.html
new file mode 100644
index 0000000000..b7dc7fa3f1
--- /dev/null
+++ b/docs/overrides/partials/copyright.html
@@ -0,0 +1,40 @@
+
+
+
+
+ {% if config.trademark %}
+
+ The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation,
+ please see our Trademark Usage page.
+
+ {% endif %}
+ {% if not config.extra.generator == false %}
+ Made with
+
+ Material for MkDocs
+
+ {% endif %}
+
\ No newline at end of file
diff --git a/docs/proposal/multi-target.md b/docs/proposal/multi-target.md
index 0d0a007616..a59020235f 100644
--- a/docs/proposal/multi-target.md
+++ b/docs/proposal/multi-target.md
@@ -1,64 +1,64 @@
-# Multiple Targets per hostname
+# Multiple Targets per hostname
*(November 2017)*
-## Purpose
+## Purpose
-One should be able to define multiple targets (IPs/Hostnames) in the **same** Kubernetes resource object and expect
-ExternalDNS create DNS record(s) with a specified hostname and all targets. So far the connection between k8s resources (ingress/services) and DNS records
+One should be able to define multiple targets (IPs/Hostnames) in the **same** Kubernetes resource object and expect
+ExternalDNS create DNS record(s) with a specified hostname and all targets. So far the connection between k8s resources (ingress/services) and DNS records
were not streamlined. This proposal aims to make the connection explicit, making k8s resources acquire or release certain DNS names. As long as the resource
-ingress/service owns the record it can have multiple targets enable iff they are specified in the same resource.
+ingress/service owns the record it can have multiple targets enable iff they are specified in the same resource.
-## Use cases
+## Use cases
See https://github.com/kubernetes-sigs/external-dns/issues/239
-## Current behaviour
+## Current behaviour
*(as of the moment of writing)*
-Central piece of enabling multi-target is having consistent and correct behaviour in `plan` component in regards to how endpoints generated
+Central piece of enabling multi-target is having consistent and correct behaviour in `plan` component in regards to how endpoints generated
from kubernetes resources are mapped to dns records. Current implementation of the `plan` has inconsistent behaviour in the following scenarios, all
-of which must be resolved before multi-target support can be enabled in the provider implementations:
+of which must be resolved before multi-target support can be enabled in the provider implementations:
-1. No records registered so far. Two **different** ingresses request same hostname but different targets, e.g. Ingress A: example.com -> 1.1.1.1 and Ingress B: example.com -> 2.2.2.2
+1. No records registered so far. Two **different** ingresses request same hostname but different targets, e.g. Ingress A: example.com -> 1.1.1.1 and Ingress B: example.com -> 2.2.2.2
* *Current Behaviour*: both are added to the "Create" (records to be created) list and passed to Provider
- * *Expected Behaviour*: only one (random/ or according to predefined strategy) should be chosen and passed to Provider
-
- **NOTE**: while this seems to go against multi-target support, this is done so no other resource can "hijack" already created DNS record. Multi targets are supported only
-on per single resource basis
+ * *Expected Behaviour*: only one (random/ or according to predefined strategy) should be chosen and passed to Provider
-2. Now let's say Ingress A was chosen and successfully created, but both ingress A and B are still there. So on next iteration ExternalDNS would see both again in the Desired list.
+ **NOTE**: while this seems to go against multi-target support, this is done so no other resource can "hijack" already created DNS record. Multi targets are supported only
+on per single resource basis
+
+2. Now let's say Ingress A was chosen and successfully created, but both ingress A and B are still there. So on next iteration ExternalDNS would see both again in the Desired list.
* *Current Behaviour*: DNS record target will change to that of Ingress B.
* *Expected Behaviour*: Ingress A should stay unchanged. Ingress B record is not created
3. DNS record for Ingress A was created but its target has changed. Ingress B is still there
- * *Current Behaviour*: Undetermined behaviour based on which ingress will be parsed last.
+ * *Current Behaviour*: Undetermined behaviour based on which ingress will be parsed last.
* *Expected Behaviour*: DNS record should point to the new target specified in A. Ingress B should still be ignored.
-
- **NOTE**: both 2. and 3. can be resolved if External DNS is aware which resource has already acquired DNS record
-
+
+ **NOTE**: both 2. and 3. can be resolved if External DNS is aware which resource has already acquired DNS record
+
4. Ingress C has multiple targets: 1.1.1.1 and 2.2.2.2
- * *Current Behaviour*: Both targets are split into different endpoints and we end up in one of the cases above
- * *Expected Behaviour*: Endpoint should contain list of targets and treated as one ingress object.
+ * *Current Behaviour*: Both targets are split into different endpoints and we end up in one of the cases above
+ * *Expected Behaviour*: Endpoint should contain list of targets and treated as one ingress object.
## Requirements and assumptions
-For this feature to work we have to make sure that:
+For this feature to work we have to make sure that:
-1. DNS records are now owned by certain ingress/service resources. For External DNS it would mean that TXT records now
-should store back-reference for the resource this record was created for, i.e. `"heritage=external-dns,external-dns/resource=ingress/default/my-ingress-object-name"`
-2. DNS records are updated only:
+1. DNS records are now owned by certain ingress/service resources. For External DNS it would mean that TXT records now
+should store back-reference for the resource this record was created for, i.e. `"heritage=external-dns,external-dns/resource=ingress/default/my-ingress-object-name"`
+2. DNS records are updated only:
- - If owning resource target list has changed
+ - If owning resource target list has changed
- If owning resource record is not found in the desired list (meaning it was deleted), therefore it will now be owned by another record. So its target list will be updated
- - Changes related to other record properties (e.g. TTL)
+ - Changes related to other record properties (e.g. TTL)
-4. All of the issues described in `Current Behaviour` sections are resolved
+4. All of the issues described in `Current Behaviour` sections are resolved
Once Create/Update/Delete lists are calculated correctly (this is where conflicts based on requested DNS names are resolved) they are passed to `provider`, where
`provider` specific implementation will decide how to convert the structures into required formats. If DNS provider does not (or partially) support multi targets
-then it is up to the provider to make sure that the change list of records passed to the DNS provider API is valid. **TODO**: explain best strategy.
+then it is up to the provider to make sure that the change list of records passed to the DNS provider API is valid. **TODO**: explain best strategy.
Additionally see https://github.com/kubernetes-sigs/external-dns/issues/258
@@ -66,54 +66,53 @@ Additionally see https://github.com/kubernetes-sigs/external-dns/issues/258
Brief summary of open PRs and what they are trying to address:
-### PRs
+### PRs
1. https://github.com/kubernetes-sigs/external-dns/pull/243 - first attempt to add support for multiple targets. It is lagging far behind from tip
-
+
*what it does*: unfinished attempt to extend `Endpoint` struct, for it to allow multiple targets (essentially `target string -> targets []string`)
-
- *action*: evaluate if rebasing makes sense, or we can just close it.
-
-2. https://github.com/kubernetes-sigs/external-dns/pull/261 - attempt to rework `plan` to make it work correctly with multiple targets.
-
+
+ *action*: evaluate if rebasing makes sense, or we can just close it.
+
+2. https://github.com/kubernetes-sigs/external-dns/pull/261 - attempt to rework `plan` to make it work correctly with multiple targets.
+
*what it does* : attempts to fix issues with `plan` described in `Current Behaviour` section above. Included tests reveal the current problem with `plan`
-
+
*action*: rebase on default branch and make necessary changes to satisfy requirements listed in this document including back-reference to owning record
-
-3. https://github.com/kubernetes-sigs/external-dns/pull/326 - attempt to add multiple target support.
-
+
+3. https://github.com/kubernetes-sigs/external-dns/pull/326 - attempt to add multiple target support.
+
*what it does*: for each pair `DNS Name` + `Record Type` it aggregates **all** targets from the cluster and passes them to Provider. It adds basic support
- for DO, Azure, Cloudflare, AWS, GCP, however those are not tested (?). (DNSSimple and Infoblox providers were not updated)
-
- *action*: the `plan` logic will probably needs to be reworked, however the rest concerning support in Providers and extending `Endpoint` struct can be reused.
+
+ *action*: the `plan` logic will probably needs to be reworked, however the rest concerning support in Providers and extending `Endpoint` struct can be reused.
Rebase on default branch and add missing pieces. Depends on `2`.
-
+
Related PRs: https://github.com/kubernetes-sigs/external-dns/pull/331/files, https://github.com/kubernetes-sigs/external-dns/pull/347/files - aiming at AWS Route53 weighted records.
These PRs should be considered after common agreement about the way to address multi-target support is achieved. Related discussion: https://github.com/kubernetes-sigs/external-dns/issues/196
### How to proceed from here
-The following steps are needed:
-1. Make sure consensus regarding the approach is achieved via collaboration on the current document
+The following steps are needed:
+1. Make sure consensus regarding the approach is achieved via collaboration on the current document
2. Notify all PR (see above) authors about the agreed approach
-3. Implementation:
-
+3. Implementation:
+
a. `Plan` is working as expected - either based on #261 above or from scratch. `Plan` should be working correctly regardless of multi-target support
-
+
b. Extensive testing making sure new `plan` does not introduce any breaking changes
-
+
c. Change Endpoint struct to support multiple targets - based on #326 - integrate it with new `plan` @sethpollack
-
- d. Make sure new endpoint format can still be used in providers which have only partial support for multi targets ~~**TODO**: how ?~~ . This is to be done by simply using first target in the targets list.
-
- e. Add support for multi target which are already addressed in #326. It goes alongside c. and can be based on the same PR @sethpollack. New providers
- added since then should maintain same functionality.
+
+ d. Make sure new endpoint format can still be used in providers which have only partial support for multi targets ~~**TODO**: how ?~~ . This is to be done by simply using first target in the targets list.
+
+ e. Add support for multi target which are already addressed in #326. It goes alongside c. and can be based on the same PR @sethpollack. New providers
+ added since then should maintain same functionality.
5. Extensive testing on **all** providers before making new release
-6. Update all related documentation and explain how multi targets are supported on per provider basis
-7. Think of introducing weighted records (see PRs section above) and making them configurable.
-
-## Open questions
+6. Update all related documentation and explain how multi targets are supported on per provider basis
+7. Think of introducing weighted records (see PRs section above) and making them configurable.
+
+## Open questions
- Handling cases when ingress/service targets include both hostnames and IPs - postpone this until use cases occurs
- "Weighted records scope": https://github.com/kubernetes-sigs/external-dns/issues/196 - this should be considered once multi-target support is implemented
diff --git a/docs/tutorials/coredns.md b/docs/tutorials/coredns.md
index 13d7877576..bc673494c5 100644
--- a/docs/tutorials/coredns.md
+++ b/docs/tutorials/coredns.md
@@ -86,6 +86,7 @@ helm install --name my-coredns --values values.yaml stable/coredns
## Installing ExternalDNS
### Install external ExternalDNS
ETCD_URLS is configured to etcd client service address.
+Optionnally, you can configure ETCD_USERNAME and ETCD_PASSWORD for authenticating to etcd.
#### Manifest (for clusters without RBAC enabled)
diff --git a/docs/tutorials/infoblox.md b/docs/tutorials/infoblox.md
deleted file mode 100644
index ce8ed958f1..0000000000
--- a/docs/tutorials/infoblox.md
+++ /dev/null
@@ -1,290 +0,0 @@
-# Setting up ExternalDNS for Infoblox
-
-This tutorial describes how to setup ExternalDNS for usage with Infoblox.
-
-Make sure to use **>=0.4.6** version of ExternalDNS for this tutorial. The only WAPI version that
-has been validated is **v2.3.1**. It is assumed that the API user has rights to create objects of
-the following types: `zone_auth`, `record:a`, `record:cname`, `record:txt`.
-
-This tutorial assumes you have substituted the correct values for the following environment variables:
-
-```
-export GRID_HOST=127.0.0.1
-export WAPI_PORT=443
-export WAPI_VERSION=2.3.1
-export WAPI_USERNAME=admin
-export WAPI_PASSWORD=infoblox
-```
-
-## Creating an Infoblox DNS zone
-
-The Infoblox provider for ExternalDNS will find suitable zones for domains it manages; it will
-not automatically create zones.
-
-Create an Infoblox DNS zone for "example.com":
-
-```
-$ curl -kl \
- -X POST \
- -d fqdn=example.com \
- -u ${WAPI_USERNAME}:${WAPI_PASSWORD} \
- https://${GRID_HOST}:${WAPI_PORT}/wapi/v${WAPI_VERSION}/zone_auth
-```
-
-Substitute a domain you own for "example.com" if desired.
-
-## Creating an Infoblox Configuration Secret
-
-For ExternalDNS to access the Infoblox API, create a Kubernetes secret.
-
-To create the secret:
-
-```
-$ kubectl create secret generic external-dns \
- --from-literal=EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME=${WAPI_USERNAME} \
- --from-literal=EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD=${WAPI_PASSWORD}
-```
-
-## 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
- image: registry.k8s.io/external-dns/external-dns:v0.14.2
- args:
- - --source=service
- - --domain-filter=example.com # (optional) limit to only example.com domains.
- - --provider=infoblox
- - --infoblox-grid-host=${GRID_HOST} # (required) IP of the Infoblox Grid host.
- - --infoblox-wapi-port=443 # (optional) Infoblox WAPI port. The default is "443".
- - --infoblox-wapi-version=2.3.1 # (optional) Infoblox WAPI version. The default is "2.3.1"
- - --infoblox-ssl-verify # (optional) Use --no-infoblox-ssl-verify to skip server certificate verification.
- - --infoblox-create-ptr # (optional) Use --infoblox-create-ptr to create a ptr entry in addition to an entry.
- - --infoblox-view="" # (optional) DNS view (default: "")
- env:
- - name: EXTERNAL_DNS_INFOBLOX_HTTP_POOL_CONNECTIONS
- value: "10" # (optional) Infoblox WAPI request connection pool size. The default is "10".
- - name: EXTERNAL_DNS_INFOBLOX_HTTP_REQUEST_TIMEOUT
- value: "60" # (optional) Infoblox WAPI request timeout in seconds. The default is "60".
- - name: EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME
- valueFrom:
- secretKeyRef:
- name: external-dns
- key: EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME
- - name: EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD
- valueFrom:
- secretKeyRef:
- name: external-dns
- key: EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD
-```
-
-### 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: registry.k8s.io/external-dns/external-dns:v0.14.2
- args:
- - --source=service
- - --domain-filter=example.com # (optional) limit to only example.com domains.
- - --provider=infoblox
- - --infoblox-grid-host=${GRID_HOST} # (required) IP of the Infoblox Grid host.
- - --infoblox-wapi-port=443 # (optional) Infoblox WAPI port. The default is "443".
- - --infoblox-wapi-version=2.3.1 # (optional) Infoblox WAPI version. The default is "2.3.1"
- - --infoblox-ssl-verify # (optional) Use --no-infoblox-ssl-verify to skip server certificate verification.
- - --infoblox-create-ptr # (optional) Use --infoblox-create-ptr to create a ptr entry in addition to an entry.
- - --infoblox-view="" # (optional) DNS view (default: "")
- env:
- - name: EXTERNAL_DNS_INFOBLOX_HTTP_POOL_CONNECTIONS
- value: "10" # (optional) Infoblox WAPI request connection pool size. The default is "10".
- - name: EXTERNAL_DNS_INFOBLOX_HTTP_REQUEST_TIMEOUT
- value: "60" # (optional) Infoblox WAPI request timeout in seconds. The default is "60".
- - name: EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME
- valueFrom:
- secretKeyRef:
- name: external-dns
- key: EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME
- - name: EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD
- valueFrom:
- secretKeyRef:
- name: external-dns
- key: EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD
-```
-
-
-## 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: example.com
-spec:
- selector:
- app: nginx
- type: LoadBalancer
- ports:
- - protocol: TCP
- port: 80
- targetPort: 80
-```
-
-Note the annotation on the service; use the same hostname as the Infoblox DNS zone created above. The annotation may also be a subdomain
-of the DNS zone (e.g. 'www.example.com').
-
-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:
-
-```
-$ kubectl create -f nginx.yaml
-```
-
-It takes a little while for the Infoblox cloud provider to create an external IP for the service. Check the status by running
-`kubectl get services nginx`. If the `EXTERNAL-IP` field shows an address, the service is ready to be accessed externally.
-
-Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize
-the Infoblox DNS records.
-
-## Verifying Infoblox DNS records
-
-Run the following command to view the A records for your Infoblox DNS zone:
-
-```
-$ curl -kl \
- -X GET \
- -u ${WAPI_USERNAME}:${WAPI_PASSWORD} \
- https://${GRID_HOST}:${WAPI_PORT}/wapi/v${WAPI_VERSION}/record:a?zone=example.com
-```
-
-Substitute the zone for the one created above if a different domain was used.
-
-This should show the external IP address of the service as the A record for your domain ('@' indicates the record is for the zone itself).
-
-## Clean up
-
-Now that we have verified that ExternalDNS will automatically manage Infoblox DNS records, we can delete the tutorial's
-DNS zone:
-
-```
-$ curl -kl \
- -X DELETE \
- -u ${WAPI_USERNAME}:${WAPI_PASSWORD} \
- https://${GRID_HOST}:${WAPI_PORT}/wapi/v${WAPI_VERSION}/zone_auth?fqdn=example.com
-```
-
-## Ability to filter results from the zone auth API using a regular expression
-
-There is also the ability to filter results from the Infoblox zone_auth service based upon a regular expression. See the [Infoblox API document](https://www.infoblox.com/wp-content/uploads/infoblox-deployment-infoblox-rest-api.pdf) for examples. To use this feature for the zone_auth service, set the parameter infoblox-fqdn-regex for external-dns to a regular expression that makes sense for you. For instance, to only return hosted zones that start with staging in the test.com domain (like staging.beta.test.com, or staging.test.com), use the following command line option when starting external-dns
-
-```
---infoblox-fqdn-regex=^staging.*test.com$
-```
-
-## Ability to filter A, Host, CNAME and TXT records from the by name using a regular expression
-
-Infoblox supports filtering records by name using a regular expression. See the [Infoblox API document](https://www.infoblox.com/wp-content/uploads/infoblox-deployment-infoblox-rest-api.pdf) for examples. To use this feature, set the parameter infoblox-name-regex for external-dns to a regular expression that makes sense for you. For instance, if all your dns records end with `cluster1.example.com`, you can fetch records matching this style by setting the following:
-
-```
---infoblox-name-regex=cluster1.example.com
-```
-
-## Infoblox PTR record support
-
-There is an option to enable PTR records support for infoblox provider. PTR records allow to do reverse dns search. To enable PTR records support, add following into arguments for external-dns:
-`--infoblox-create-ptr` to allow management of PTR records.
-You can also add a filter for reverse dns zone to limit PTR records to specific zones only:
-`--domain-filter=10.196.0.0/16` change this to the reverse zone(s) as defined in your infoblox.
-Now external-dns will manage PTR records for you.
diff --git a/docs/tutorials/webhook-provider.md b/docs/tutorials/webhook-provider.md
index dd07e727f6..c956ce325a 100644
--- a/docs/tutorials/webhook-provider.md
+++ b/docs/tutorials/webhook-provider.md
@@ -31,11 +31,16 @@ The default recommended port is 8888, and should listen only on localhost (ie: o
**NOTE**: only `5xx` responses will be retried and only `20x` will be considered as successful. All status codes different from those will be considered a failure on ExternalDNS's side.
-
## Metrics support
The metrics should listen ":8080" on `/metrics` following [Open Metrics](https://github.com/OpenObservability/OpenMetrics) format.
+## Custom Annotations
+
+The Webhook provider supports custom annotations for DNS records. This feature allows users to define additional configuration options for DNS records managed by the Webhook provider. Custom annotations are defined using the annotation format `external-dns.alpha.kubernetes.io/webhook-`.
+
+Custom annotations can be used to influence DNS record creation and updates. Providers implementing the Webhook API should document the custom annotations they support and how they affect DNS record management.
+
## Provider registry
To simplify the discovery of providers, we will accept pull requests that will add links to providers in the [README](../../README.md) file. This list will only serve the purpose of simplifying finding providers and will not constitute an official endorsement of any of the externally implemented providers unless otherwise stated.
diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go
index 9c7107a377..1bd2d54fb8 100644
--- a/endpoint/endpoint.go
+++ b/endpoint/endpoint.go
@@ -357,6 +357,7 @@ type DNSEndpointStatus struct {
// +kubebuilder:resource:path=dnsendpoints
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
+// +kubebuilder:metadata:annotations="api-approved.kubernetes.io=https://github.com/kubernetes-sigs/external-dns/pull/2007"
// +versionName=v1alpha1
type DNSEndpoint struct {
diff --git a/go.mod b/go.mod
index 9fcbf58862..a280683478 100644
--- a/go.mod
+++ b/go.mod
@@ -1,42 +1,41 @@
module sigs.k8s.io/external-dns
-go 1.22.2
+go 1.22.4
require (
cloud.google.com/go/compute/metadata v0.3.0
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.16.1
- github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.3.0
+ github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.4.0
github.com/IBM/go-sdk-core/v5 v5.17.3
github.com/IBM/networking-go-sdk v0.46.1
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2
github.com/alecthomas/kingpin/v2 v2.4.0
- github.com/aliyun/alibaba-cloud-sdk-go v1.62.737
+ github.com/aliyun/alibaba-cloud-sdk-go v1.62.758
github.com/ans-group/sdk-go v1.17.0
- github.com/aws/aws-sdk-go v1.53.4
+ github.com/aws/aws-sdk-go v1.53.18
github.com/bodgit/tsig v1.2.2
github.com/cenkalti/backoff/v4 v4.3.0
- github.com/civo/civogo v0.3.69
- github.com/cloudflare/cloudflare-go v0.95.0
+ github.com/civo/civogo v0.3.70
+ github.com/cloudflare/cloudflare-go v0.97.0
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
github.com/datawire/ambassador v1.12.4
github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace
- github.com/digitalocean/godo v1.116.0
+ github.com/digitalocean/godo v1.117.0
github.com/dnsimple/dnsimple-go v1.7.0
github.com/exoscale/egoscale v0.102.3
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
github.com/go-gandi/go-gandi v0.7.0
- github.com/go-logr/logr v1.4.1
+ github.com/go-logr/logr v1.4.2
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
- github.com/gophercloud/gophercloud v1.11.0
+ github.com/gophercloud/gophercloud v1.12.0
github.com/hooklift/gowsdl v0.5.0
- github.com/infobloxopen/infoblox-go-client/v2 v2.6.0
github.com/linki/instrumented_http v0.3.0
- github.com/linode/linodego v1.33.1
+ github.com/linode/linodego v1.35.0
github.com/maxatome/go-testdeep v1.14.0
github.com/miekg/dns v1.1.59
github.com/nesv/go-dynect v0.6.0
@@ -44,35 +43,35 @@ require (
github.com/onsi/ginkgo v1.16.5
github.com/openshift/api v0.0.0-20230607130528-611114dca681
github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3
- github.com/oracle/oci-go-sdk/v65 v65.65.2
+ github.com/oracle/oci-go-sdk/v65 v65.67.0
github.com/ovh/go-ovh v1.5.1
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/pluralsh/gqlclient v1.11.0
github.com/projectcontour/contour v1.29.0
github.com/prometheus/client_golang v1.19.1
- github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26
+ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
- github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.923
- github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.923
- github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.923
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.938
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.938
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.938
github.com/transip/gotransip/v6 v6.24.0
github.com/ultradns/ultradns-sdk-go v1.3.7
github.com/vinyldns/go-vinyldns v0.9.16
github.com/vultr/govultr/v2 v2.17.2
- go.etcd.io/etcd/api/v3 v3.5.13
- go.etcd.io/etcd/client/v3 v3.5.13
+ go.etcd.io/etcd/api/v3 v3.5.14
+ go.etcd.io/etcd/client/v3 v3.5.14
go.uber.org/ratelimit v0.3.1
- golang.org/x/net v0.25.0
- golang.org/x/oauth2 v0.20.0
+ golang.org/x/net v0.26.0
+ golang.org/x/oauth2 v0.21.0
golang.org/x/sync v0.7.0
golang.org/x/time v0.5.0
- google.golang.org/api v0.181.0
- gopkg.in/ns1/ns1-go.v2 v2.10.0
+ google.golang.org/api v0.183.0
+ gopkg.in/ns1/ns1-go.v2 v2.11.0
gopkg.in/yaml.v2 v2.4.0
- istio.io/api v1.22.0
- istio.io/client-go v1.22.0
+ istio.io/api v1.22.1
+ istio.io/client-go v1.22.1
k8s.io/api v0.30.1
k8s.io/apimachinery v0.30.1
k8s.io/client-go v0.30.1
@@ -81,10 +80,10 @@ require (
)
require (
- cloud.google.com/go/auth v0.4.1 // indirect
+ cloud.google.com/go/auth v0.5.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Yamashou/gqlgenc v0.14.0 // indirect
@@ -114,8 +113,8 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.19.0 // indirect
- github.com/go-resty/resty/v2 v2.12.0 // indirect
- github.com/goccy/go-json v0.10.2 // indirect
+ github.com/go-resty/resty/v2 v2.13.1 // indirect
+ github.com/goccy/go-json v0.10.3 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -132,7 +131,7 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/imdario/mergo v0.3.16 // indirect
@@ -187,7 +186,7 @@ require (
github.com/terra-farm/udnssdk v1.3.5 // indirect
github.com/vektah/gqlparser/v2 v2.5.1 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
- go.etcd.io/etcd/client/pkg/v3 v3.5.13 // indirect
+ go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
@@ -197,16 +196,16 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
- golang.org/x/crypto v0.23.0 // indirect
+ golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/mod v0.17.0 // indirect
- golang.org/x/sys v0.20.0 // indirect
- golang.org/x/term v0.20.0 // indirect
- golang.org/x/text v0.15.0 // indirect
- golang.org/x/tools v0.20.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
- google.golang.org/grpc v1.63.2 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+ golang.org/x/term v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
+ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240521202816-d264139d666e // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
+ google.golang.org/grpc v1.64.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
diff --git a/go.sum b/go.sum
index 0e75119494..7262829c54 100644
--- a/go.sum
+++ b/go.sum
@@ -18,8 +18,8 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
-cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg=
-cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro=
+cloud.google.com/go/auth v0.5.1 h1:0QNO7VThG54LUzKiQxv8C6x1YX7lUrzlAa1nVLF8CIw=
+cloud.google.com/go/auth v0.5.1/go.mod h1:vbZT8GjzDf3AVqCcQmqeeM32U9HBFc32vVVAbwDsa6s=
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
@@ -48,12 +48,12 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
git.lukeshu.com/go/libsystemd v0.5.3/go.mod h1:FfDoP0i92r4p5Vn4NCLxvjkd7rCOe6otPa4L6hZg9WM=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbxukD1WuWXOiE9fRdu32f2I=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 h1:1nGuui+4POelzDwI7RG56yfQJHCnKvwfMoU7VsEp+Zg=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0/go.mod h1:99EvauvlcJ1U06amZiksfYz/3aFGyIhWGHVyiZXtBAI=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 h1:H+U3Gk9zY56G3u872L82bk4thcsy2Gghb9ExT4Zvm1o=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0/go.mod h1:mgrmMSgaLp9hmax62XQTd0N4aAqSE5E0DulSpVYK7vc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
@@ -79,8 +79,8 @@ github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.16.1 h1:120diyj8dHg+8cB9VQINqah5NUXBI8fHhsDGg6qyyCA=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.16.1/go.mod h1:mMF9pk71U8aIzMBS+CWq8OL3gLcFCRCiy+wNpE4gDIE=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
-github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.3.0 h1:doaNKL5riraIgQjvp9c+5V1VxhJtGU2zHA8pKD36KEQ=
-github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.3.0/go.mod h1:5af0xTkIeNbzWSXOLReMnWy6SC9gVyxdXibvkKIby9o=
+github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.4.0 h1:Q+zEWnb3z9vfWOkCRlNziO5Pd7i2xwXFRMWQ5RU+CKA=
+github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.4.0/go.mod h1:XxWyb5MQDU4GnRBSDZpGgIFwfbcn+GAUbPKS8CR8Bxc=
github.com/IBM/go-sdk-core/v5 v5.17.3 h1:CZSVCKzhQc/hRQZOtuEmi9dlNtWMnxJvOsPtQKP7cZ4=
github.com/IBM/go-sdk-core/v5 v5.17.3/go.mod h1:GatGZpxlo1KaxiRN6E10/rNgWtUtx1hN/GoHSCaSPKA=
github.com/IBM/networking-go-sdk v0.46.1 h1:DnnLR7YDJQRE38/nIeeD7PaKyxpoiIe/aW7NP6bcPHM=
@@ -126,8 +126,8 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
-github.com/aliyun/alibaba-cloud-sdk-go v1.62.737 h1:ZJQHOp8O0RpldZ8XQwCSlpiDMkiYwcqi1rTAs/7oxQY=
-github.com/aliyun/alibaba-cloud-sdk-go v1.62.737/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.758 h1:jAq1MJDZkNZlqSAmzdyu5vauCB2O1Ki8BsJNkcFER00=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.758/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
@@ -152,8 +152,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.53.4 h1:SNCHaUqS3KMNl6fzwUv+iNl3VT9Y5ULfbbk6z4EMSnY=
-github.com/aws/aws-sdk-go v1.53.4/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aws/aws-sdk-go v1.53.18 h1:BeMeCK5e3bDGJj675FhnO94zRci8O35ombWXRvYomJs=
+github.com/aws/aws-sdk-go v1.53.18/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -186,12 +186,12 @@ github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/civo/civogo v0.3.69 h1:Not+F3Z1mxtXjMvDhUD5Nwi/1ql3uBT0ioRfhKXYhOA=
-github.com/civo/civogo v0.3.69/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM=
+github.com/civo/civogo v0.3.70 h1:QPuFm5EmpkScbdFo5/6grcG2xcvd/lgdolOtENT04Ac=
+github.com/civo/civogo v0.3.70/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cloudflare/cloudflare-go v0.95.0 h1:VCOZWcIdcbQw1CwT40w0wxqG/wRbp/M5WpWfn50nVCo=
-github.com/cloudflare/cloudflare-go v0.95.0/go.mod h1:X0MKeYo7qpA162hx9N51EG+cSzgWq8wguF9Oe+kF+7I=
+github.com/cloudflare/cloudflare-go v0.97.0 h1:feZRGiRF1EbljnNIYdt8014FnOLtC3CCvgkLXu915ks=
+github.com/cloudflare/cloudflare-go v0.97.0/go.mod h1:JXRwuTfHpe5xFg8xytc2w0XC6LcrFsBVMS4WlVaiGg8=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s=
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@@ -255,8 +255,8 @@ github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace/go.mod h1:TK05
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/digitalocean/godo v1.116.0 h1:SuF/Imd1/dE/nYrUFVkJ2itesQNnJQE1a/vmtHknxeE=
-github.com/digitalocean/godo v1.116.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo=
+github.com/digitalocean/godo v1.117.0 h1:WVlTe09melDYTd7VCVyvHcNWbgB+uI1O115+5LOtdSw=
+github.com/digitalocean/godo v1.117.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
@@ -350,8 +350,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
@@ -426,8 +426,8 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
-github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA=
-github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0=
+github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
+github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -445,8 +445,8 @@ github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
-github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godror/godror v0.13.3/go.mod h1:2ouUT4kdhUBk7TAkHWD4SN0CdI0pgEQbo8FVHhbSKWg=
@@ -572,8 +572,8 @@ github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1a
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gookit/color v1.2.3/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
-github.com/gophercloud/gophercloud v1.11.0 h1:ls0O747DIq1D8SUHc7r2vI8BFbMLeLFuENaAIfEx7OM=
-github.com/gophercloud/gophercloud v1.11.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
+github.com/gophercloud/gophercloud v1.12.0 h1:Jrz16vPAL93l80q16fp8NplrTCp93y7rZh2P3Q4Yq7g=
+github.com/gophercloud/gophercloud v1.12.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
@@ -610,17 +610,16 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
-github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
-github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
-github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
+github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
+github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
-github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
+github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -656,8 +655,6 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
-github.com/infobloxopen/infoblox-go-client/v2 v2.6.0 h1:nwdGhQ5XRheGybEdUQ4cSl1Vw2UsSQKKi+HEleguQug=
-github.com/infobloxopen/infoblox-go-client/v2 v2.6.0/go.mod h1:Zu7c+X0mTB6ahIYm7p9LlvfcH814ZUEP+eXGPEYLDU4=
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
@@ -752,8 +749,8 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/linki/instrumented_http v0.3.0 h1:dsN92+mXpfZtjJraartcQ99jnuw7fqsnPDjr85ma2dA=
github.com/linki/instrumented_http v0.3.0/go.mod h1:pjYbItoegfuVi2GUOMhEqzvm/SJKuEL3H0tc8QRLRFk=
-github.com/linode/linodego v1.33.1 h1:GcI7ozlHHzZbfthD8edLNInhHjQ452iCwtphza+FJGc=
-github.com/linode/linodego v1.33.1/go.mod h1:rEjoJQACp1gKZn9LfxtCJPwS8ri/+h2B3ScJrgBPPdI=
+github.com/linode/linodego v1.35.0 h1:rIhUeCHBLEDlkoRnOTwzSGzljQ3ksXwLxacmXnrV+Do=
+github.com/linode/linodego v1.35.0/go.mod h1:JxuhOEAMfSxun6RU5/MgTKH2GGTmFrhKRj3wL1NFin0=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/lyft/protoc-gen-star v0.4.10/go.mod h1:mE8fbna26u7aEA2QCVvvfBU/ZrPgocG1206xAFPcs94=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
@@ -903,8 +900,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
-github.com/oracle/oci-go-sdk/v65 v65.65.2 h1:6cYJuFWmDg5PyA1qWiU4TckGPAukv8X1kIbKoNdYsj8=
-github.com/oracle/oci-go-sdk/v65 v65.65.2/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
+github.com/oracle/oci-go-sdk/v65 v65.67.0 h1:bKcbNQyWUDiDgyE4crer3hZmiwpZ3rQnMi03jdKta/w=
+github.com/oracle/oci-go-sdk/v65 v65.67.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/ovh/go-ovh v1.5.1 h1:P8O+7H+NQuFK9P/j4sFW5C0fvSS2DnHYGPwdVCp45wI=
github.com/ovh/go-ovh v1.5.1/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
@@ -992,8 +989,8 @@ github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
-github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
-github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc=
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@@ -1005,8 +1002,8 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26 h1:F+GIVtGqCFxPxO46ujf8cEOP574MBoRm3gNbPXECbxs=
-github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
+github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27 h1:yGAraK1uUjlhSXgNMIy8o/J4LFNcy7yeipBqt9N9mVg=
+github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
github.com/schollz/progressbar/v3 v3.8.6 h1:QruMUdzZ1TbEP++S1m73OqRJk20ON11m6Wqv4EoGg8c=
github.com/schollz/progressbar/v3 v3.8.6/go.mod h1:W5IEwbJecncFGBvuEh4A7HT1nZZ6WNIL2i3qbnI0WKY=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
@@ -1091,12 +1088,12 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.923 h1:H1IQXQ8k1JvtCKBBxC9Q3FR9hflDdjedmlhgbC4i/+o=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.923/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.923 h1:GFWyZoP3QgP+/NvXtpokMZvf3y5WANxY6X5TM2UjRoE=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.923/go.mod h1:/NnC/MecfLrNbxBAfkQHasIUR1vKJnB+CNTS9IYM/Ec=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.923 h1:qO7b/xwBti79q7PKHNvtY/dhl6nVF5/h/V3TwRf76vg=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.923/go.mod h1:WLAClOTvOEXRkIAjoE5sqm6dmKCO9fZvMxhSGNlM6Mg=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.938 h1:i3DFXbdh3UAsr9dJMMY+OSD6Js4mOBFVmEkmLAbHyrM=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.938/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.938 h1:efDgov3mn3ivtiz6Ze68cRpLuKJYwGgPAtSlqK2WOf0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.938/go.mod h1:LI5bGEYMSPSMcG3NYFnDZScSu9cmn0YI2KeNyruivuw=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.938 h1:5gVnVdRFHRQtpfDluel54yWswlwXYiInVy3pFKEoYUo=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.938/go.mod h1:Hc7DtgTg3nYhwvIW06pWVvqFOLa4Pt6b869v6qE/inY=
github.com/terra-farm/udnssdk v1.3.5 h1:MNR3adfuuEK/l04+jzo8WW/0fnorY+nW515qb3vEr6I=
github.com/terra-farm/udnssdk v1.3.5/go.mod h1:8RnM56yZTR7mYyUIvrDgXzdRaEyFIzqdEi7+um26Sv8=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -1153,12 +1150,12 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
-go.etcd.io/etcd/api/v3 v3.5.13 h1:8WXU2/NBge6AUF1K1gOexB6e07NgsN1hXK0rSTtgSp4=
-go.etcd.io/etcd/api/v3 v3.5.13/go.mod h1:gBqlqkcMMZMVTMm4NDZloEVJzxQOQIls8splbqBDa0c=
-go.etcd.io/etcd/client/pkg/v3 v3.5.13 h1:RVZSAnWWWiI5IrYAXjQorajncORbS0zI48LQlE2kQWg=
-go.etcd.io/etcd/client/pkg/v3 v3.5.13/go.mod h1:XxHT4u1qU12E2+po+UVPrEeL94Um6zL58ppuJWXSAB8=
-go.etcd.io/etcd/client/v3 v3.5.13 h1:o0fHTNJLeO0MyVbc7I3fsCf6nrOqn5d+diSarKnB2js=
-go.etcd.io/etcd/client/v3 v3.5.13/go.mod h1:cqiAeY8b5DEEcpxvgWKsbLIWNM/8Wy2xJSDMtioMcoI=
+go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0=
+go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU=
+go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ=
+go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI=
+go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
+go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
@@ -1229,9 +1226,9 @@ golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
-golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1334,9 +1331,9 @@ golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
-golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1347,8 +1344,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
-golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1443,18 +1440,18 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
-golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1467,8 +1464,9 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1548,8 +1546,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
-golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1580,8 +1578,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.181.0 h1:rPdjwnWgiPPOJx3IcSAQ2III5aX5tCer6wMpa/xmZi4=
-google.golang.org/api v0.181.0/go.mod h1:MnQ+M0CFsfUwA5beZ+g/vCBCPXvtmZwRz2qzZk8ih1k=
+google.golang.org/api v0.183.0 h1:PNMeRDwo1pJdgNcFQ9GstuLe/noWKIc89pRWRLMvLwE=
+google.golang.org/api v0.183.0/go.mod h1:q43adC5/pHoSZTx5h2mSmdF7NcyfW9JuDyIOJAgS9ZQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1628,10 +1626,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae h1:AH34z6WAGVNkllnKs5raNq3yRq93VnjBG6rpfub/jYk=
-google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:FfiGhwUm6CJviekPrc0oJ+7h29e+DmWU6UtjX0ZvI7Y=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
+google.golang.org/genproto/googleapis/api v0.0.0-20240521202816-d264139d666e h1:SkdGTrROJl2jRGT/Fxv5QUf9jtdKCQh4KQJXbXVLAi0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240521202816-d264139d666e/go.mod h1:LweJcLbyVij6rCex8YunD8DYR5VDonap/jYl3ZRxcIU=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1654,8 +1652,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
-google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1699,8 +1697,8 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/ns1/ns1-go.v2 v2.10.0 h1:JbwyVcVRhBczmKnVeK0cc5UbHkYr0JlBWCYGWsTWlyU=
-gopkg.in/ns1/ns1-go.v2 v2.10.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
+gopkg.in/ns1/ns1-go.v2 v2.11.0 h1:T+rMHhQsQ58bSgGZwX8INxU0sjDO7cWieX9xPr/UEY4=
+gopkg.in/ns1/ns1-go.v2 v2.11.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
@@ -1732,10 +1730,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-istio.io/api v1.22.0 h1:CdMUHgN/OfQK9ojj6lCjxlJSuUe0vD0ZAvoCcoBfn20=
-istio.io/api v1.22.0/go.mod h1:S3l8LWqNYS9yT+d4bH+jqzH2lMencPkW7SKM1Cu9EyM=
-istio.io/client-go v1.22.0 h1:TQ+Y7hqZVQHvaJXF99Q1jBqnVG7gYAHR9IvCK2nlwfE=
-istio.io/client-go v1.22.0/go.mod h1:1lAPr0DOVBbnRQqLAQKxWbEaxFk6b1CJTm+ypnP7sMo=
+istio.io/api v1.22.1 h1:dzjmBYq6PEWAF7Kn7Dy5mI+RbJd49I41DEZLjYKfdZM=
+istio.io/api v1.22.1/go.mod h1:S3l8LWqNYS9yT+d4bH+jqzH2lMencPkW7SKM1Cu9EyM=
+istio.io/client-go v1.22.1 h1:78BUMxytD0muwpwHdcA9qTOTJXN0jib0mXmNLdXxj0c=
+istio.io/client-go v1.22.1/go.mod h1:Z2QE9uMt6tDVyrmiLfLVhutbqtfUkPJ7A5Uw/p6gNFo=
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8=
k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4=
diff --git a/main.go b/main.go
index 96e79c7400..a84a4df0b0 100644
--- a/main.go
+++ b/main.go
@@ -61,7 +61,6 @@ import (
"sigs.k8s.io/external-dns/provider/godaddy"
"sigs.k8s.io/external-dns/provider/google"
"sigs.k8s.io/external-dns/provider/ibmcloud"
- "sigs.k8s.io/external-dns/provider/infoblox"
"sigs.k8s.io/external-dns/provider/inmemory"
"sigs.k8s.io/external-dns/provider/linode"
"sigs.k8s.io/external-dns/provider/ns1"
@@ -280,26 +279,6 @@ func main() {
p, err = linode.NewLinodeProvider(domainFilter, cfg.DryRun, externaldns.Version)
case "dnsimple":
p, err = dnsimple.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
- case "infoblox":
- p, err = infoblox.NewInfobloxProvider(
- infoblox.StartupConfig{
- DomainFilter: domainFilter,
- ZoneIDFilter: zoneIDFilter,
- Host: cfg.InfobloxGridHost,
- Port: cfg.InfobloxWapiPort,
- Username: cfg.InfobloxWapiUsername,
- Password: cfg.InfobloxWapiPassword,
- Version: cfg.InfobloxWapiVersion,
- SSLVerify: cfg.InfobloxSSLVerify,
- View: cfg.InfobloxView,
- MaxResults: cfg.InfobloxMaxResults,
- DryRun: cfg.DryRun,
- FQDNRegEx: cfg.InfobloxFQDNRegEx,
- NameRegEx: cfg.InfobloxNameRegEx,
- CreatePTR: cfg.InfobloxCreatePTR,
- CacheDuration: cfg.InfobloxCacheDuration,
- },
- )
case "dyn":
p, err = dyn.NewDynProvider(
dyn.DynConfig{
diff --git a/mkdocs.yml b/mkdocs.yml
index 8698d28b2d..aa2bb9abbb 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -4,6 +4,8 @@ site_author: external-dns maintainers
repo_name: kubernetes-sigs/external-dns
repo_url: https://github.com/kubernetes-sigs/external-dns/
+trademark: https://www.linuxfoundation.org/legal/trademark-usage
+
nav:
- index.md
- About:
@@ -33,6 +35,7 @@ nav:
theme:
name: material
+ custom_dir: docs/overrides
features:
- content.code.annotate
- navigation.top
diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go
index 26b579f829..e88dabf659 100644
--- a/pkg/apis/externaldns/types.go
+++ b/pkg/apis/externaldns/types.go
@@ -120,18 +120,6 @@ type Config struct {
AkamaiAccessToken string
AkamaiEdgercPath string
AkamaiEdgercSection string
- InfobloxGridHost string
- InfobloxWapiPort int
- InfobloxWapiUsername string
- InfobloxWapiPassword string `secure:"yes"`
- InfobloxWapiVersion string
- InfobloxSSLVerify bool
- InfobloxView string
- InfobloxMaxResults int
- InfobloxFQDNRegEx string
- InfobloxNameRegEx string
- InfobloxCreatePTR bool
- InfobloxCacheDuration int
DynCustomerName string
DynUsername string
DynPassword string `secure:"yes"`
@@ -292,17 +280,6 @@ var defaultConfig = &Config{
AkamaiAccessToken: "",
AkamaiEdgercSection: "",
AkamaiEdgercPath: "",
- InfobloxGridHost: "",
- InfobloxWapiPort: 443,
- InfobloxWapiUsername: "admin",
- InfobloxWapiPassword: "",
- InfobloxWapiVersion: "2.3.1",
- InfobloxSSLVerify: true,
- InfobloxView: "",
- InfobloxMaxResults: 0,
- InfobloxFQDNRegEx: "",
- InfobloxCreatePTR: false,
- InfobloxCacheDuration: 0,
OCIConfigFile: "/etc/kubernetes/oci.yaml",
OCIZoneScope: "GLOBAL",
OCIZoneCacheDuration: 0 * time.Second,
@@ -475,7 +452,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("traefik-disable-new", "Disable listeners on Resources under the traefik.io API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableNew)).BoolVar(&cfg.TraefikDisableNew)
// Flags related to providers
- providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "bluecat", "civo", "cloudflare", "coredns", "designate", "digitalocean", "dnsimple", "dyn", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "infoblox", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rcodezero", "rdns", "rfc2136", "safedns", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "vinyldns", "vultr", "webhook"}
+ providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "bluecat", "civo", "cloudflare", "coredns", "designate", "digitalocean", "dnsimple", "dyn", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rcodezero", "rdns", "rfc2136", "safedns", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "vinyldns", "vultr", "webhook"}
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: "+strings.Join(providers, ", ")+")").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, providers...)
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)
@@ -529,18 +506,6 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("akamai-access-token", "When using the Akamai provider, specify the access token (required when --provider=akamai and edgerc-path not specified)").Default(defaultConfig.AkamaiAccessToken).StringVar(&cfg.AkamaiAccessToken)
app.Flag("akamai-edgerc-path", "When using the Akamai provider, specify the .edgerc file path. Path must be reachable form invocation environment. (required when --provider=akamai and *-token, secret serviceconsumerdomain not specified)").Default(defaultConfig.AkamaiEdgercPath).StringVar(&cfg.AkamaiEdgercPath)
app.Flag("akamai-edgerc-section", "When using the Akamai provider, specify the .edgerc file path (Optional when edgerc-path is specified)").Default(defaultConfig.AkamaiEdgercSection).StringVar(&cfg.AkamaiEdgercSection)
- app.Flag("infoblox-grid-host", "When using the Infoblox provider, specify the Grid Manager host (required when --provider=infoblox)").Default(defaultConfig.InfobloxGridHost).StringVar(&cfg.InfobloxGridHost)
- app.Flag("infoblox-wapi-port", "When using the Infoblox provider, specify the WAPI port (default: 443)").Default(strconv.Itoa(defaultConfig.InfobloxWapiPort)).IntVar(&cfg.InfobloxWapiPort)
- app.Flag("infoblox-wapi-username", "When using the Infoblox provider, specify the WAPI username (default: admin)").Default(defaultConfig.InfobloxWapiUsername).StringVar(&cfg.InfobloxWapiUsername)
- app.Flag("infoblox-wapi-password", "When using the Infoblox provider, specify the WAPI password (required when --provider=infoblox)").Default(defaultConfig.InfobloxWapiPassword).StringVar(&cfg.InfobloxWapiPassword)
- app.Flag("infoblox-wapi-version", "When using the Infoblox provider, specify the WAPI version (default: 2.3.1)").Default(defaultConfig.InfobloxWapiVersion).StringVar(&cfg.InfobloxWapiVersion)
- app.Flag("infoblox-ssl-verify", "When using the Infoblox provider, specify whether to verify the SSL certificate (default: true, disable with --no-infoblox-ssl-verify)").Default(strconv.FormatBool(defaultConfig.InfobloxSSLVerify)).BoolVar(&cfg.InfobloxSSLVerify)
- app.Flag("infoblox-view", "DNS view (default: \"\")").Default(defaultConfig.InfobloxView).StringVar(&cfg.InfobloxView)
- app.Flag("infoblox-max-results", "Add _max_results as query parameter to the URL on all API requests. The default is 0 which means _max_results is not set and the default of the server is used.").Default(strconv.Itoa(defaultConfig.InfobloxMaxResults)).IntVar(&cfg.InfobloxMaxResults)
- app.Flag("infoblox-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-name-regex", "Apply this regular expression as a filter on the name field for obtaining infoblox records. This is disabled by default.").Default(defaultConfig.InfobloxNameRegEx).StringVar(&cfg.InfobloxNameRegEx)
- 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-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)
@@ -639,11 +604,11 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("log-level", "Set the level of logging. (default: info, options: panic, debug, info, warning, error, fatal)").Default(defaultConfig.LogLevel).EnumVar(&cfg.LogLevel, allLogLevelsAsStrings()...)
// Webhook provider
- app.Flag("webhook-provider-url", "[EXPERIMENTAL] The URL of the remote endpoint to call for the webhook provider (default: http://localhost:8888)").Default(defaultConfig.WebhookProviderURL).StringVar(&cfg.WebhookProviderURL)
- app.Flag("webhook-provider-read-timeout", "[EXPERIMENTAL] The read timeout for the webhook provider in duration format (default: 5s)").Default(defaultConfig.WebhookProviderReadTimeout.String()).DurationVar(&cfg.WebhookProviderReadTimeout)
- app.Flag("webhook-provider-write-timeout", "[EXPERIMENTAL] The write timeout for the webhook provider in duration format (default: 10s)").Default(defaultConfig.WebhookProviderWriteTimeout.String()).DurationVar(&cfg.WebhookProviderWriteTimeout)
+ app.Flag("webhook-provider-url", "The URL of the remote endpoint to call for the webhook provider (default: http://localhost:8888)").Default(defaultConfig.WebhookProviderURL).StringVar(&cfg.WebhookProviderURL)
+ app.Flag("webhook-provider-read-timeout", "The read timeout for the webhook provider in duration format (default: 5s)").Default(defaultConfig.WebhookProviderReadTimeout.String()).DurationVar(&cfg.WebhookProviderReadTimeout)
+ app.Flag("webhook-provider-write-timeout", "The write timeout for the webhook provider in duration format (default: 10s)").Default(defaultConfig.WebhookProviderWriteTimeout.String()).DurationVar(&cfg.WebhookProviderWriteTimeout)
- app.Flag("webhook-server", "[EXPERIMENTAL] When enabled, runs as a webhook server instead of a controller. (default: false).").BoolVar(&cfg.WebhookServer)
+ app.Flag("webhook-server", "When enabled, runs as a webhook server instead of a controller. (default: false).").BoolVar(&cfg.WebhookServer)
_, err := app.Parse(args)
if err != nil {
diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go
index 987fd5ef37..68f22fa936 100644
--- a/pkg/apis/externaldns/types_test.go
+++ b/pkg/apis/externaldns/types_test.go
@@ -88,14 +88,6 @@ var (
AkamaiAccessToken: "",
AkamaiEdgercPath: "",
AkamaiEdgercSection: "",
- InfobloxGridHost: "",
- InfobloxWapiPort: 443,
- InfobloxWapiUsername: "admin",
- InfobloxWapiPassword: "",
- InfobloxWapiVersion: "2.3.1",
- InfobloxView: "",
- InfobloxSSLVerify: true,
- InfobloxMaxResults: 0,
OCIConfigFile: "/etc/kubernetes/oci.yaml",
OCIZoneScope: "GLOBAL",
OCIZoneCacheDuration: 0 * time.Second,
@@ -202,14 +194,6 @@ var (
AkamaiAccessToken: "o184671d5307a388180fbf7f11dbdf46",
AkamaiEdgercPath: "/home/test/.edgerc",
AkamaiEdgercSection: "default",
- InfobloxGridHost: "127.0.0.1",
- InfobloxWapiPort: 8443,
- InfobloxWapiUsername: "infoblox",
- InfobloxWapiPassword: "infoblox",
- InfobloxWapiVersion: "2.6.1",
- InfobloxView: "internal",
- InfobloxSSLVerify: false,
- InfobloxMaxResults: 2000,
OCIConfigFile: "oci.yaml",
OCIZoneScope: "PRIVATE",
OCIZoneCacheDuration: 30 * time.Second,
@@ -320,13 +304,6 @@ func TestParseFlags(t *testing.T) {
"--akamai-access-token=o184671d5307a388180fbf7f11dbdf46",
"--akamai-edgerc-path=/home/test/.edgerc",
"--akamai-edgerc-section=default",
- "--infoblox-grid-host=127.0.0.1",
- "--infoblox-wapi-port=8443",
- "--infoblox-wapi-username=infoblox",
- "--infoblox-wapi-password=infoblox",
- "--infoblox-wapi-version=2.6.1",
- "--infoblox-view=internal",
- "--infoblox-max-results=2000",
"--inmemory-zone=example.org",
"--inmemory-zone=company.com",
"--ovh-endpoint=ovh-ca",
@@ -340,7 +317,6 @@ func TestParseFlags(t *testing.T) {
"--tls-ca=/path/to/ca.crt",
"--tls-client-cert=/path/to/cert.pem",
"--tls-client-cert-key=/path/to/key.pem",
- "--no-infoblox-ssl-verify",
"--domain-filter=example.org",
"--domain-filter=company.com",
"--exclude-domains=xapi.example.org",
@@ -451,14 +427,6 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_AKAMAI_ACCESS_TOKEN": "o184671d5307a388180fbf7f11dbdf46",
"EXTERNAL_DNS_AKAMAI_EDGERC_PATH": "/home/test/.edgerc",
"EXTERNAL_DNS_AKAMAI_EDGERC_SECTION": "default",
- "EXTERNAL_DNS_INFOBLOX_GRID_HOST": "127.0.0.1",
- "EXTERNAL_DNS_INFOBLOX_WAPI_PORT": "8443",
- "EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox",
- "EXTERNAL_DNS_INFOBLOX_WAPI_PASSWORD": "infoblox",
- "EXTERNAL_DNS_INFOBLOX_WAPI_VERSION": "2.6.1",
- "EXTERNAL_DNS_INFOBLOX_VIEW": "internal",
- "EXTERNAL_DNS_INFOBLOX_SSL_VERIFY": "0",
- "EXTERNAL_DNS_INFOBLOX_MAX_RESULTS": "2000",
"EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml",
"EXTERNAL_DNS_OCI_ZONE_SCOPE": "PRIVATE",
"EXTERNAL_DNS_OCI_ZONES_CACHE_DURATION": "30s",
@@ -564,7 +532,6 @@ func restoreEnv(t *testing.T, originalEnv map[string]string) {
func TestPasswordsNotLogged(t *testing.T) {
cfg := Config{
DynPassword: "dyn-pass",
- InfobloxWapiPassword: "infoblox-pass",
PDNSAPIKey: "pdns-api-key",
RFC2136TSIGSecret: "tsig-secret",
}
@@ -572,7 +539,6 @@ func TestPasswordsNotLogged(t *testing.T) {
s := cfg.String()
assert.False(t, strings.Contains(s, "dyn-pass"))
- assert.False(t, strings.Contains(s, "infoblox-pass"))
assert.False(t, strings.Contains(s, "pdns-api-key"))
assert.False(t, strings.Contains(s, "tsig-secret"))
}
diff --git a/pkg/apis/externaldns/validation/validation.go b/pkg/apis/externaldns/validation/validation.go
index c5ebc38451..6af44ab125 100644
--- a/pkg/apis/externaldns/validation/validation.go
+++ b/pkg/apis/externaldns/validation/validation.go
@@ -61,16 +61,6 @@ func ValidateConfig(cfg *externaldns.Config) error {
}
}
- // Infoblox provider specific validations
- if cfg.Provider == "infoblox" {
- if cfg.InfobloxGridHost == "" {
- return errors.New("no Infoblox Grid Manager host specified")
- }
- if cfg.InfobloxWapiPassword == "" {
- return errors.New("no Infoblox WAPI password specified")
- }
- }
-
if cfg.Provider == "dyn" {
if cfg.DynUsername == "" {
return errors.New("no Dyn username specified")
diff --git a/provider/coredns/coredns.go b/provider/coredns/coredns.go
index a514ca8659..eeb545aea5 100644
--- a/provider/coredns/coredns.go
+++ b/provider/coredns/coredns.go
@@ -205,8 +205,10 @@ func getETCDConfig() (*etcdcv3.Config, error) {
}
etcdURLs := strings.Split(etcdURLsStr, ",")
firstURL := strings.ToLower(etcdURLs[0])
+ etcdUsername := os.Getenv("ETCD_USERNAME")
+ etcdPassword := os.Getenv("ETCD_PASSWORD")
if strings.HasPrefix(firstURL, "http://") {
- return &etcdcv3.Config{Endpoints: etcdURLs}, nil
+ return &etcdcv3.Config{Endpoints: etcdURLs, Username: etcdUsername, Password: etcdPassword}, nil
} else if strings.HasPrefix(firstURL, "https://") {
caFile := os.Getenv("ETCD_CA_FILE")
certFile := os.Getenv("ETCD_CERT_FILE")
@@ -221,6 +223,8 @@ func getETCDConfig() (*etcdcv3.Config, error) {
return &etcdcv3.Config{
Endpoints: etcdURLs,
TLS: tlsConfig,
+ Username: etcdUsername,
+ Password: etcdPassword,
}, nil
} else {
return nil, errors.New("etcd URLs must start with either http:// or https://")
diff --git a/provider/coredns/coredns_test.go b/provider/coredns/coredns_test.go
index 8c9aa8f9d6..baf5652f8e 100644
--- a/provider/coredns/coredns_test.go
+++ b/provider/coredns/coredns_test.go
@@ -18,9 +18,12 @@ package coredns
import (
"context"
+ "os"
+ "reflect"
"strings"
"testing"
+ etcdcv3 "go.etcd.io/etcd/client/v3"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
@@ -55,6 +58,68 @@ func (c fakeETCDClient) DeleteService(key string) error {
return nil
}
+func TestETCDConfig(t *testing.T) {
+ var tests = []struct {
+ name string
+ input map[string]string
+ want *etcdcv3.Config
+ }{
+ {
+ "default config",
+ map[string]string{},
+ &etcdcv3.Config{Endpoints: []string{"http://localhost:2379"}},
+ },
+ {
+ "config with ETCD_URLS",
+ map[string]string{"ETCD_URLS": "http://example.com:2379"},
+ &etcdcv3.Config{Endpoints: []string{"http://example.com:2379"}},
+ },
+ {
+ "config with ETCD_USERNAME and ETCD_PASSWORD",
+ map[string]string{"ETCD_USERNAME": "root", "ETCD_PASSWORD": "test"},
+ &etcdcv3.Config{
+ Endpoints: []string{"http://localhost:2379"},
+ Username: "root",
+ Password: "test",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ closer := envSetter(tt.input)
+ cfg, _ := getETCDConfig()
+ if !reflect.DeepEqual(cfg, tt.want) {
+ t.Errorf("unexpected config. Got %v, want %v", cfg, tt.want)
+ }
+ t.Cleanup(closer)
+ })
+ }
+
+}
+
+func envSetter(envs map[string]string) (closer func()) {
+ originalEnvs := map[string]string{}
+
+ for name, value := range envs {
+ if originalValue, ok := os.LookupEnv(name); ok {
+ originalEnvs[name] = originalValue
+ }
+ _ = os.Setenv(name, value)
+ }
+
+ return func() {
+ for name := range envs {
+ origValue, has := originalEnvs[name]
+ if has {
+ _ = os.Setenv(name, origValue)
+ } else {
+ _ = os.Unsetenv(name)
+ }
+ }
+ }
+}
+
func TestAServiceTranslation(t *testing.T) {
expectedTarget := "1.2.3.4"
expectedDNSName := "example.com"
diff --git a/provider/infoblox/infoblox.go b/provider/infoblox/infoblox.go
deleted file mode 100644
index 841cca9ae6..0000000000
--- a/provider/infoblox/infoblox.go
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
-Copyright 2017 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 infoblox
-
-import (
- "context"
- "fmt"
- "net"
- "net/http"
- "os"
- "sort"
- "strconv"
- "strings"
-
- ibclient "github.com/infobloxopen/infoblox-go-client/v2"
- "github.com/sirupsen/logrus"
-
- "sigs.k8s.io/external-dns/endpoint"
- "sigs.k8s.io/external-dns/pkg/rfc2317"
- "sigs.k8s.io/external-dns/plan"
- "sigs.k8s.io/external-dns/provider"
-)
-
-const (
- // provider specific key to track if PTR record was already created or not for A records
- providerSpecificInfobloxPtrRecord = "infoblox-ptr-record-exists"
-)
-
-func isNotFoundError(err error) bool {
- _, ok := err.(*ibclient.NotFoundError)
- return ok
-}
-
-// StartupConfig clarifies the method signature
-type StartupConfig struct {
- DomainFilter endpoint.DomainFilter
- ZoneIDFilter provider.ZoneIDFilter
- Host string
- Port int
- Username string
- Password string
- Version string
- SSLVerify bool
- DryRun bool
- View string
- MaxResults int
- FQDNRegEx string
- NameRegEx string
- CreatePTR bool
- CacheDuration int
-}
-
-// ProviderConfig implements the DNS provider for Infoblox.
-type ProviderConfig struct {
- provider.BaseProvider
- client ibclient.IBConnector
- domainFilter endpoint.DomainFilter
- zoneIDFilter provider.ZoneIDFilter
- view string
- dryRun bool
- fqdnRegEx string
- createPTR bool
- cacheDuration int
-}
-
-type infobloxRecordSet struct {
- obj ibclient.IBObject
- res interface{}
-}
-
-// ExtendedRequestBuilder implements a HttpRequestBuilder which sets
-// additional query parameter on all get requests
-type ExtendedRequestBuilder struct {
- fqdnRegEx string
- nameRegEx string
- maxResults int
- ibclient.WapiRequestBuilder
-}
-
-// NewExtendedRequestBuilder returns a ExtendedRequestBuilder which adds
-// _max_results query parameter to all GET requests
-func NewExtendedRequestBuilder(maxResults int, fqdnRegEx string, nameRegEx string) *ExtendedRequestBuilder {
- return &ExtendedRequestBuilder{
- fqdnRegEx: fqdnRegEx,
- nameRegEx: nameRegEx,
- maxResults: maxResults,
- }
-}
-
-// BuildRequest prepares the api request. it uses BuildRequest of
-// WapiRequestBuilder and then add the _max_requests parameter
-func (mrb *ExtendedRequestBuilder) BuildRequest(t ibclient.RequestType, obj ibclient.IBObject, ref string, queryParams *ibclient.QueryParams) (req *http.Request, err error) {
- req, err = mrb.WapiRequestBuilder.BuildRequest(t, obj, ref, queryParams)
- if req.Method == "GET" {
- query := req.URL.Query()
- if mrb.maxResults > 0 {
- query.Set("_max_results", strconv.Itoa(mrb.maxResults))
- }
- _, zoneAuthQuery := obj.(*ibclient.ZoneAuth)
- if zoneAuthQuery && t == ibclient.GET && mrb.fqdnRegEx != "" {
- query.Set("fqdn~", mrb.fqdnRegEx)
- }
-
- // if we are not doing a ZoneAuth query, support the name filter
- if !zoneAuthQuery && mrb.nameRegEx != "" {
- query.Set("name~", mrb.nameRegEx)
- }
-
- req.URL.RawQuery = query.Encode()
- }
- return
-}
-
-// NewInfobloxProvider creates a new Infoblox provider.
-func NewInfobloxProvider(ibStartupCfg StartupConfig) (*ProviderConfig, error) {
- hostCfg := ibclient.HostConfig{
- Host: ibStartupCfg.Host,
- Port: strconv.Itoa(ibStartupCfg.Port),
- Version: ibStartupCfg.Version,
- }
-
- authCfg := ibclient.AuthConfig{
- Username: ibStartupCfg.Username,
- Password: ibStartupCfg.Password,
- }
-
- httpPoolConnections := lookupEnvAtoi("EXTERNAL_DNS_INFOBLOX_HTTP_POOL_CONNECTIONS", 10)
- httpRequestTimeout := lookupEnvAtoi("EXTERNAL_DNS_INFOBLOX_HTTP_REQUEST_TIMEOUT", 60)
-
- transportConfig := ibclient.NewTransportConfig(
- strconv.FormatBool(ibStartupCfg.SSLVerify),
- httpRequestTimeout,
- httpPoolConnections,
- )
-
- var (
- requestBuilder ibclient.HttpRequestBuilder
- err error
- )
- if ibStartupCfg.MaxResults != 0 || ibStartupCfg.FQDNRegEx != "" || ibStartupCfg.NameRegEx != "" {
- // use our own HttpRequestBuilder which sets _max_results parameter on GET requests
- requestBuilder = NewExtendedRequestBuilder(ibStartupCfg.MaxResults, ibStartupCfg.FQDNRegEx, ibStartupCfg.NameRegEx)
- } else {
- // use the default HttpRequestBuilder of the infoblox client
- requestBuilder, err = ibclient.NewWapiRequestBuilder(hostCfg, authCfg)
- if err != nil {
- return nil, err
- }
- }
-
- requestor := &ibclient.WapiHttpRequestor{}
-
- client, err := ibclient.NewConnector(hostCfg, authCfg, transportConfig, requestBuilder, requestor)
- if err != nil {
- return nil, err
- }
-
- providerCfg := &ProviderConfig{
- client: client,
- domainFilter: ibStartupCfg.DomainFilter,
- zoneIDFilter: ibStartupCfg.ZoneIDFilter,
- dryRun: ibStartupCfg.DryRun,
- view: ibStartupCfg.View,
- fqdnRegEx: ibStartupCfg.FQDNRegEx,
- createPTR: ibStartupCfg.CreatePTR,
- cacheDuration: ibStartupCfg.CacheDuration,
- }
-
- return providerCfg, nil
-}
-
-func recordQueryParams(zone string, view string) *ibclient.QueryParams {
- searchFields := map[string]string{}
- if zone != "" {
- searchFields["zone"] = zone
- }
-
- if view != "" {
- searchFields["view"] = view
- }
- return ibclient.NewQueryParams(false, searchFields)
-}
-
-// Records gets the current records.
-func (p *ProviderConfig) Records(ctx context.Context) (endpoints []*endpoint.Endpoint, err error) {
- zones, err := p.zones()
- if err != nil {
- return nil, fmt.Errorf("could not fetch zones: %w", err)
- }
-
- for _, zone := range zones {
- logrus.Debugf("fetch records from zone '%s'", zone.Fqdn)
- searchParams := recordQueryParams(zone.Fqdn, p.view)
- var resA []ibclient.RecordA
- objA := ibclient.NewEmptyRecordA()
- err = p.client.GetObject(objA, "", searchParams, &resA)
- if err != nil && !isNotFoundError(err) {
- return nil, fmt.Errorf("could not fetch A records from zone '%s': %w", zone.Fqdn, err)
- }
- for _, res := range resA {
- // Check if endpoint already exists and add to existing endpoint if it does
- foundExisting := false
- for _, ep := range endpoints {
- if ep.DNSName == *res.Name && ep.RecordType == endpoint.RecordTypeA {
- foundExisting = true
- duplicateTarget := false
-
- for _, t := range ep.Targets {
- if t == *res.Ipv4Addr {
- duplicateTarget = true
- break
- }
- }
-
- if duplicateTarget {
- logrus.Debugf("A duplicate target '%s' found for existing A record '%s'", *res.Ipv4Addr, ep.DNSName)
- } else {
- logrus.Debugf("Adding target '%s' to existing A record '%s'", *res.Ipv4Addr, *res.Name)
- ep.Targets = append(ep.Targets, *res.Ipv4Addr)
- }
- break
- }
- }
- if !foundExisting {
- newEndpoint := endpoint.NewEndpoint(*res.Name, endpoint.RecordTypeA, *res.Ipv4Addr)
- if p.createPTR {
- newEndpoint.WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true")
- }
- endpoints = append(endpoints, newEndpoint)
- }
- }
- // sort targets so that they are always in same order, as infoblox might return them in different order
- for _, ep := range endpoints {
- sort.Sort(ep.Targets)
- }
-
- // Include Host records since they should be treated synonymously with A records
- var resH []ibclient.HostRecord
- objH := ibclient.NewEmptyHostRecord()
- err = p.client.GetObject(objH, "", searchParams, &resH)
- if err != nil && !isNotFoundError(err) {
- return nil, fmt.Errorf("could not fetch host records from zone '%s': %w", zone.Fqdn, err)
- }
- for _, res := range resH {
- 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
- // for any host record we already should have a PTR record in infoblox, so mark it as created
- newEndpoint := endpoint.NewEndpoint(*res.Name, endpoint.RecordTypeA, *ip.Ipv4Addr)
- if p.createPTR {
- newEndpoint.WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true")
- }
- endpoints = append(endpoints, newEndpoint)
- }
- }
-
- var resC []ibclient.RecordCNAME
- objC := ibclient.NewEmptyRecordCNAME()
- err = p.client.GetObject(objC, "", searchParams, &resC)
- if err != nil && !isNotFoundError(err) {
- return nil, fmt.Errorf("could not fetch CNAME records from zone '%s': %w", zone.Fqdn, err)
- }
- 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))
- }
-
- if p.createPTR {
- // infoblox doesn't accept reverse zone's fqdn, and instead expects .in-addr.arpa zone
- // so convert our zone fqdn (if it is a correct cidr block) into in-addr.arpa address and pass that into infoblox
- // example: 10.196.38.0/24 becomes 38.196.10.in-addr.arpa
- arpaZone, err := rfc2317.CidrToInAddr(zone.Fqdn)
- if err == nil {
- var resP []ibclient.RecordPTR
- objP := ibclient.NewEmptyRecordPTR()
- err = p.client.GetObject(objP, "", recordQueryParams(arpaZone, p.view), &resP)
- if err != nil && !isNotFoundError(err) {
- return nil, fmt.Errorf("could not fetch PTR records from zone '%s': %w", zone.Fqdn, err)
- }
- for _, res := range resP {
- endpoints = append(endpoints, endpoint.NewEndpoint(*res.PtrdName, endpoint.RecordTypePTR, *res.Ipv4Addr))
- }
- }
- }
-
- var resT []ibclient.RecordTXT
- objT := ibclient.NewEmptyRecordTXT()
- err = p.client.GetObject(objT, "", searchParams, &resT)
- if err != nil && !isNotFoundError(err) {
- return nil, fmt.Errorf("could not fetch TXT records from zone '%s': %w", zone.Fqdn, err)
- }
- for _, res := range resT {
- // The Infoblox API strips enclosing double quotes from TXT records lacking whitespace.
- // Unhandled, the missing double quotes would break the extractOwnerID method of the registry package.
- if _, err := strconv.Unquote(*res.Text); err != nil {
- quoted := strconv.Quote(*res.Text)
- res.Text = "ed
- }
-
- foundExisting := false
-
- for _, ep := range endpoints {
- if ep.DNSName == *res.Name && ep.RecordType == endpoint.RecordTypeTXT {
- foundExisting = true
- duplicateTarget := false
-
- for _, t := range ep.Targets {
- if t == *res.Text {
- duplicateTarget = true
- break
- }
- }
-
- if duplicateTarget {
- logrus.Debugf("A duplicate target '%s' found for existing TXT record '%s'", *res.Text, ep.DNSName)
- } else {
- logrus.Debugf("Adding target '%s' to existing TXT record '%s'", *res.Text, *res.Name)
- ep.Targets = append(ep.Targets, *res.Text)
- }
- break
- }
- }
- if !foundExisting {
- logrus.Debugf("Record='%s' TXT:'%s'", *res.Name, *res.Text)
- newEndpoint := endpoint.NewEndpoint(*res.Name, endpoint.RecordTypeTXT, *res.Text)
- endpoints = append(endpoints, newEndpoint)
- }
- }
- }
-
- // update A records that have PTR record created for them already
- if p.createPTR {
- // save all ptr records into map for a quick look up
- ptrRecordsMap := make(map[string]bool)
- for _, ptrRecord := range endpoints {
- if ptrRecord.RecordType != endpoint.RecordTypePTR {
- continue
- }
- ptrRecordsMap[ptrRecord.DNSName] = true
- }
-
- for i := range endpoints {
- if endpoints[i].RecordType != endpoint.RecordTypeA {
- continue
- }
- // if PTR record already exists for A record, then mark it as such
- if ptrRecordsMap[endpoints[i].DNSName] {
- found := false
- for j := range endpoints[i].ProviderSpecific {
- if endpoints[i].ProviderSpecific[j].Name == providerSpecificInfobloxPtrRecord {
- endpoints[i].ProviderSpecific[j].Value = "true"
- found = true
- }
- }
- if !found {
- endpoints[i].WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true")
- }
- }
- }
- }
- logrus.Debugf("fetched %d records from infoblox", len(endpoints))
- return endpoints, nil
-}
-
-func (p *ProviderConfig) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
- // Update user specified TTL (0 == disabled)
- for i := range endpoints {
- endpoints[i].RecordTTL = endpoint.TTL(p.cacheDuration)
- }
-
- if !p.createPTR {
- return endpoints, nil
- }
-
- // for all A records, we want to create PTR records
- // so add provider specific property to track if the record was created or not
- for i := range endpoints {
- if endpoints[i].RecordType == endpoint.RecordTypeA {
- found := false
- for j := range endpoints[i].ProviderSpecific {
- if endpoints[i].ProviderSpecific[j].Name == providerSpecificInfobloxPtrRecord {
- endpoints[i].ProviderSpecific[j].Value = "true"
- found = true
- }
- }
- if !found {
- endpoints[i].WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true")
- }
- }
- }
-
- return endpoints, nil
-}
-
-// ApplyChanges applies the given changes.
-func (p *ProviderConfig) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
- zones, err := p.zones()
- if err != nil {
- return err
- }
-
- created, deleted := p.mapChanges(zones, changes)
- p.deleteRecords(deleted)
- p.createRecords(created)
- return nil
-}
-
-func (p *ProviderConfig) zones() ([]ibclient.ZoneAuth, error) {
- var res, result []ibclient.ZoneAuth
- obj := ibclient.NewZoneAuth(ibclient.ZoneAuth{})
- queryParams := recordQueryParams("", p.view)
- err := p.client.GetObject(obj, "", queryParams, &res)
- if err != nil && !isNotFoundError(err) {
- return nil, err
- }
-
- for _, zone := range res {
- if !p.domainFilter.Match(zone.Fqdn) {
- continue
- }
-
- if !p.zoneIDFilter.Match(zone.Ref) {
- continue
- }
-
- result = append(result, zone)
- }
-
- return result, nil
-}
-
-type infobloxChangeMap map[string][]*endpoint.Endpoint
-
-func (p *ProviderConfig) mapChanges(zones []ibclient.ZoneAuth, changes *plan.Changes) (infobloxChangeMap, infobloxChangeMap) {
- created := infobloxChangeMap{}
- deleted := infobloxChangeMap{}
-
- mapChange := func(changeMap infobloxChangeMap, change *endpoint.Endpoint) {
- zone := p.findZone(zones, change.DNSName)
- if zone == nil {
- logrus.Debugf("Ignoring changes to '%s' because a suitable Infoblox DNS zone was not found.", change.DNSName)
- return
- }
- // Ensure the record type is suitable
- changeMap[zone.Fqdn] = append(changeMap[zone.Fqdn], change)
-
- if p.createPTR && change.RecordType == endpoint.RecordTypeA {
- reverseZone := p.findReverseZone(zones, change.Targets[0])
- if reverseZone == nil {
- logrus.Debugf("Ignoring changes to '%s' because a suitable Infoblox DNS reverse zone was not found.", change.Targets[0])
- return
- }
- changecopy := *change
- changecopy.RecordType = endpoint.RecordTypePTR
- changeMap[reverseZone.Fqdn] = append(changeMap[reverseZone.Fqdn], &changecopy)
- }
- }
-
- for _, change := range changes.Delete {
- mapChange(deleted, change)
- }
- for _, change := range changes.UpdateOld {
- mapChange(deleted, change)
- }
- for _, change := range changes.Create {
- mapChange(created, change)
- }
- for _, change := range changes.UpdateNew {
- mapChange(created, change)
- }
-
- return created, deleted
-}
-
-func (p *ProviderConfig) findZone(zones []ibclient.ZoneAuth, name string) *ibclient.ZoneAuth {
- var result *ibclient.ZoneAuth
-
- // Go through every zone looking for the longest name (i.e. most specific) as a matching suffix
- for idx := range zones {
- zone := &zones[idx]
- if strings.HasSuffix(name, "."+zone.Fqdn) {
- if result == nil || len(zone.Fqdn) > len(result.Fqdn) {
- result = zone
- }
- } else if strings.EqualFold(name, zone.Fqdn) {
- if result == nil || len(zone.Fqdn) > len(result.Fqdn) {
- result = zone
- }
- }
- }
- return result
-}
-
-func (p *ProviderConfig) findReverseZone(zones []ibclient.ZoneAuth, name string) *ibclient.ZoneAuth {
- ip := net.ParseIP(name)
- networks := map[int]*ibclient.ZoneAuth{}
- maxMask := 0
-
- for i, zone := range zones {
- _, rZoneNet, err := net.ParseCIDR(zone.Fqdn)
- if err != nil {
- logrus.WithError(err).Debugf("fqdn %s is no cidr", zone.Fqdn)
- } else {
- if rZoneNet.Contains(ip) {
- _, mask := rZoneNet.Mask.Size()
- networks[mask] = &zones[i]
- if mask > maxMask {
- maxMask = mask
- }
- }
- }
- }
- return networks[maxMask]
-}
-
-func (p *ProviderConfig) recordSet(ep *endpoint.Endpoint, getObject bool, targetIndex int) (recordSet infobloxRecordSet, err error) {
- switch ep.RecordType {
- case endpoint.RecordTypeA:
- var res []ibclient.RecordA
- obj := ibclient.NewEmptyRecordA()
- obj.Name = &ep.DNSName
- obj.Ipv4Addr = &ep.Targets[targetIndex]
- obj.View = p.view
- if getObject {
- queryParams := ibclient.NewQueryParams(false, map[string]string{"name": *obj.Name})
- err = p.client.GetObject(obj, "", queryParams, &res)
- if err != nil && !isNotFoundError(err) {
- return
- }
- }
- recordSet = infobloxRecordSet{
- obj: obj,
- res: &res,
- }
- case endpoint.RecordTypePTR:
- var res []ibclient.RecordPTR
- obj := ibclient.NewEmptyRecordPTR()
- obj.PtrdName = &ep.DNSName
- obj.Ipv4Addr = &ep.Targets[targetIndex]
- obj.View = p.view
- if getObject {
- queryParams := ibclient.NewQueryParams(false, map[string]string{"name": *obj.PtrdName})
- err = p.client.GetObject(obj, "", queryParams, &res)
- if err != nil && !isNotFoundError(err) {
- return
- }
- }
- recordSet = infobloxRecordSet{
- obj: obj,
- res: &res,
- }
- case endpoint.RecordTypeCNAME:
- var res []ibclient.RecordCNAME
- obj := ibclient.NewEmptyRecordCNAME()
- obj.Name = &ep.DNSName
- obj.Canonical = &ep.Targets[0]
- obj.View = &p.view
- if getObject {
- queryParams := ibclient.NewQueryParams(false, map[string]string{"name": *obj.Name})
- err = p.client.GetObject(obj, "", queryParams, &res)
- if err != nil && !isNotFoundError(err) {
- return
- }
- }
- recordSet = infobloxRecordSet{
- obj: obj,
- res: &res,
- }
- case endpoint.RecordTypeTXT:
- var res []ibclient.RecordTXT
- // The Infoblox API strips enclosing double quotes from TXT records lacking whitespace.
- // Here we reconcile that fact by making this state match that reality.
- if target, err2 := strconv.Unquote(ep.Targets[0]); err2 == nil && !strings.Contains(ep.Targets[0], " ") {
- ep.Targets = endpoint.Targets{target}
- }
- obj := ibclient.NewEmptyRecordTXT()
- obj.Name = &ep.DNSName
- obj.Text = &ep.Targets[0]
- obj.View = &p.view
- if getObject {
- queryParams := ibclient.NewQueryParams(false, map[string]string{"name": *obj.Name})
- err = p.client.GetObject(obj, "", queryParams, &res)
- if err != nil && !isNotFoundError(err) {
- return
- }
- }
- recordSet = infobloxRecordSet{
- obj: obj,
- res: &res,
- }
- }
- return
-}
-
-func (p *ProviderConfig) createRecords(created infobloxChangeMap) {
- for zone, endpoints := range created {
- for _, ep := range endpoints {
- for targetIndex := range ep.Targets {
- if p.dryRun {
- logrus.Infof(
-
- "Would create %s record named '%s' to '%s' for Infoblox DNS zone '%s'.",
- ep.RecordType,
- ep.DNSName,
- ep.Targets[targetIndex],
- zone,
- )
- continue
- }
-
- logrus.Infof(
- "Creating %s record named '%s' to '%s' for Infoblox DNS zone '%s'.",
- ep.RecordType,
- ep.DNSName,
- ep.Targets[targetIndex],
- zone,
- )
-
- recordSet, err := p.recordSet(ep, false, targetIndex)
- if err != nil && !isNotFoundError(err) {
- logrus.Errorf(
- "Failed to retrieve %s record named '%s' to '%s' for DNS zone '%s': %v",
- ep.RecordType,
- ep.DNSName,
- ep.Targets[targetIndex],
- zone,
- err,
- )
- continue
- }
- _, err = p.client.CreateObject(recordSet.obj)
- if err != nil {
- logrus.Errorf(
- "Failed to create %s record named '%s' to '%s' for DNS zone '%s': %v",
- ep.RecordType,
- ep.DNSName,
- ep.Targets[targetIndex],
- zone,
- err,
- )
- }
- }
- }
- }
-}
-
-func (p *ProviderConfig) deleteRecords(deleted infobloxChangeMap) {
- // Delete records first
- for zone, endpoints := range deleted {
- for _, ep := range endpoints {
- for targetIndex := range ep.Targets {
- recordSet, err := p.recordSet(ep, true, targetIndex)
- if err != nil && !isNotFoundError(err) {
- logrus.Errorf(
- "Failed to retrieve %s record named '%s' to '%s' for DNS zone '%s': %v",
- ep.RecordType,
- ep.DNSName,
- ep.Targets[targetIndex],
- zone,
- err,
- )
- continue
- }
- switch ep.RecordType {
- case endpoint.RecordTypeA:
- for _, record := range *recordSet.res.(*[]ibclient.RecordA) {
- if p.dryRun {
- logrus.Infof("Would delete %s record named '%p' to '%p' for Infoblox DNS zone '%s'.", "A", record.Name, record.Ipv4Addr, record.Zone)
- } else {
- logrus.Infof("Deleting %s record named '%p' to '%p' for Infoblox DNS zone '%s'.", "A", record.Name, record.Ipv4Addr, record.Zone)
- _, err = p.client.DeleteObject(record.Ref)
- }
- }
- case endpoint.RecordTypePTR:
- 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.Infof("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)
- }
- }
- case endpoint.RecordTypeCNAME:
- 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.Infof("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)
- }
- }
- case endpoint.RecordTypeTXT:
- 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.Infof("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)
- }
- }
- }
- if err != nil && !isNotFoundError(err) {
- logrus.Errorf(
- "Failed to delete %s record named '%s' to '%s' for Infoblox DNS zone '%s': %v",
- ep.RecordType,
- ep.DNSName,
- ep.Targets[targetIndex],
- zone,
- err,
- )
- }
- }
- }
- }
-}
-
-func lookupEnvAtoi(key string, fallback int) (i int) {
- val, ok := os.LookupEnv(key)
- if !ok {
- i = fallback
- return
- }
- i, err := strconv.Atoi(val)
- if err != nil {
- i = fallback
- return
- }
- return
-}
diff --git a/provider/infoblox/infoblox_test.go b/provider/infoblox/infoblox_test.go
deleted file mode 100644
index b2d0a256e2..0000000000
--- a/provider/infoblox/infoblox_test.go
+++ /dev/null
@@ -1,939 +0,0 @@
-/*
-Copyright 2017 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 infoblox
-
-import (
- "bytes"
- "context"
- "encoding/base64"
- "fmt"
- "net/http"
- "net/url"
- "regexp"
- "strings"
- "testing"
-
- ibclient "github.com/infobloxopen/infoblox-go-client/v2"
- "github.com/miekg/dns"
- "github.com/stretchr/testify/assert"
-
- "sigs.k8s.io/external-dns/endpoint"
- "sigs.k8s.io/external-dns/internal/testutils"
- "sigs.k8s.io/external-dns/plan"
- "sigs.k8s.io/external-dns/provider"
-)
-
-type mockIBConnector struct {
- mockInfobloxZones *[]ibclient.ZoneAuth
- mockInfobloxObjects *[]ibclient.IBObject
- createdEndpoints []*endpoint.Endpoint
- deletedEndpoints []*endpoint.Endpoint
- updatedEndpoints []*endpoint.Endpoint
- getObjectRequests []*getObjectRequest
- requestBuilder ExtendedRequestBuilder
-}
-
-type getObjectRequest struct {
- obj string
- ref string
- queryParams string
- url url.URL
- verified bool
-}
-
-func (req *getObjectRequest) ExpectRequestURLQueryParam(t *testing.T, name string, value string) *getObjectRequest {
- if req.url.Query().Get(name) != value {
- t.Errorf("Expected GetObject Request URL to contain query parameter %s=%s, Got: %v", name, value, req.url.Query())
- }
-
- return req
-}
-
-func (req *getObjectRequest) ExpectNotRequestURLQueryParam(t *testing.T, name string) *getObjectRequest {
- if req.url.Query().Has(name) {
- t.Errorf("Expected GetObject Request URL not to contain query parameter %s, Got: %v", name, req.url.Query())
- }
-
- return req
-}
-
-func (client *mockIBConnector) verifyGetObjectRequest(t *testing.T, obj string, ref string, query *map[string]string) *getObjectRequest {
- qp := ""
- if query != nil {
- qp = fmt.Sprint(ibclient.NewQueryParams(false, *query))
- }
-
- for _, req := range client.getObjectRequests {
- if !req.verified && req.obj == obj && req.ref == ref && req.queryParams == qp {
- req.verified = true
- return req
- }
- }
-
- t.Errorf("Expected GetObject obj=%s, query=%s, ref=%s", obj, qp, ref)
- return &getObjectRequest{}
-}
-
-// verifyNoMoreGetObjectRequests will assert that all "GetObject" calls have been verified.
-func (client *mockIBConnector) verifyNoMoreGetObjectRequests(t *testing.T) {
- unverified := []getObjectRequest{}
- for _, req := range client.getObjectRequests {
- if !req.verified {
- unverified = append(unverified, *req)
- }
- }
-
- if len(unverified) > 0 {
- b := new(bytes.Buffer)
- for _, req := range unverified {
- fmt.Fprintf(b, "obj=%s, ref=%s, params=%s (url=%s)\n", req.obj, req.ref, req.queryParams, req.url.String())
- }
-
- t.Errorf("Unverified GetObject Requests: %v", unverified)
- }
-}
-
-func (client *mockIBConnector) CreateObject(obj ibclient.IBObject) (ref string, err error) {
- switch obj.ObjectType() {
- case "record:a":
- client.createdEndpoints = append(
- client.createdEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.RecordA).Name,
- endpoint.RecordTypeA,
- *obj.(*ibclient.RecordA).Ipv4Addr,
- ),
- )
- ref = fmt.Sprintf("%s/%s:%s/default", obj.ObjectType(), base64.StdEncoding.EncodeToString([]byte(*obj.(*ibclient.RecordA).Name)), *obj.(*ibclient.RecordA).Name)
- obj.(*ibclient.RecordA).Ref = ref
- case "record:cname":
- client.createdEndpoints = append(
- client.createdEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.RecordCNAME).Name,
- endpoint.RecordTypeCNAME,
- *obj.(*ibclient.RecordCNAME).Canonical,
- ),
- )
- ref = fmt.Sprintf("%s/%s:%s/default", obj.ObjectType(), base64.StdEncoding.EncodeToString([]byte(*obj.(*ibclient.RecordCNAME).Name)), *obj.(*ibclient.RecordCNAME).Name)
- obj.(*ibclient.RecordCNAME).Ref = ref
- case "record:host":
- for _, i := range obj.(*ibclient.HostRecord).Ipv4Addrs {
- client.createdEndpoints = append(
- client.createdEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.HostRecord).Name,
- endpoint.RecordTypeA,
- *i.Ipv4Addr,
- ),
- )
- }
- ref = fmt.Sprintf("%s/%s:%s/default", obj.ObjectType(), base64.StdEncoding.EncodeToString([]byte(*obj.(*ibclient.HostRecord).Name)), *obj.(*ibclient.HostRecord).Name)
- obj.(*ibclient.HostRecord).Ref = ref
- case "record:txt":
- client.createdEndpoints = append(
- client.createdEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.RecordTXT).Name,
- endpoint.RecordTypeTXT,
- *obj.(*ibclient.RecordTXT).Text,
- ),
- )
- obj.(*ibclient.RecordTXT).Ref = ref
- ref = fmt.Sprintf("%s/%s:%s/default", obj.ObjectType(), base64.StdEncoding.EncodeToString([]byte(*obj.(*ibclient.RecordTXT).Name)), *obj.(*ibclient.RecordTXT).Name)
- case "record:ptr":
- client.createdEndpoints = append(
- client.createdEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.RecordPTR).PtrdName,
- endpoint.RecordTypePTR,
- *obj.(*ibclient.RecordPTR).Ipv4Addr,
- ),
- )
- obj.(*ibclient.RecordPTR).Ref = ref
- reverseAddr, err := dns.ReverseAddr(*obj.(*ibclient.RecordPTR).Ipv4Addr)
- if err != nil {
- return ref, fmt.Errorf("unable to create reverse addr from %s", *obj.(*ibclient.RecordPTR).Ipv4Addr)
- }
- ref = fmt.Sprintf("%s/%s:%s/default", obj.ObjectType(), base64.StdEncoding.EncodeToString([]byte(*obj.(*ibclient.RecordPTR).PtrdName)), reverseAddr)
- }
- *client.mockInfobloxObjects = append(
- *client.mockInfobloxObjects,
- obj,
- )
- return ref, nil
-}
-
-func (client *mockIBConnector) GetObject(obj ibclient.IBObject, ref string, queryParams *ibclient.QueryParams, res interface{}) (err error) {
- req := getObjectRequest{
- obj: obj.ObjectType(),
- ref: ref,
- }
- if queryParams != nil {
- req.queryParams = fmt.Sprint(queryParams)
- }
- r, _ := client.requestBuilder.BuildRequest(ibclient.GET, obj, ref, queryParams)
- if r != nil {
- req.url = *r.URL
- }
- client.getObjectRequests = append(client.getObjectRequests, &req)
- switch obj.ObjectType() {
- case "record:a":
- var result []ibclient.RecordA
- for _, object := range *client.mockInfobloxObjects {
- if object.ObjectType() == "record:a" {
- if ref != "" &&
- ref != object.(*ibclient.RecordA).Ref {
- continue
- }
- if obj.(*ibclient.RecordA).Name != nil &&
- *obj.(*ibclient.RecordA).Name != *object.(*ibclient.RecordA).Name {
- continue
- }
- result = append(result, *object.(*ibclient.RecordA))
- }
- }
- *res.(*[]ibclient.RecordA) = result
- case "record:cname":
- var result []ibclient.RecordCNAME
- for _, object := range *client.mockInfobloxObjects {
- if object.ObjectType() == "record:cname" {
- if ref != "" &&
- ref != object.(*ibclient.RecordCNAME).Ref {
- continue
- }
- if obj.(*ibclient.RecordCNAME).Name != nil &&
- *obj.(*ibclient.RecordCNAME).Name != *object.(*ibclient.RecordCNAME).Name {
- continue
- }
- result = append(result, *object.(*ibclient.RecordCNAME))
- }
- }
- *res.(*[]ibclient.RecordCNAME) = result
- case "record:host":
- var result []ibclient.HostRecord
- for _, object := range *client.mockInfobloxObjects {
- if object.ObjectType() == "record:host" {
- if ref != "" &&
- ref != object.(*ibclient.HostRecord).Ref {
- continue
- }
- if obj.(*ibclient.HostRecord).Name != nil &&
- *obj.(*ibclient.HostRecord).Name != *object.(*ibclient.HostRecord).Name {
- continue
- }
- result = append(result, *object.(*ibclient.HostRecord))
- }
- }
- *res.(*[]ibclient.HostRecord) = result
- case "record:txt":
- var result []ibclient.RecordTXT
- for _, object := range *client.mockInfobloxObjects {
- if object.ObjectType() == "record:txt" {
- if ref != "" &&
- ref != object.(*ibclient.RecordTXT).Ref {
- continue
- }
- if obj.(*ibclient.RecordTXT).Name != nil &&
- *obj.(*ibclient.RecordTXT).Name != *object.(*ibclient.RecordTXT).Name {
- continue
- }
- result = append(result, *object.(*ibclient.RecordTXT))
- }
- }
- *res.(*[]ibclient.RecordTXT) = result
- case "record:ptr":
- var result []ibclient.RecordPTR
- for _, object := range *client.mockInfobloxObjects {
- if object.ObjectType() == "record:ptr" {
- if ref != "" &&
- ref != object.(*ibclient.RecordPTR).Ref {
- continue
- }
- if obj.(*ibclient.RecordPTR).PtrdName != nil &&
- *obj.(*ibclient.RecordPTR).PtrdName != *object.(*ibclient.RecordPTR).PtrdName {
- continue
- }
- result = append(result, *object.(*ibclient.RecordPTR))
- }
- }
- *res.(*[]ibclient.RecordPTR) = result
- case "zone_auth":
- *res.(*[]ibclient.ZoneAuth) = *client.mockInfobloxZones
- }
- return
-}
-
-func (client *mockIBConnector) DeleteObject(ref string) (refRes string, err error) {
- re := regexp.MustCompile(`([^/]+)/[^:]+:([^/]+)/default`)
- result := re.FindStringSubmatch(ref)
-
- switch result[1] {
- case "record:a":
- var records []ibclient.RecordA
- obj := ibclient.NewEmptyRecordA()
- obj.Name = &result[2]
- client.GetObject(obj, ref, nil, &records)
- for _, record := range records {
- client.deletedEndpoints = append(
- client.deletedEndpoints,
- endpoint.NewEndpoint(
- *record.Name,
- endpoint.RecordTypeA,
- "",
- ),
- )
- }
- case "record:cname":
- var records []ibclient.RecordCNAME
- obj := ibclient.NewEmptyRecordCNAME()
- obj.Name = &result[2]
- client.GetObject(obj, ref, nil, &records)
- for _, record := range records {
- client.deletedEndpoints = append(
- client.deletedEndpoints,
- endpoint.NewEndpoint(
- *record.Name,
- endpoint.RecordTypeCNAME,
- "",
- ),
- )
- }
- case "record:host":
- var records []ibclient.HostRecord
- obj := ibclient.NewEmptyHostRecord()
- obj.Name = &result[2]
- client.GetObject(obj, ref, nil, &records)
- for _, record := range records {
- client.deletedEndpoints = append(
- client.deletedEndpoints,
- endpoint.NewEndpoint(
- *record.Name,
- endpoint.RecordTypeA,
- "",
- ),
- )
- }
- case "record:txt":
- var records []ibclient.RecordTXT
- obj := ibclient.NewEmptyRecordTXT()
- obj.Name = &result[2]
- client.GetObject(obj, ref, nil, &records)
- for _, record := range records {
- client.deletedEndpoints = append(
- client.deletedEndpoints,
- endpoint.NewEndpoint(
- *record.Name,
- endpoint.RecordTypeTXT,
- "",
- ),
- )
- }
- case "record:ptr":
- var records []ibclient.RecordPTR
- obj := ibclient.NewEmptyRecordPTR()
- obj.Name = &result[2]
- client.GetObject(obj, ref, nil, &records)
- for _, record := range records {
- client.deletedEndpoints = append(
- client.deletedEndpoints,
- endpoint.NewEndpoint(
- *record.PtrdName,
- endpoint.RecordTypePTR,
- "",
- ),
- )
- }
- }
- return "", nil
-}
-
-func (client *mockIBConnector) UpdateObject(obj ibclient.IBObject, ref string) (refRes string, err error) {
- switch obj.ObjectType() {
- case "record:a":
- client.updatedEndpoints = append(
- client.updatedEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.RecordA).Name,
- *obj.(*ibclient.RecordA).Ipv4Addr,
- endpoint.RecordTypeA,
- ),
- )
- case "record:cname":
- client.updatedEndpoints = append(
- client.updatedEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.RecordCNAME).Name,
- *obj.(*ibclient.RecordCNAME).Canonical,
- endpoint.RecordTypeCNAME,
- ),
- )
- case "record:host":
- for _, i := range obj.(*ibclient.HostRecord).Ipv4Addrs {
- client.updatedEndpoints = append(
- client.updatedEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.HostRecord).Name,
- *i.Ipv4Addr,
- endpoint.RecordTypeA,
- ),
- )
- }
- case "record:txt":
- client.updatedEndpoints = append(
- client.updatedEndpoints,
- endpoint.NewEndpoint(
- *obj.(*ibclient.RecordTXT).Name,
- *obj.(*ibclient.RecordTXT).Text,
- endpoint.RecordTypeTXT,
- ),
- )
- }
- return "", nil
-}
-
-func createMockInfobloxZone(fqdn string) ibclient.ZoneAuth {
- return ibclient.ZoneAuth{
- Fqdn: fqdn,
- }
-}
-
-func createMockInfobloxObject(name, recordType, value string) ibclient.IBObject {
- ref := fmt.Sprintf("record:%s/%s:%s/default", strings.ToLower(recordType), base64.StdEncoding.EncodeToString([]byte(name)), name)
- switch recordType {
- case endpoint.RecordTypeA:
- obj := ibclient.NewEmptyRecordA()
- obj.Name = &name
- obj.Ref = ref
- obj.Ipv4Addr = &value
- return obj
- case endpoint.RecordTypeCNAME:
- obj := ibclient.NewEmptyRecordCNAME()
- obj.Name = &name
- obj.Ref = ref
- obj.Canonical = &value
- return obj
- case endpoint.RecordTypeTXT:
- obj := ibclient.NewEmptyRecordTXT()
- obj.Name = &name
- obj.Ref = ref
- obj.Text = &value
- return obj
- case "HOST":
- obj := ibclient.NewEmptyHostRecord()
- obj.Name = &name
- obj.Ref = ref
- obj.Ipv4Addrs = []ibclient.HostRecordIpv4Addr{
- {
- Ipv4Addr: &value,
- },
- }
- return obj
- case endpoint.RecordTypePTR:
- obj := ibclient.NewEmptyRecordPTR()
- obj.PtrdName = &name
- obj.Ref = ref
- obj.Ipv4Addr = &value
- return obj
- }
-
- return nil
-}
-
-func newInfobloxProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, view string, dryRun bool, createPTR bool, client ibclient.IBConnector) *ProviderConfig {
- return &ProviderConfig{
- client: client,
- domainFilter: domainFilter,
- zoneIDFilter: zoneIDFilter,
- dryRun: dryRun,
- createPTR: createPTR,
- view: view,
- }
-}
-
-func TestInfobloxRecords(t *testing.T) {
- client := mockIBConnector{
- mockInfobloxZones: &[]ibclient.ZoneAuth{
- createMockInfobloxZone("example.com"),
- createMockInfobloxZone("other.com"),
- },
- mockInfobloxObjects: &[]ibclient.IBObject{
- createMockInfobloxObject("example.com", endpoint.RecordTypeA, "123.123.123.122"),
- createMockInfobloxObject("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
- createMockInfobloxObject("nginx.example.com", endpoint.RecordTypeA, "123.123.123.123"),
- createMockInfobloxObject("nginx.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
- createMockInfobloxObject("whitespace.example.com", endpoint.RecordTypeA, "123.123.123.124"),
- createMockInfobloxObject("whitespace.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=white space"),
- createMockInfobloxObject("hack.example.com", endpoint.RecordTypeCNAME, "cerberus.infoblox.com"),
- createMockInfobloxObject("multiple.example.com", endpoint.RecordTypeA, "123.123.123.122"),
- createMockInfobloxObject("multiple.example.com", endpoint.RecordTypeA, "123.123.123.121"),
- createMockInfobloxObject("multiple.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
- createMockInfobloxObject("existing.example.com", endpoint.RecordTypeA, "124.1.1.1"),
- createMockInfobloxObject("existing.example.com", endpoint.RecordTypeA, "124.1.1.2"),
- createMockInfobloxObject("existing.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=existing"),
- createMockInfobloxObject("host.example.com", "HOST", "125.1.1.1"),
- },
- }
-
- providerCfg := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), "", true, false, &client)
- actual, err := providerCfg.Records(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- expected := []*endpoint.Endpoint{
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122"),
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "\"heritage=external-dns,external-dns/owner=default\""),
- endpoint.NewEndpoint("nginx.example.com", endpoint.RecordTypeA, "123.123.123.123"),
- endpoint.NewEndpoint("nginx.example.com", endpoint.RecordTypeTXT, "\"heritage=external-dns,external-dns/owner=default\""),
- endpoint.NewEndpoint("whitespace.example.com", endpoint.RecordTypeA, "123.123.123.124"),
- endpoint.NewEndpoint("whitespace.example.com", endpoint.RecordTypeTXT, "\"heritage=external-dns,external-dns/owner=white space\""),
- endpoint.NewEndpoint("hack.example.com", endpoint.RecordTypeCNAME, "cerberus.infoblox.com"),
- endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeA, "123.123.123.122", "123.123.123.121"),
- endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeTXT, "\"heritage=external-dns,external-dns/owner=default\""),
- endpoint.NewEndpoint("existing.example.com", endpoint.RecordTypeA, "124.1.1.1", "124.1.1.2"),
- endpoint.NewEndpoint("existing.example.com", endpoint.RecordTypeTXT, "\"heritage=external-dns,external-dns/owner=existing\""),
- endpoint.NewEndpoint("host.example.com", endpoint.RecordTypeA, "125.1.1.1"),
- }
- validateEndpoints(t, actual, expected)
- client.verifyGetObjectRequest(t, "zone_auth", "", &map[string]string{}).
- ExpectNotRequestURLQueryParam(t, "view").
- ExpectNotRequestURLQueryParam(t, "zone")
- client.verifyGetObjectRequest(t, "record:a", "", &map[string]string{"zone": "example.com"}).
- ExpectRequestURLQueryParam(t, "zone", "example.com")
- client.verifyGetObjectRequest(t, "record:host", "", &map[string]string{"zone": "example.com"}).
- ExpectRequestURLQueryParam(t, "zone", "example.com")
- client.verifyGetObjectRequest(t, "record:cname", "", &map[string]string{"zone": "example.com"}).
- ExpectRequestURLQueryParam(t, "zone", "example.com")
- client.verifyGetObjectRequest(t, "record:txt", "", &map[string]string{"zone": "example.com"}).
- ExpectRequestURLQueryParam(t, "zone", "example.com")
- client.verifyNoMoreGetObjectRequests(t)
-}
-
-func TestInfobloxRecordsWithView(t *testing.T) {
- client := mockIBConnector{
- mockInfobloxZones: &[]ibclient.ZoneAuth{
- createMockInfobloxZone("foo.example.com"),
- createMockInfobloxZone("bar.example.com"),
- },
- mockInfobloxObjects: &[]ibclient.IBObject{
- createMockInfobloxObject("cat.foo.example.com", endpoint.RecordTypeA, "123.123.123.122"),
- createMockInfobloxObject("dog.bar.example.com", endpoint.RecordTypeA, "123.123.123.123"),
- },
- }
-
- providerCfg := newInfobloxProvider(endpoint.NewDomainFilter([]string{"foo.example.com", "bar.example.com"}), provider.NewZoneIDFilter([]string{""}), "Inside", true, false, &client)
- actual, err := providerCfg.Records(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- expected := []*endpoint.Endpoint{
- endpoint.NewEndpoint("cat.foo.example.com", endpoint.RecordTypeA, "123.123.123.122"),
- endpoint.NewEndpoint("dog.bar.example.com", endpoint.RecordTypeA, "123.123.123.123"),
- }
- validateEndpoints(t, actual, expected)
- client.verifyGetObjectRequest(t, "zone_auth", "", &map[string]string{"view": "Inside"}).
- ExpectRequestURLQueryParam(t, "view", "Inside").
- ExpectNotRequestURLQueryParam(t, "zone")
- client.verifyGetObjectRequest(t, "record:a", "", &map[string]string{"zone": "foo.example.com", "view": "Inside"}).
- ExpectRequestURLQueryParam(t, "zone", "foo.example.com").
- ExpectRequestURLQueryParam(t, "view", "Inside")
- client.verifyGetObjectRequest(t, "record:host", "", &map[string]string{"zone": "foo.example.com", "view": "Inside"}).
- ExpectRequestURLQueryParam(t, "zone", "foo.example.com").
- ExpectRequestURLQueryParam(t, "view", "Inside")
- client.verifyGetObjectRequest(t, "record:cname", "", &map[string]string{"zone": "foo.example.com", "view": "Inside"}).
- ExpectRequestURLQueryParam(t, "zone", "foo.example.com").
- ExpectRequestURLQueryParam(t, "view", "Inside")
- client.verifyGetObjectRequest(t, "record:txt", "", &map[string]string{"zone": "foo.example.com", "view": "Inside"}).
- ExpectRequestURLQueryParam(t, "zone", "foo.example.com").
- ExpectRequestURLQueryParam(t, "view", "Inside")
- client.verifyGetObjectRequest(t, "record:a", "", &map[string]string{"zone": "bar.example.com", "view": "Inside"}).
- ExpectRequestURLQueryParam(t, "zone", "bar.example.com").
- ExpectRequestURLQueryParam(t, "view", "Inside")
- client.verifyGetObjectRequest(t, "record:host", "", &map[string]string{"zone": "bar.example.com", "view": "Inside"}).
- ExpectRequestURLQueryParam(t, "zone", "bar.example.com").
- ExpectRequestURLQueryParam(t, "view", "Inside")
- client.verifyGetObjectRequest(t, "record:cname", "", &map[string]string{"zone": "bar.example.com", "view": "Inside"}).
- ExpectRequestURLQueryParam(t, "zone", "bar.example.com").
- ExpectRequestURLQueryParam(t, "view", "Inside")
- client.verifyGetObjectRequest(t, "record:txt", "", &map[string]string{"zone": "bar.example.com", "view": "Inside"}).
- ExpectRequestURLQueryParam(t, "zone", "bar.example.com").
- ExpectRequestURLQueryParam(t, "view", "Inside")
- client.verifyNoMoreGetObjectRequests(t)
-}
-
-func TestInfobloxAdjustEndpoints(t *testing.T) {
- client := mockIBConnector{
- mockInfobloxZones: &[]ibclient.ZoneAuth{
- createMockInfobloxZone("example.com"),
- createMockInfobloxZone("other.com"),
- },
- mockInfobloxObjects: &[]ibclient.IBObject{
- createMockInfobloxObject("example.com", endpoint.RecordTypeA, "123.123.123.122"),
- createMockInfobloxObject("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
- createMockInfobloxObject("hack.example.com", endpoint.RecordTypeCNAME, "cerberus.infoblox.com"),
- createMockInfobloxObject("host.example.com", "HOST", "125.1.1.1"),
- },
- }
-
- providerCfg := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), "", true, true, &client)
- actual, err := providerCfg.Records(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- providerCfg.AdjustEndpoints(actual)
-
- expected := []*endpoint.Endpoint{
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122").WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true"),
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "\"heritage=external-dns,external-dns/owner=default\""),
- endpoint.NewEndpoint("hack.example.com", endpoint.RecordTypeCNAME, "cerberus.infoblox.com"),
- endpoint.NewEndpoint("host.example.com", endpoint.RecordTypeA, "125.1.1.1").WithProviderSpecific(providerSpecificInfobloxPtrRecord, "true"),
- }
- validateEndpoints(t, actual, expected)
-}
-
-func TestInfobloxRecordsReverse(t *testing.T) {
- client := mockIBConnector{
- mockInfobloxZones: &[]ibclient.ZoneAuth{
- createMockInfobloxZone("10.0.0.0/24"),
- createMockInfobloxZone("10.0.1.0/24"),
- },
- mockInfobloxObjects: &[]ibclient.IBObject{
- createMockInfobloxObject("example.com", endpoint.RecordTypePTR, "10.0.0.1"),
- createMockInfobloxObject("example2.com", endpoint.RecordTypePTR, "10.0.0.2"),
- },
- }
-
- providerCfg := newInfobloxProvider(endpoint.NewDomainFilter([]string{"10.0.0.0/24"}), provider.NewZoneIDFilter([]string{""}), "", true, true, &client)
- actual, err := providerCfg.Records(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- expected := []*endpoint.Endpoint{
- endpoint.NewEndpoint("example.com", endpoint.RecordTypePTR, "10.0.0.1"),
- endpoint.NewEndpoint("example2.com", endpoint.RecordTypePTR, "10.0.0.2"),
- }
- validateEndpoints(t, actual, expected)
-}
-
-func TestInfobloxApplyChanges(t *testing.T) {
- client := mockIBConnector{}
-
- testInfobloxApplyChangesInternal(t, false, false, &client)
-
- validateEndpoints(t, client.createdEndpoints, []*endpoint.Endpoint{
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"),
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
- endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"),
- endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("other.com", endpoint.RecordTypeA, "5.6.7.8"),
- endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("new.example.com", endpoint.RecordTypeA, "111.222.111.222"),
- endpoint.NewEndpoint("newcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
- endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeA, "1.2.3.4,3.4.5.6,8.9.10.11"),
- endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeTXT, "tag-multiple-A-records"),
- })
-
- validateEndpoints(t, client.deletedEndpoints, []*endpoint.Endpoint{
- endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""),
- endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""),
- endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""),
- endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""),
- })
-
- validateEndpoints(t, client.updatedEndpoints, []*endpoint.Endpoint{})
-}
-
-func TestInfobloxApplyChangesReverse(t *testing.T) {
- client := mockIBConnector{}
-
- testInfobloxApplyChangesInternal(t, false, true, &client)
-
- validateEndpoints(t, client.createdEndpoints, []*endpoint.Endpoint{
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"),
- endpoint.NewEndpoint("example.com", endpoint.RecordTypePTR, "1.2.3.4"),
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
- endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypePTR, "1.2.3.4"),
- endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"),
- endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("other.com", endpoint.RecordTypeA, "5.6.7.8"),
- endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("new.example.com", endpoint.RecordTypeA, "111.222.111.222"),
- endpoint.NewEndpoint("newcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
- endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeA, "1.2.3.4,3.4.5.6,8.9.10.11"),
- endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeTXT, "tag-multiple-A-records"),
- })
-
- validateEndpoints(t, client.deletedEndpoints, []*endpoint.Endpoint{
- endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, ""),
- endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, ""),
- endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""),
- endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypePTR, ""),
- endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""),
- })
-
- validateEndpoints(t, client.updatedEndpoints, []*endpoint.Endpoint{})
-}
-
-func TestInfobloxApplyChangesDryRun(t *testing.T) {
- client := mockIBConnector{
- mockInfobloxObjects: &[]ibclient.IBObject{},
- }
-
- testInfobloxApplyChangesInternal(t, true, false, &client)
-
- validateEndpoints(t, client.createdEndpoints, []*endpoint.Endpoint{})
-
- validateEndpoints(t, client.deletedEndpoints, []*endpoint.Endpoint{})
-
- validateEndpoints(t, client.updatedEndpoints, []*endpoint.Endpoint{})
-}
-
-func testInfobloxApplyChangesInternal(t *testing.T, dryRun, createPTR bool, client ibclient.IBConnector) {
- client.(*mockIBConnector).mockInfobloxZones = &[]ibclient.ZoneAuth{
- createMockInfobloxZone("example.com"),
- createMockInfobloxZone("other.com"),
- createMockInfobloxZone("1.2.3.0/24"),
- }
- client.(*mockIBConnector).mockInfobloxObjects = &[]ibclient.IBObject{
- createMockInfobloxObject("deleted.example.com", endpoint.RecordTypeA, "121.212.121.212"),
- createMockInfobloxObject("deleted.example.com", endpoint.RecordTypeTXT, "test-deleting-txt"),
- createMockInfobloxObject("deleted.example.com", endpoint.RecordTypePTR, "121.212.121.212"),
- createMockInfobloxObject("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
- createMockInfobloxObject("old.example.com", endpoint.RecordTypeA, "121.212.121.212"),
- createMockInfobloxObject("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
- }
-
- providerCfg := newInfobloxProvider(
- endpoint.NewDomainFilter([]string{""}),
- provider.NewZoneIDFilter([]string{""}),
- "",
- dryRun,
- createPTR,
- client,
- )
-
- createRecords := []*endpoint.Endpoint{
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "1.2.3.4"),
- endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
- endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"),
- endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("other.com", endpoint.RecordTypeA, "5.6.7.8"),
- endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"),
- endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"),
- endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeA, "1.2.3.4,3.4.5.6,8.9.10.11"),
- endpoint.NewEndpoint("multiple.example.com", endpoint.RecordTypeTXT, "tag-multiple-A-records"),
- }
-
- updateOldRecords := []*endpoint.Endpoint{
- endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, "121.212.121.212"),
- endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
- endpoint.NewEndpoint("old.nope.com", endpoint.RecordTypeA, "121.212.121.212"),
- }
-
- updateNewRecords := []*endpoint.Endpoint{
- endpoint.NewEndpoint("new.example.com", endpoint.RecordTypeA, "111.222.111.222"),
- endpoint.NewEndpoint("newcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
- endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeA, "222.111.222.111"),
- }
-
- deleteRecords := []*endpoint.Endpoint{
- endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, "121.212.121.212"),
- endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
- endpoint.NewEndpoint("deleted.nope.com", endpoint.RecordTypeA, "222.111.222.111"),
- }
-
- if createPTR {
- deleteRecords = append(deleteRecords, endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypePTR, "121.212.121.212"))
- }
-
- changes := &plan.Changes{
- Create: createRecords,
- UpdateNew: updateNewRecords,
- UpdateOld: updateOldRecords,
- Delete: deleteRecords,
- }
-
- if err := providerCfg.ApplyChanges(context.Background(), changes); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestInfobloxZones(t *testing.T) {
- client := mockIBConnector{
- mockInfobloxZones: &[]ibclient.ZoneAuth{
- createMockInfobloxZone("example.com"),
- createMockInfobloxZone("lvl1-1.example.com"),
- createMockInfobloxZone("lvl2-1.lvl1-1.example.com"),
- createMockInfobloxZone("1.2.3.0/24"),
- },
- mockInfobloxObjects: &[]ibclient.IBObject{},
- }
-
- providerCfg := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com", "1.2.3.0/24"}), provider.NewZoneIDFilter([]string{""}), "", true, false, &client)
- zones, _ := providerCfg.zones()
- var emptyZoneAuth *ibclient.ZoneAuth
- assert.Equal(t, providerCfg.findZone(zones, "example.com").Fqdn, "example.com")
- assert.Equal(t, providerCfg.findZone(zones, "nomatch-example.com"), emptyZoneAuth)
- assert.Equal(t, providerCfg.findZone(zones, "nginx.example.com").Fqdn, "example.com")
- assert.Equal(t, providerCfg.findZone(zones, "lvl1-1.example.com").Fqdn, "lvl1-1.example.com")
- assert.Equal(t, providerCfg.findZone(zones, "lvl1-2.example.com").Fqdn, "example.com")
- assert.Equal(t, providerCfg.findZone(zones, "lvl2-1.lvl1-1.example.com").Fqdn, "lvl2-1.lvl1-1.example.com")
- assert.Equal(t, providerCfg.findZone(zones, "lvl2-2.lvl1-1.example.com").Fqdn, "lvl1-1.example.com")
- assert.Equal(t, providerCfg.findZone(zones, "lvl2-2.lvl1-2.example.com").Fqdn, "example.com")
- assert.Equal(t, providerCfg.findZone(zones, "1.2.3.0/24").Fqdn, "1.2.3.0/24")
-}
-
-func TestInfobloxReverseZones(t *testing.T) {
- client := mockIBConnector{
- mockInfobloxZones: &[]ibclient.ZoneAuth{
- createMockInfobloxZone("example.com"),
- createMockInfobloxZone("1.2.3.0/24"),
- createMockInfobloxZone("10.0.0.0/8"),
- },
- mockInfobloxObjects: &[]ibclient.IBObject{},
- }
-
- providerCfg := newInfobloxProvider(endpoint.NewDomainFilter([]string{"example.com", "1.2.3.0/24", "10.0.0.0/8"}), provider.NewZoneIDFilter([]string{""}), "", true, false, &client)
- zones, _ := providerCfg.zones()
- var emptyZoneAuth *ibclient.ZoneAuth
- assert.Equal(t, providerCfg.findReverseZone(zones, "nomatch-example.com"), emptyZoneAuth)
- assert.Equal(t, providerCfg.findReverseZone(zones, "192.168.0.1"), emptyZoneAuth)
- assert.Equal(t, providerCfg.findReverseZone(zones, "1.2.3.4").Fqdn, "1.2.3.0/24")
- assert.Equal(t, providerCfg.findReverseZone(zones, "10.28.29.30").Fqdn, "10.0.0.0/8")
-}
-
-func TestExtendedRequestFDQDRegExBuilder(t *testing.T) {
- hostCfg := ibclient.HostConfig{
- Host: "localhost",
- Port: "8080",
- Version: "2.3.1",
- }
-
- authCfg := ibclient.AuthConfig{
- Username: "user",
- Password: "abcd",
- }
-
- requestBuilder := NewExtendedRequestBuilder(0, "^staging.*test.com$", "")
- requestBuilder.Init(hostCfg, authCfg)
-
- obj := ibclient.NewZoneAuth(ibclient.ZoneAuth{})
-
- req, _ := requestBuilder.BuildRequest(ibclient.GET, obj, "", &ibclient.QueryParams{})
-
- assert.True(t, req.URL.Query().Get("fqdn~") == "^staging.*test.com$")
-
- req, _ = requestBuilder.BuildRequest(ibclient.CREATE, obj, "", &ibclient.QueryParams{})
-
- assert.True(t, req.URL.Query().Get("fqdn~") == "")
-}
-
-func TestExtendedRequestNameRegExBuilder(t *testing.T) {
- hostCfg := ibclient.HostConfig{
- Host: "localhost",
- Port: "8080",
- Version: "2.3.1",
- }
-
- authCfg := ibclient.AuthConfig{
- Username: "user",
- Password: "abcd",
- }
-
- requestBuilder := NewExtendedRequestBuilder(0, "", "^staging.*test.com$")
- requestBuilder.Init(hostCfg, authCfg)
-
- obj := ibclient.NewEmptyRecordCNAME()
-
- req, _ := requestBuilder.BuildRequest(ibclient.GET, obj, "", &ibclient.QueryParams{})
-
- assert.True(t, req.URL.Query().Get("name~") == "^staging.*test.com$")
-
- req, _ = requestBuilder.BuildRequest(ibclient.CREATE, obj, "", &ibclient.QueryParams{})
-
- assert.True(t, req.URL.Query().Get("name~") == "")
-}
-
-func TestExtendedRequestMaxResultsBuilder(t *testing.T) {
- hostCfg := ibclient.HostConfig{
- Host: "localhost",
- Port: "8080",
- Version: "2.3.1",
- }
-
- authCfg := ibclient.AuthConfig{
- Username: "user",
- Password: "abcd",
- }
-
- requestBuilder := NewExtendedRequestBuilder(54321, "", "")
- requestBuilder.Init(hostCfg, authCfg)
-
- obj := ibclient.NewEmptyRecordCNAME()
- obj.Zone = "foo.bar.com"
-
- req, _ := requestBuilder.BuildRequest(ibclient.GET, obj, "", &ibclient.QueryParams{})
-
- assert.True(t, req.URL.Query().Get("_max_results") == "54321")
-
- req, _ = requestBuilder.BuildRequest(ibclient.CREATE, obj, "", &ibclient.QueryParams{})
-
- assert.True(t, req.URL.Query().Get("_max_results") == "")
-}
-
-func TestGetObject(t *testing.T) {
- hostCfg := ibclient.HostConfig{}
- authCfg := ibclient.AuthConfig{}
- transportConfig := ibclient.TransportConfig{}
- requestBuilder := NewExtendedRequestBuilder(1000, "mysite.com", "")
- requestor := mockRequestor{}
- client, _ := ibclient.NewConnector(hostCfg, authCfg, transportConfig, requestBuilder, &requestor)
-
- providerConfig := newInfobloxProvider(endpoint.NewDomainFilter([]string{"mysite.com"}), provider.NewZoneIDFilter([]string{""}), "", true, true, client)
-
- providerConfig.deleteRecords(infobloxChangeMap{
- "myzone.com": []*endpoint.Endpoint{
- endpoint.NewEndpoint("deletethisrecord.com", endpoint.RecordTypeA, "1.2.3.4"),
- },
- })
-
- requestQuery := requestor.request.URL.Query()
- assert.True(t, requestQuery.Has("name"), "Expected the request to filter objects by name")
-}
-
-// Mock requestor that doesn't send request
-type mockRequestor struct {
- request *http.Request
-}
-
-func (r *mockRequestor) Init(ibclient.AuthConfig, ibclient.TransportConfig) {}
-func (r *mockRequestor) SendRequest(req *http.Request) (res []byte, err error) {
- res = []byte("[{}]")
- r.request = req
- return
-}
-
-func validateEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expected []*endpoint.Endpoint) {
- assert.True(t, testutils.SameEndpoints(endpoints, expected), "actual and expected endpoints don't match. %s:%s", endpoints, expected)
-}
diff --git a/provider/webhook/webhook_test.go b/provider/webhook/webhook_test.go
index 03b02c20f7..999cdc3bfb 100644
--- a/provider/webhook/webhook_test.go
+++ b/provider/webhook/webhook_test.go
@@ -26,6 +26,7 @@ import (
"github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint"
+ "sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
webhookapi "sigs.k8s.io/external-dns/provider/webhook/api"
)
@@ -217,3 +218,54 @@ func TestAdjustendpointsWithError(t *testing.T) {
require.Error(t, err)
require.ErrorIs(t, err, provider.SoftError)
}
+
+// test apply changes with an endpoint with a provider specific property
+func TestApplyChangesWithProviderSpecificProperty(t *testing.T) {
+ svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/" {
+ w.Header().Set(webhookapi.ContentTypeHeader, webhookapi.MediaTypeFormatAndVersion)
+ w.Write([]byte(`{}`))
+ return
+ }
+ if r.URL.Path == "/records" {
+ w.Header().Set(webhookapi.ContentTypeHeader, webhookapi.MediaTypeFormatAndVersion)
+ // assert that the request contains the provider specific property
+ var changes plan.Changes
+ defer r.Body.Close()
+ b, err := io.ReadAll(r.Body)
+ require.Nil(t, err)
+ err = json.Unmarshal(b, &changes)
+ require.Nil(t, err)
+ require.Len(t, changes.Create, 1)
+ require.Len(t, changes.Create[0].ProviderSpecific, 1)
+ require.Equal(t, "prop1", changes.Create[0].ProviderSpecific[0].Name)
+ require.Equal(t, "value1", changes.Create[0].ProviderSpecific[0].Value)
+ w.WriteHeader(http.StatusNoContent)
+ return
+ }
+ }))
+ defer svr.Close()
+
+ p, err := NewWebhookProvider(svr.URL)
+ require.NoError(t, err)
+ e := &endpoint.Endpoint{
+ DNSName: "test.example.com",
+ RecordTTL: 10,
+ RecordType: "A",
+ Targets: endpoint.Targets{
+ "",
+ },
+ ProviderSpecific: endpoint.ProviderSpecific{
+ endpoint.ProviderSpecificProperty{
+ Name: "prop1",
+ Value: "value1",
+ },
+ },
+ }
+ err = p.ApplyChanges(context.TODO(), &plan.Changes{
+ Create: []*endpoint.Endpoint{
+ e,
+ },
+ })
+ require.NoError(t, err)
+}
diff --git a/source/source.go b/source/source.go
index c7d46c9708..132f40dcd9 100644
--- a/source/source.go
+++ b/source/source.go
@@ -224,6 +224,13 @@ func getProviderSpecificAnnotations(annotations map[string]string) (endpoint.Pro
Name: fmt.Sprintf("ibmcloud-%s", attr),
Value: v,
})
+ } else if strings.HasPrefix(k, "external-dns.alpha.kubernetes.io/webhook-") {
+ // Support for wildcard annotations for webhook providers
+ attr := strings.TrimPrefix(k, "external-dns.alpha.kubernetes.io/webhook-")
+ providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{
+ Name: fmt.Sprintf("webhook/%s", attr),
+ Value: v,
+ })
}
}
return providerSpecificAnnotations, setIdentifier
diff --git a/source/traefik_proxy.go b/source/traefik_proxy.go
index 0c79e95315..ea4c41f5f4 100644
--- a/source/traefik_proxy.go
+++ b/source/traefik_proxy.go
@@ -759,14 +759,26 @@ func (ts *traefikSource) AddEventHandler(ctx context.Context, handler func()) {
// Right now there is no way to remove event handler from informer, see:
// https://github.com/kubernetes/kubernetes/issues/79610
log.Debug("Adding event handler for IngressRoute")
- ts.ingressRouteInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
- ts.oldIngressRouteInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ if ts.ingressRouteInformer != nil {
+ ts.ingressRouteInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ }
+ if ts.oldIngressRouteInformer != nil {
+ ts.oldIngressRouteInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ }
log.Debug("Adding event handler for IngressRouteTCP")
- ts.ingressRouteTcpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
- ts.oldIngressRouteTcpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ if ts.ingressRouteTcpInformer != nil {
+ ts.ingressRouteTcpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ }
+ if ts.oldIngressRouteTcpInformer != nil {
+ ts.oldIngressRouteTcpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ }
log.Debug("Adding event handler for IngressRouteUDP")
- ts.ingressRouteUdpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
- ts.oldIngressRouteUdpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ if ts.ingressRouteUdpInformer != nil {
+ ts.ingressRouteUdpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ }
+ if ts.oldIngressRouteUdpInformer != nil {
+ ts.oldIngressRouteUdpInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
+ }
}
// newTraefikUnstructuredConverter returns a new unstructuredConverter initialized