diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 768f0d87a6..030ad5887a 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Check out code into the Go module directory
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Go 1.x
uses: actions/setup-go@v5
diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml
index 237c700159..9feb4f8e74 100644
--- a/.github/workflows/codeql-analysis.yaml
+++ b/.github/workflows/codeql-analysis.yaml
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install go version
uses: actions/setup-go@v5
with:
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 43ccfed34f..f2e42b84e4 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -15,11 +15,11 @@ jobs:
name: Release Docs
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
+ - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: "3.12"
cache: "pip"
diff --git a/.github/workflows/json-yaml-validate.yml b/.github/workflows/json-yaml-validate.yml
index 356119146e..5f9a49c962 100644
--- a/.github/workflows/json-yaml-validate.yml
+++ b/.github/workflows/json-yaml-validate.yml
@@ -14,7 +14,7 @@ jobs:
json-yaml-validate:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: json-yaml-validate
uses: GrantBirki/json-yaml-validate@v3.2.1
diff --git a/.github/workflows/lint-test-chart.yaml b/.github/workflows/lint-test-chart.yaml
index 112e40640b..e1d503bce3 100644
--- a/.github/workflows/lint-test-chart.yaml
+++ b/.github/workflows/lint-test-chart.yaml
@@ -14,7 +14,7 @@ jobs:
shell: bash
steps:
- name: Checkout
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
@@ -59,7 +59,7 @@ jobs:
version: latest
- name: Install Python
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
+ uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
token: ${{ github.token }}
python-version: "3.x"
@@ -80,7 +80,7 @@ jobs:
- name: Create Kind cluster
if: steps.changes.outputs.changed == 'true'
- uses: helm/kind-action@0025e74a8c7512023d06dc019c617aa3cf561fde # v1.10.0
+ uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
with:
wait: 120s
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 9ef05fcab3..aed1b4e872 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -22,15 +22,21 @@ jobs:
steps:
- name: Check out code into the Go module directory
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Go 1.x
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- - name: Lint
+ - name: Lint go code
uses: golangci/golangci-lint-action@v6
with:
args: --timeout=30m
version: v1.60
+
+ # Run Spectral
+ - name: Lint OpenAPI spec
+ uses: stoplightio/spectral-action@2ad0b9302e32a77c1caccf474a9b2191a8060d83 # v0.8.11
+ with:
+ file_glob: 'api/*.yaml'
diff --git a/.github/workflows/release-chart.yaml b/.github/workflows/release-chart.yaml
index ec326758af..a92cf8400f 100644
--- a/.github/workflows/release-chart.yaml
+++ b/.github/workflows/release-chart.yaml
@@ -20,7 +20,7 @@ jobs:
shell: bash
steps:
- name: Checkout
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
diff --git a/.github/workflows/staging-image-tester.yaml b/.github/workflows/staging-image-tester.yaml
index a42c86e779..a6950195ca 100644
--- a/.github/workflows/staging-image-tester.yaml
+++ b/.github/workflows/staging-image-tester.yaml
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Check out code into the Go module directory
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Go 1.x
uses: actions/setup-go@v5
diff --git a/.ko.yaml b/.ko.yaml
index 41a1f0aec4..8e488dc270 100644
--- a/.ko.yaml
+++ b/.ko.yaml
@@ -1,4 +1,4 @@
-defaultBaseImage: gcr.io/distroless/static-debian11:latest
+defaultBaseImage: gcr.io/distroless/static-debian12:latest
builds:
- env:
- CGO_ENABLED=0
diff --git a/.spectral.yaml b/.spectral.yaml
new file mode 100644
index 0000000000..d47c47d315
--- /dev/null
+++ b/.spectral.yaml
@@ -0,0 +1 @@
+extends: ["spectral:oas"]
diff --git a/Makefile b/Makefile
index 5ffdfd0fc5..06e0557f35 100644
--- a/Makefile
+++ b/Makefile
@@ -60,9 +60,13 @@ licensecheck:
exit 1; \
fi
+# Requires to install spectral. See https://github.com/stoplightio/spectral
+oas-lint:
+ spectral lint api/*.yaml
+
# Run all the linters
.PHONY: lint
-lint: licensecheck go-lint
+lint: licensecheck go-lint oas-lint
# generates CRD using controller-gen
.PHONY: crd
@@ -156,3 +160,8 @@ release.prod: test
.PHONY: ko
ko:
scripts/install-ko.sh
+
+# generate-flags-documentation: Generate documentation (docs/flags.md)
+.PHONE: generate-flags-documentation
+generate-flags-documentation:
+ go run internal/gen/docs/flags/main.go
diff --git a/README.md b/README.md
index 282fa8edc3..041edab82a 100644
--- a/README.md
+++ b/README.md
@@ -74,22 +74,26 @@ See PR #3063 for all the discussions about it.
Known providers using webhooks:
-| Provider | Repo |
-| -------- | ----------- |
-| Adguard Home Provider | https://github.com/muhlba91/external-dns-provider-adguard |
-| Anexia | https://github.com/ProbstenHias/external-dns-anexia-webhook |
-| Bizfly Cloud | https://github.com/bizflycloud/external-dns-bizflycloud-webhook |
-| Efficient IP | https://github.com/EfficientIP-Labs/external-dns-efficientip-webhook |
-| Gcore | https://github.com/G-Core/external-dns-gcore-webhook |
-| 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 |
-| Netic | https://github.com/neticdk/external-dns-tidydns-webhook |
-| RouterOS | https://github.com/benfiola/external-dns-routeros-provider |
-| STACKIT | https://github.com/stackitcloud/external-dns-stackit-webhook |
-| Unifi | https://github.com/kashalls/external-dns-unifi-webhook |
+| Provider | Repo |
+|-----------------------|----------------------------------------------------------------------|
+| Abion | https://github.com/abiondevelopment/external-dns-webhook-abion |
+| Adguard Home Provider | https://github.com/muhlba91/external-dns-provider-adguard |
+| Anexia | https://github.com/ProbstenHias/external-dns-anexia-webhook |
+| Bizfly Cloud | https://github.com/bizflycloud/external-dns-bizflycloud-webhook |
+| Efficient IP | https://github.com/EfficientIP-Labs/external-dns-efficientip-webhook |
+| Gcore | https://github.com/G-Core/external-dns-gcore-webhook |
+| GleSYS | https://github.com/glesys/external-dns-glesys |
+| Hetzner | https://github.com/mconfalonieri/external-dns-hetzner-webhook |
+| Huawei Cloud | https://github.com/setoru/external-dns-huaweicloud-webhook |
+| IONOS | https://github.com/ionos-cloud/external-dns-ionos-webhook |
+| Infoblox | https://github.com/AbsaOSS/external-dns-infoblox-webhook |
+| Mikrotik | https://github.com/mirceanton/external-dns-provider-mikrotik |
+| Netcup | https://github.com/mrueg/external-dns-netcup-webhook |
+| Netic | https://github.com/neticdk/external-dns-tidydns-webhook |
+| RouterOS | https://github.com/benfiola/external-dns-routeros-provider |
+| STACKIT | https://github.com/stackitcloud/external-dns-stackit-webhook |
+| Unifi | https://github.com/kashalls/external-dns-unifi-webhook |
+| Vultr | https://github.com/vultr/external-dns-vultr-webhook |
## Status of in-tree providers
@@ -125,7 +129,6 @@ The following table clarifies the current status of the providers according to t
| RFC2136 | Alpha | |
| NS1 | Alpha | |
| TransIP | Alpha | |
-| RancherDNS | Alpha | |
| OVH | Alpha | |
| Scaleway DNS | Alpha | @Sh4d1 |
| UltraDNS | Alpha | |
@@ -183,10 +186,10 @@ The following tutorials are provided:
* [NS1](docs/tutorials/ns1.md)
* [NS Record Creation with CRD Source](docs/sources/ns-record.md)
* [MX Record Creation with CRD Source](docs/sources/mx-record.md)
+* [TXT Record Creation with CRD Source](docs/sources/txt-record.md)
* [OpenStack Designate](docs/tutorials/designate.md)
* [Oracle Cloud Infrastructure (OCI) DNS](docs/tutorials/oracle.md)
* [PowerDNS](docs/tutorials/pdns.md)
-* [RancherDNS (RDNS)](docs/tutorials/rdns.md)
* [RFC2136](docs/tutorials/rfc2136.md)
* [TransIP](docs/tutorials/transip.md)
* [OVH](docs/tutorials/ovh.md)
diff --git a/api/webhook.yaml b/api/webhook.yaml
new file mode 100644
index 0000000000..c0c4f16aa8
--- /dev/null
+++ b/api/webhook.yaml
@@ -0,0 +1,265 @@
+---
+openapi: "3.0.0"
+info:
+ version: v0.15.0
+ title: External DNS Webhook Server
+ description: >-
+ Implements the external DNS webhook endpoints.
+ contact:
+ url: https://github.com/kubernetes-sigs/external-dns
+ license:
+ name: Apache 2.0
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
+tags:
+ - name: initialization
+ description: Endpoints for initial negotiation.
+ - name: listing
+ description: Endpoints to get listings of DNS records.
+ - name: update
+ description: Endpoints to update DNS records.
+servers:
+ - url: http://localhost:8888
+ description: Server url for a Kubernetes deployment.
+paths:
+ /:
+ get:
+ summary: >-
+ Initialisation and negotiates headers and returns domain
+ filter.
+ description: |
+ Initialisation and negotiates headers and returns domain
+ filter.
+ operationId: negotiate
+ tags: [initialization]
+ responses:
+ '200':
+ description: |
+ The list of domains this DNS provider serves.
+ content:
+ application/external.dns.webhook+json;version=1:
+ schema:
+ $ref: '#/components/schemas/filters'
+ example:
+ filters:
+ - example.com
+ '500':
+ description: |
+ Negociation failed.
+
+ /records:
+ get:
+ summary: Returns the current records.
+ description: |
+ Get the current records from the DNS provider and return them.
+ operationId: getRecords
+ tags: [listing]
+ responses:
+ '200':
+ description: |
+ Provided the list of DNS records successfully.
+ content:
+ application/external.dns.webhook+json;version=1:
+ schema:
+ $ref: '#/components/schemas/endpoints'
+ example:
+ - dnsName: "test.example.com"
+ recordTTL: 10
+ recordType: 'A'
+ targets:
+ - "1.2.3.4"
+ '500':
+ description: |
+ Failed to provide the list of DNS records.
+
+ post:
+ summary: Applies the changes.
+ description: |
+ Set the records in the DNS provider based on those supplied here.
+ operationId: setRecords
+ tags: [update]
+ requestBody:
+ description: |
+ This is the list of changes that need to be applied. There are
+ four lists of endpoints. The `create` and `delete` lists are lists
+ of records to create and delete respectively. The `updateOld` and
+ `updateNew` lists are paired. For each entry there's the old version
+ of the record and a new version of the record.
+ required: true
+ content:
+ application/external.dns.webhook+json;version=1:
+ schema:
+ $ref: '#/components/schemas/changes'
+ example:
+ create:
+ - dnsName: "test.example.com"
+ recordTTL: 10
+ recordType: 'A'
+ responses:
+ '204':
+ description: |
+ Changes were accepted.
+ '500':
+ description: |
+ Changes were not accepted.
+
+ /adjustendpoints:
+ post:
+ summary: Executes the AdjustEndpoints method.
+ description: |
+ Adjusts the records in the provider based on those supplied here.
+ operationId: adjustRecords
+ tags: [update]
+ requestBody:
+ description: |
+ This is the list of changes to be applied.
+ required: true
+ content:
+ application/external.dns.webhook+json;version=1:
+ schema:
+ $ref: '#/components/schemas/endpoints'
+ example:
+ - dnsName: "test.example.com"
+ recordTTL: 10
+ recordType: 'A'
+ targets:
+ - "1.2.3.4"
+ responses:
+ '200':
+ description: |
+ Adjustments were accepted.
+ content:
+ application/external.dns.webhook+json;version=1:
+ schema:
+ $ref: '#/components/schemas/endpoints'
+ example:
+ - dnsName: "test.example.com"
+ recordTTL: 0
+ recordType: 'A'
+ targets:
+ - "1.2.3.4"
+ '500':
+ description: |
+ Adjustments were not accepted.
+
+components:
+ schemas:
+ filters:
+ description: |
+ external-dns will only create DNS records for host names (specified in ingress objects and services with the external-dns annotation) related to zones that match filters. They can set in external-dns deployment manifest.
+ type: object
+ properties:
+ filters:
+ type: array
+ items:
+ type: string
+ example: "foo.example.com"
+ example:
+ - ".example.com"
+ example:
+ filters:
+ - ".example.com"
+ - ".example.org"
+
+ endpoints:
+ description: |
+ This is a list of DNS records.
+ type: array
+ items:
+ $ref: '#/components/schemas/endpoint'
+ example:
+ - dnsName: foo.example.com
+ recordType: A
+ recordTTL: 60
+
+ endpoint:
+ description: |
+ This is a DNS record.
+ type: object
+ properties:
+ dnsName:
+ type: string
+ example: "foo.example.org"
+ targets:
+ $ref: '#/components/schemas/targets'
+ recordType:
+ type: string
+ example: "CNAME"
+ setIdentifier:
+ type: string
+ example: "v1"
+ recordTTL:
+ type: integer
+ format: int64
+ example: 60
+ labels:
+ type: object
+ additionalProperties:
+ type: string
+ example: "foo"
+ example:
+ foo: bar
+ providerSpecific:
+ type: array
+ items:
+ $ref: '#/components/schemas/providerSpecificProperty'
+ example:
+ - name: foo
+ value: bar
+ example:
+ dnsName: foo.example.com
+ recordType: A
+ recordTTL: 60
+
+ targets:
+ description: |
+ This is the list of targets that this DNS record points to.
+ So for an A record it will be a list of IP addresses.
+ type: array
+ items:
+ type: string
+ example: "::1"
+ example:
+ - "1.2.3.4"
+ - "test.example.org"
+
+ providerSpecificProperty:
+ description: |
+ Allows provider to pass property specific to their implementation.
+ type: object
+ properties:
+ name:
+ type: string
+ example: foo
+ value:
+ type: string
+ example: bar
+ example:
+ name: foo
+ value: bar
+
+ changes:
+ description: |
+ This is the list of changes send by `external-dns` that need to
+ be applied. There are four lists of endpoints. The `create`
+ and `delete` lists are lists of records to create and delete
+ respectively. The `updateOld` and `updateNew` lists are paired.
+ For each entry there's the old version of the record and a new
+ version of the record.
+ type: object
+ properties:
+ create:
+ $ref: '#/components/schemas/endpoints'
+ updateOld:
+ $ref: '#/components/schemas/endpoints'
+ updateNew:
+ $ref: '#/components/schemas/endpoints'
+ delete:
+ $ref: '#/components/schemas/endpoints'
+ example:
+ create:
+ - dnsName: foo.example.com
+ recordType: A
+ recordTTL: 60
+ delete:
+ - dnsName: foo.example.org
+ recordType: CNAME
diff --git a/charts/external-dns/CHANGELOG.md b/charts/external-dns/CHANGELOG.md
index 02b467e1d6..3569fa6869 100644
--- a/charts/external-dns/CHANGELOG.md
+++ b/charts/external-dns/CHANGELOG.md
@@ -18,6 +18,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [UNRELEASED]
+### Added
+
+- Ability to configure `imagePullSecrets` via helm `global` value ([#4667](https://github.com/kubernetes-sigs/external-dns/pull/4667)) _@jkroepke_
+- Added options to configure `labelFilter` and `managedRecordTypes` via dedicated helm values ([#4849](https://github.com/kubernetes-sigs/external-dns/pull/4849)) _@abaguas_
+
+### Fixed
+
+- Fixed automatic addition of pod selector labels to `affinity` and `topologySpreadConstraints` if not defined. _@pvickery-ParamountCommerce_
+
## [v1.15.0] - 2023-09-10
### Changed
diff --git a/charts/external-dns/README.md b/charts/external-dns/README.md
index 9b21ecdec6..d88545c383 100644
--- a/charts/external-dns/README.md
+++ b/charts/external-dns/README.md
@@ -97,23 +97,26 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
| deploymentStrategy | object | `{"type":"Recreate"}` | [Deployment Strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). |
| dnsConfig | object | `nil` | [DNS config](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-dns-config) for the pod, if not set the default will be used. |
| dnsPolicy | string | `nil` | [DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) for the pod, if not set the default will be used. |
-| domainFilters | list | `[]` | |
+| domainFilters | list | `[]` | Limit possible target zones by domain suffixes. |
| env | list | `[]` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `external-dns` container. |
-| excludeDomains | list | `[]` | |
+| excludeDomains | list | `[]` | Intentionally exclude domains from being managed. |
| extraArgs | list | `[]` | Extra arguments to provide to _ExternalDNS_. |
| extraContainers | object | `{}` | Extra containers to add to the `Deployment`. |
| extraVolumeMounts | list | `[]` | Extra [volume mounts](https://kubernetes.io/docs/concepts/storage/volumes/) for the `external-dns` container. |
| extraVolumes | list | `[]` | Extra [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) for the `Pod`. |
| fullnameOverride | string | `nil` | Override the full name of the chart. |
+| global.imagePullSecrets | list | `[]` | Global image pull secrets. |
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy for the `external-dns` container. |
| image.repository | string | `"registry.k8s.io/external-dns/external-dns"` | Image repository for the `external-dns` container. |
| image.tag | string | `nil` | Image tag for the `external-dns` container, this will default to `.Chart.AppVersion` if not set. |
| imagePullSecrets | list | `[]` | Image pull secrets. |
| initContainers | list | `[]` | [Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) to add to the `Pod` definition. |
| interval | string | `"1m"` | Interval for DNS updates. |
+| labelFilter | string | `nil` | Filter resources queried for endpoints by label selector |
| livenessProbe | object | See _values.yaml_ | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `external-dns` container. |
| logFormat | string | `"text"` | Log format. |
| logLevel | string | `"info"` | Log level. |
+| managedRecordTypes | list | `[]` | Record types to manage (default: A, AAAA, CNAME) |
| nameOverride | string | `nil` | Override the name of the chart. |
| namespaced | bool | `false` | if `true`, _ExternalDNS_ will run in a namespaced scope (`Role`` and `Rolebinding`` will be namespaced too). |
| nodeSelector | object | `{}` | Node labels to match for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). |
diff --git a/charts/external-dns/ci/ci-values.yaml b/charts/external-dns/ci/ci-values.yaml
index 4d278e94be..e9b1db41cb 100644
--- a/charts/external-dns/ci/ci-values.yaml
+++ b/charts/external-dns/ci/ci-values.yaml
@@ -1,2 +1,36 @@
+labelFilter: foo=bar
+managedRecordTypes: []
provider:
name: inmemory
+affinity:
+ podAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 100
+ podAffinityTerm:
+ topologyKey: "kubernetes.io/hostname"
+ labelSelector:
+ matchExpressions:
+ - key: app.kubernetes.io/name
+ operator: In
+ values:
+ - test
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - topologyKey: "kubernetes.io/hostname"
+ podAntiAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - weight: 100
+ podAffinityTerm:
+ topologyKey: "kubernetes.io/hostname"
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - topologyKey: "kubernetes.io/hostname"
+ labelSelector:
+ matchExpressions:
+ - key: app.kubernetes.io/name
+ operator: In
+ values:
+ - test
+
+topologySpreadConstraints:
+ - maxSkew: 1
+ topologyKey: "topology.kubernetes.io/zone"
+ whenUnsatisfiable: "ScheduleAnyway"
diff --git a/charts/external-dns/templates/_helpers.tpl b/charts/external-dns/templates/_helpers.tpl
index 3ce55cd8a3..15304fbcbf 100644
--- a/charts/external-dns/templates/_helpers.tpl
+++ b/charts/external-dns/templates/_helpers.tpl
@@ -93,3 +93,12 @@ The image to use for optional webhook sidecar
{{- printf "%s:%s" .repository .tag }}
{{- end }}
{{- end }}
+
+{{/*
+The pod affinity default label Selector
+*/}}
+{{- define "external-dns.labelSelector" -}}
+labelSelector:
+ matchLabels:
+ {{ include "external-dns.selectorLabels" . | nindent 4 }}
+{{- end }}
diff --git a/charts/external-dns/templates/deployment.yaml b/charts/external-dns/templates/deployment.yaml
index 02e9b397ad..46a41de4fa 100644
--- a/charts/external-dns/templates/deployment.yaml
+++ b/charts/external-dns/templates/deployment.yaml
@@ -1,3 +1,4 @@
+{{- $defaultSelector := (include "external-dns.labelSelector" $ ) | fromYaml -}}
{{- $providerName := tpl (include "external-dns.providerName" .) $ }}
apiVersion: apps/v1
kind: Deployment
@@ -40,7 +41,7 @@ spec:
{{- if not (quote .Values.automountServiceAccountToken | empty) }}
automountServiceAccountToken: {{ .Values.automountServiceAccountToken }}
{{- end }}
- {{- with .Values.imagePullSecrets }}
+ {{- with (default .Values.global.imagePullSecrets .Values.imagePullSecrets) }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
@@ -114,6 +115,12 @@ spec:
{{- range .Values.excludeDomains }}
- --exclude-domains={{ . }}
{{- end }}
+ {{- if .Values.labelFilter }}
+ - --label-filter={{ .Values.labelFilter }}
+ {{- end }}
+ {{- range .Values.managedRecordTypes }}
+ - --managed-record-types={{ . }}
+ {{- end }}
- --provider={{ $providerName }}
{{- range .Values.extraArgs }}
- {{ tpl . $ }}
@@ -197,11 +204,67 @@ spec:
{{- end }}
{{- with .Values.affinity }}
affinity:
- {{- toYaml . | nindent 8 }}
+ {{- with .podAffinity }}
+ podAffinity:
+ {{- with .preferredDuringSchedulingIgnoredDuringExecution }}
+ preferredDuringSchedulingIgnoredDuringExecution:
+ {{- range . }}
+ - podAffinityTerm:
+ {{- if dig "podAffinityTerm" "labelSelector" nil . }}
+ {{- toYaml .podAffinityTerm | nindent 16 }}
+ {{- else }}
+ {{- (merge $defaultSelector .podAffinityTerm) | toYaml | nindent 16 }}
+ {{- end }}
+ weight: {{ .weight }}
+ {{- end }}
+ {{- end }}
+ {{- with .requiredDuringSchedulingIgnoredDuringExecution }}
+ requiredDuringSchedulingIgnoredDuringExecution:
+ {{- range . }}
+ {{- if dig "labelSelector" nil . }}
+ - {{ toYaml . | indent 16 | trim }}
+ {{- else }}
+ - {{ (merge $defaultSelector .) | toYaml | indent 16 | trim }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ {{- with .podAntiAffinity }}
+ podAntiAffinity:
+ {{- with .preferredDuringSchedulingIgnoredDuringExecution }}
+ preferredDuringSchedulingIgnoredDuringExecution:
+ {{- range . }}
+ - podAffinityTerm:
+ {{- if dig "podAffinityTerm" "labelSelector" nil . }}
+ {{- toYaml .podAffinityTerm | nindent 16 }}
+ {{- else }}
+ {{- (merge $defaultSelector .podAffinityTerm) | toYaml | nindent 16 }}
+ {{- end }}
+ weight: {{ .weight }}
+ {{- end }}
+ {{- end }}
+ {{- with .requiredDuringSchedulingIgnoredDuringExecution }}
+ requiredDuringSchedulingIgnoredDuringExecution:
+ {{- range . }}
+ {{- if dig "labelSelector" nil . }}
+ - {{ toYaml . | indent 16 | trim }}
+ {{- else }}
+ - {{ (merge $defaultSelector .) | toYaml | indent 16 | trim }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
{{- end }}
{{- with .Values.topologySpreadConstraints }}
topologySpreadConstraints:
- {{- toYaml . | nindent 8 }}
+ {{- range . }}
+ - {{ toYaml . | nindent 10 | trim }}
+ {{- if not (hasKey . "labelSelector") }}
+ labelSelector:
+ matchLabels:
+ {{- include "external-dns.selectorLabels" $ | nindent 12 }}
+ {{- end }}
+ {{- end }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
diff --git a/charts/external-dns/values.schema.json b/charts/external-dns/values.schema.json
index 614deeaca3..80378c7247 100644
--- a/charts/external-dns/values.schema.json
+++ b/charts/external-dns/values.schema.json
@@ -2,6 +2,9 @@
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"properties": {
+ "global": {
+ "type": "object"
+ },
"provider": {
"anyOf": [
{
diff --git a/charts/external-dns/values.yaml b/charts/external-dns/values.yaml
index 9d7dea1bb9..dc12cdd65b 100644
--- a/charts/external-dns/values.yaml
+++ b/charts/external-dns/values.yaml
@@ -2,6 +2,10 @@
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
+global:
+ # -- Global image pull secrets.
+ imagePullSecrets: []
+
image:
# -- Image repository for the `external-dns` container.
repository: registry.k8s.io/external-dns/external-dns
@@ -219,12 +223,18 @@ txtPrefix:
# Mutually exclusive with `txtPrefix`.
txtSuffix:
-## - Limit possible target zones by domain suffixes.
+# -- Limit possible target zones by domain suffixes.
domainFilters: []
-## -- Intentionally exclude domains from being managed.
+# -- Intentionally exclude domains from being managed.
excludeDomains: []
+# -- (string) Filter resources queried for endpoints by label selector
+labelFilter:
+
+# -- Record types to manage (default: A, AAAA, CNAME)
+managedRecordTypes: []
+
provider:
# -- _ExternalDNS_ provider name; for the available providers and how to configure them see [README](https://github.com/kubernetes-sigs/external-dns/blob/master/charts/external-dns/README.md#providers).
name: aws
diff --git a/docs/faq.md b/docs/faq.md
index d8666f363e..6998d26809 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -221,7 +221,7 @@ $ docker run \
-e EXTERNAL_DNS_SOURCE=$'service\ningress' \
-e EXTERNAL_DNS_PROVIDER=google \
-e EXTERNAL_DNS_DOMAIN_FILTER=$'foo.com\nbar.com' \
- registry.k8s.io/external-dns/external-dns:v0.15.0
+ registry.k8s.io/external-dns/external-dns:v0.15.1
time="2017-08-08T14:10:26Z" level=info msg="config: &{APIServerURL: KubeConfig: Sources:[service ingress] Namespace: ...
```
diff --git a/docs/flags.md b/docs/flags.md
new file mode 100644
index 0000000000..bf98d3f517
--- /dev/null
+++ b/docs/flags.md
@@ -0,0 +1,169 @@
+# Flags
+
+
+
+
+| Flag | Description |
+| :------ | :----------- |
+| `--[no-]version` | Show application version. |
+| `--server=""` | The Kubernetes API server to connect to (default: auto-detect) |
+| `--kubeconfig=""` | Retrieve target cluster configuration from a Kubernetes configuration file (default: auto-detect) |
+| `--request-timeout=30s` | Request timeout when calling Kubernetes APIs. 0s means no timeout |
+| `--[no-]resolve-service-load-balancer-hostname` | Resolve the hostname of LoadBalancer-type Service object to IP addresses in order to create DNS A/AAAA records instead of CNAMEs |
+| `--cf-api-endpoint=""` | The fully-qualified domain name of the cloud foundry instance you are targeting |
+| `--cf-username=""` | The username to log into the cloud foundry API |
+| `--cf-password=""` | The password to log into the cloud foundry API |
+| `--gloo-namespace=gloo-system` | The Gloo Proxy namespace; specify multiple times for multiple namespaces. (default: gloo-system) |
+| `--skipper-routegroup-groupversion="zalando.org/v1"` | The resource version for skipper routegroup |
+| `--source=source` | The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, pod, fake, connector, gateway-httproute, gateway-grpcroute, gateway-tlsroute, gateway-tcproute, gateway-udproute, istio-gateway, istio-virtualservice, cloudfoundry, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress, f5-virtualserver, traefik-proxy) |
+| `--openshift-router-name=OPENSHIFT-ROUTER-NAME` | if source is openshift-route then you can pass the ingress controller name. Based on this name external-dns will select the respective router from the route status and map that routerCanonicalHostname to the route host while creating a CNAME record. |
+| `--namespace=""` | Limit resources queried for endpoints to a specific namespace (default: all namespaces) |
+| `--annotation-filter=""` | Filter resources queried for endpoints by annotation, using label selector semantics |
+| `--label-filter=""` | Filter resources queried for endpoints by label selector; currently supported by source types crd, gateway-httproute, gateway-grpcroute, gateway-tlsroute, gateway-tcproute, gateway-udproute, ingress, node, openshift-route, service and ambassador-host |
+| `--ingress-class=INGRESS-CLASS` | Require an Ingress to have this class name (defaults to any class; specify multiple times to allow more than one class) |
+| `--fqdn-template=""` | A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN. |
+| `--[no-]combine-fqdn-annotation` | Combine FQDN template and Annotations instead of overwriting |
+| `--[no-]ignore-hostname-annotation` | Ignore hostname annotation when generating DNS names, valid only when --fqdn-template is set (default: false) |
+| `--[no-]ignore-ingress-tls-spec` | Ignore the spec.tls section in Ingress resources (default: false) |
+| `--gateway-namespace=GATEWAY-NAMESPACE` | Limit Gateways of Route endpoints to a specific namespace (default: all namespaces) |
+| `--gateway-label-filter=GATEWAY-LABEL-FILTER` | Filter Gateways of Route endpoints via label selector (default: all gateways) |
+| `--compatibility=` | Process annotation semantics from legacy implementations (optional, options: mate, molecule, kops-dns-controller) |
+| `--[no-]ignore-ingress-rules-spec` | Ignore the spec.rules section in Ingress resources (default: false) |
+| `--[no-]publish-internal-services` | Allow external-dns to publish DNS records for ClusterIP services (optional) |
+| `--[no-]publish-host-ip` | Allow external-dns to publish host-ip for headless services (optional) |
+| `--[no-]always-publish-not-ready-addresses` | Always publish also not ready addresses for headless services (optional) |
+| `--connector-source-server="localhost:8080"` | The server to connect for connector source, valid only when using connector source |
+| `--crd-source-apiversion="externaldns.k8s.io/v1alpha1"` | API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source |
+| `--crd-source-kind="DNSEndpoint"` | Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion |
+| `--service-type-filter=SERVICE-TYPE-FILTER` | The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName) |
+| `--managed-record-types=A...` | Record types to manage; specify multiple times to include many; (default: A, AAAA, CNAME) (supported records: A, AAAA, CNAME, NS, SRV, TXT) |
+| `--exclude-record-types=EXCLUDE-RECORD-TYPES` | Record types to exclude from management; specify multiple times to exclude many; (optional) |
+| `--default-targets=DEFAULT-TARGETS` | Set globally default host/IP that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional) |
+| `--target-net-filter=TARGET-NET-FILTER` | Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional) |
+| `--exclude-target-net=EXCLUDE-TARGET-NET` | Exclude target nets (optional) |
+| `--[no-]traefik-disable-legacy` | Disable listeners on Resources under the traefik.containo.us API Group |
+| `--[no-]traefik-disable-new` | Disable listeners on Resources under the traefik.io API Group |
+| `--nat64-networks=NAT64-NETWORKS` | Adding an A record for each AAAA record in NAT64-enabled networks; specify multiple times for multiple possible nets (optional) |
+| `--provider=provider` | The DNS provider where the DNS records will be created (required, options: akamai, alibabacloud, aws, aws-sd, azure, azure-dns, azure-private-dns, civo, cloudflare, coredns, designate, digitalocean, dnsimple, exoscale, gandi, godaddy, google, ibmcloud, inmemory, linode, ns1, oci, ovh, pdns, pihole, plural, rfc2136, scaleway, skydns, tencentcloud, transip, ultradns, webhook) |
+| `--provider-cache-time=0s` | The time to cache the DNS provider record list requests. |
+| `--domain-filter=` | Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional) |
+| `--exclude-domains=` | Exclude subdomains (optional) |
+| `--regex-domain-filter=` | Limit possible domains and target zones by a Regex filter; Overrides domain-filter (optional) |
+| `--regex-domain-exclusion=` | Regex filter that excludes domains and target zones matched by regex-domain-filter (optional) |
+| `--zone-name-filter=` | Filter target zones by zone domain (For now, only AzureDNS provider is using this flag); specify multiple times for multiple zones (optional) |
+| `--zone-id-filter=` | Filter target zones by hosted zone id; specify multiple times for multiple zones (optional) |
+| `--google-project=""` | When using the Google provider, current project is auto-detected, when running on GCP. Specify other project with this. Must be specified when running outside GCP. |
+| `--google-batch-change-size=1000` | When using the Google provider, set the maximum number of changes that will be applied in each batch. |
+| `--google-batch-change-interval=1s` | When using the Google provider, set the interval between batch changes. |
+| `--google-zone-visibility=` | When using the Google provider, filter for zones with this visibility (optional, options: public, private) |
+| `--alibaba-cloud-config-file="/etc/kubernetes/alibaba-cloud.json"` | When using the Alibaba Cloud provider, specify the Alibaba Cloud configuration file (required when --provider=alibabacloud) |
+| `--alibaba-cloud-zone-type=` | When using the Alibaba Cloud provider, filter for zones of this type (optional, options: public, private) |
+| `--aws-zone-type=` | When using the AWS provider, filter for zones of this type (optional, options: public, private) |
+| `--aws-zone-tags=` | When using the AWS provider, filter for zones with these tags |
+| `--aws-profile=` | When using the AWS provider, name of the profile to use |
+| `--aws-assume-role=""` | When using the AWS API, assume this IAM role. Useful for hosted zones in another AWS account. Specify the full ARN, e.g. `arn:aws:iam::123455567:role/external-dns` (optional) |
+| `--aws-assume-role-external-id=""` | When using the AWS API and assuming a role then specify this external ID` (optional) |
+| `--aws-batch-change-size=1000` | When using the AWS provider, set the maximum number of changes that will be applied in each batch. |
+| `--aws-batch-change-size-bytes=32000` | When using the AWS provider, set the maximum byte size that will be applied in each batch. |
+| `--aws-batch-change-size-values=1000` | When using the AWS provider, set the maximum total record values that will be applied in each batch. |
+| `--aws-batch-change-interval=1s` | When using the AWS provider, set the interval between batch changes. |
+| `--[no-]aws-evaluate-target-health` | When using the AWS provider, set whether to evaluate the health of a DNS target (default: enabled, disable with --no-aws-evaluate-target-health) |
+| `--aws-api-retries=3` | When using the AWS API, set the maximum number of retries before giving up. |
+| `--[no-]aws-prefer-cname` | When using the AWS provider, prefer using CNAME instead of ALIAS (default: disabled) |
+| `--aws-zones-cache-duration=0s` | When using the AWS provider, set the zones list cache TTL (0s to disable). |
+| `--[no-]aws-zone-match-parent` | Expand limit possible target by sub-domains (default: disabled) |
+| `--[no-]aws-sd-service-cleanup` | When using the AWS CloudMap provider, delete empty Services without endpoints (default: disabled) |
+| `--aws-sd-create-tag=AWS-SD-CREATE-TAG` | When using the AWS CloudMap provider, add tag to created services. The flag can be used multiple times |
+| `--azure-config-file="/etc/kubernetes/azure.json"` | When using the Azure provider, specify the Azure configuration file (required when --provider=azure) |
+| `--azure-resource-group=""` | When using the Azure provider, override the Azure resource group to use (optional) |
+| `--azure-subscription-id=""` | When using the Azure provider, override the Azure subscription to use (optional) |
+| `--azure-user-assigned-identity-client-id=""` | When using the Azure provider, override the client id of user assigned identity in config file (optional) |
+| `--azure-zones-cache-duration=0s` | When using the Azure provider, set the zones list cache TTL (0s to disable). |
+| `--tencent-cloud-config-file="/etc/kubernetes/tencent-cloud.json"` | When using the Tencent Cloud provider, specify the Tencent Cloud configuration file (required when --provider=tencentcloud) |
+| `--tencent-cloud-zone-type=` | When using the Tencent Cloud provider, filter for zones with visibility (optional, options: public, private) |
+| `--[no-]cloudflare-proxied` | When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled) |
+| `--cloudflare-dns-records-per-page=100` | When using the Cloudflare provider, specify how many DNS records listed per page, max possible 5,000 (default: 100) |
+| `--cloudflare-region-key=CLOUDFLARE-REGION-KEY` | When using the Cloudflare provider, specify the region (default: earth) |
+| `--coredns-prefix="/skydns/"` | When using the CoreDNS provider, specify the prefix name |
+| `--akamai-serviceconsumerdomain=""` | When using the Akamai provider, specify the base URL (required when --provider=akamai and edgerc-path not specified) |
+| `--akamai-client-token=""` | When using the Akamai provider, specify the client token (required when --provider=akamai and edgerc-path not specified) |
+| `--akamai-client-secret=""` | When using the Akamai provider, specify the client secret (required when --provider=akamai and edgerc-path not specified) |
+| `--akamai-access-token=""` | When using the Akamai provider, specify the access token (required when --provider=akamai and edgerc-path not specified) |
+| `--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) |
+| `--akamai-edgerc-section=""` | When using the Akamai provider, specify the .edgerc file path (Optional when edgerc-path is specified) |
+| `--oci-config-file="/etc/kubernetes/oci.yaml"` | When using the OCI provider, specify the OCI configuration file (required when --provider=oci |
+| `--oci-compartment-ocid=OCI-COMPARTMENT-OCID` | When using the OCI provider, specify the OCID of the OCI compartment containing all managed zones and records. Required when using OCI IAM instance principal authentication. |
+| `--oci-zone-scope=GLOBAL` | When using OCI provider, filter for zones with this scope (optional, options: GLOBAL, PRIVATE). Defaults to GLOBAL, setting to empty value will target both. |
+| `--[no-]oci-auth-instance-principal` | When using the OCI provider, specify whether OCI IAM instance principal authentication should be used (instead of key-based auth via the OCI config file). |
+| `--oci-zones-cache-duration=0s` | When using the OCI provider, set the zones list cache TTL (0s to disable). |
+| `--inmemory-zone=` | Provide a list of pre-configured zones for the inmemory provider; specify multiple times for multiple zones (optional) |
+| `--ovh-endpoint="ovh-eu"` | When using the OVH provider, specify the endpoint (default: ovh-eu) |
+| `--ovh-api-rate-limit=20` | When using the OVH provider, specify the API request rate limit, X operations by seconds (default: 20) |
+| `--pdns-server="http://localhost:8081"` | When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns) |
+| `--pdns-server-id="localhost"` | When using the PowerDNS/PDNS provider, specify the id of the server to retrieve. Should be `localhost` except when the server is behind a proxy (optional when --provider=pdns) (default: localhost) |
+| `--pdns-api-key=""` | When using the PowerDNS/PDNS provider, specify the API key to use to authorize requests (required when --provider=pdns) |
+| `--[no-]pdns-skip-tls-verify` | When using the PowerDNS/PDNS provider, disable verification of any TLS certificates (optional when --provider=pdns) (default: false) |
+| `--ns1-endpoint=""` | When using the NS1 provider, specify the URL of the API endpoint to target (default: https://api.nsone.net/v1/) |
+| `--[no-]ns1-ignoressl` | When using the NS1 provider, specify whether to verify the SSL certificate (default: false) |
+| `--ns1-min-ttl=NS1-MIN-TTL` | Minimal TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is lower than this. |
+| `--digitalocean-api-page-size=50` | Configure the page size used when querying the DigitalOcean API. |
+| `--ibmcloud-config-file="/etc/kubernetes/ibmcloud.json"` | When using the IBM Cloud provider, specify the IBM Cloud configuration file (required when --provider=ibmcloud |
+| `--[no-]ibmcloud-proxied` | When using the IBM provider, specify if the proxy mode must be enabled (default: disabled) |
+| `--godaddy-api-key=""` | When using the GoDaddy provider, specify the API Key (required when --provider=godaddy) |
+| `--godaddy-api-secret=""` | When using the GoDaddy provider, specify the API secret (required when --provider=godaddy) |
+| `--godaddy-api-ttl=GODADDY-API-TTL` | TTL (in seconds) for records. This value will be used if the provided TTL for a service/ingress is not provided. |
+| `--[no-]godaddy-api-ote` | When using the GoDaddy provider, use OTE api (optional, default: false, when --provider=godaddy) |
+| `--tls-ca=""` | When using TLS communication, the path to the certificate authority to verify server communications (optionally specify --tls-client-cert for two-way TLS) |
+| `--tls-client-cert=""` | When using TLS communication, the path to the certificate to present as a client (not required for TLS) |
+| `--tls-client-cert-key=""` | When using TLS communication, the path to the certificate key to use with the client certificate (not required for TLS) |
+| `--exoscale-apienv="api"` | When using Exoscale provider, specify the API environment (optional) |
+| `--exoscale-apizone="ch-gva-2"` | When using Exoscale provider, specify the API Zone (optional) |
+| `--exoscale-apikey=""` | Provide your API Key for the Exoscale provider |
+| `--exoscale-apisecret=""` | Provide your API Secret for the Exoscale provider |
+| `--rfc2136-host=""` | When using the RFC2136 provider, specify the host of the DNS server |
+| `--rfc2136-port=0` | When using the RFC2136 provider, specify the port of the DNS server |
+| `--rfc2136-zone=RFC2136-ZONE` | When using the RFC2136 provider, specify zone entries of the DNS server to use |
+| `--[no-]rfc2136-create-ptr` | When using the RFC2136 provider, enable PTR management |
+| `--[no-]rfc2136-insecure` | When using the RFC2136 provider, specify whether to attach TSIG or not (default: false, requires --rfc2136-tsig-keyname and rfc2136-tsig-secret) |
+| `--rfc2136-tsig-keyname=""` | When using the RFC2136 provider, specify the TSIG key to attached to DNS messages (required when --rfc2136-insecure=false) |
+| `--rfc2136-tsig-secret=""` | When using the RFC2136 provider, specify the TSIG (base64) value to attached to DNS messages (required when --rfc2136-insecure=false) |
+| `--rfc2136-tsig-secret-alg=""` | When using the RFC2136 provider, specify the TSIG (base64) value to attached to DNS messages (required when --rfc2136-insecure=false) |
+| `--[no-]rfc2136-tsig-axfr` | When using the RFC2136 provider, specify the TSIG (base64) value to attached to DNS messages (required when --rfc2136-insecure=false) |
+| `--rfc2136-min-ttl=0s` | When using the RFC2136 provider, specify minimal TTL (in duration format) for records. This value will be used if the provided TTL for a service/ingress is lower than this |
+| `--[no-]rfc2136-gss-tsig` | When using the RFC2136 provider, specify whether to use secure updates with GSS-TSIG using Kerberos (default: false, requires --rfc2136-kerberos-realm, --rfc2136-kerberos-username, and rfc2136-kerberos-password) |
+| `--rfc2136-kerberos-username=""` | When using the RFC2136 provider with GSS-TSIG, specify the username of the user with permissions to update DNS records (required when --rfc2136-gss-tsig=true) |
+| `--rfc2136-kerberos-password=""` | When using the RFC2136 provider with GSS-TSIG, specify the password of the user with permissions to update DNS records (required when --rfc2136-gss-tsig=true) |
+| `--rfc2136-kerberos-realm=""` | When using the RFC2136 provider with GSS-TSIG, specify the realm of the user with permissions to update DNS records (required when --rfc2136-gss-tsig=true) |
+| `--rfc2136-batch-change-size=50` | When using the RFC2136 provider, set the maximum number of changes that will be applied in each batch. |
+| `--[no-]rfc2136-use-tls` | When using the RFC2136 provider, communicate with name server over tls |
+| `--[no-]rfc2136-skip-tls-verify` | When using TLS with the RFC2136 provider, disable verification of any TLS certificates |
+| `--transip-account=""` | When using the TransIP provider, specify the account name (required when --provider=transip) |
+| `--transip-keyfile=""` | When using the TransIP provider, specify the path to the private key file (required when --provider=transip) |
+| `--pihole-server=""` | When using the Pihole provider, the base URL of the Pihole web server (required when --provider=pihole) |
+| `--pihole-password=""` | When using the Pihole provider, the password to the server if it is protected |
+| `--[no-]pihole-tls-skip-verify` | When using the Pihole provider, disable verification of any TLS certificates |
+| `--plural-cluster=""` | When using the plural provider, specify the cluster name you're running with |
+| `--plural-provider=""` | When using the plural provider, specify the provider name you're running with |
+| `--policy=sync` | Modify how DNS records are synchronized between sources and providers (default: sync, options: sync, upsert-only, create-only) |
+| `--registry=txt` | The registry implementation to use to keep track of DNS record ownership (default: txt, options: txt, noop, dynamodb, aws-sd) |
+| `--txt-owner-id="default"` | When using the TXT or DynamoDB registry, a name that identifies this instance of ExternalDNS (default: default) |
+| `--txt-prefix=""` | When using the TXT registry, a custom string that's prefixed to each ownership DNS record (optional). Could contain record type template like '%{record_type}-prefix-'. Mutual exclusive with txt-suffix! |
+| `--txt-suffix=""` | When using the TXT registry, a custom string that's suffixed to the host portion of each ownership DNS record (optional). Could contain record type template like '-%{record_type}-suffix'. Mutual exclusive with txt-prefix! |
+| `--txt-wildcard-replacement=""` | When using the TXT registry, a custom string that's used instead of an asterisk for TXT records corresponding to wildcard DNS records (optional) |
+| `--[no-]txt-encrypt-enabled` | When using the TXT registry, set if TXT records should be encrypted before stored (default: disabled) |
+| `--txt-encrypt-aes-key=""` | When using the TXT registry, set TXT record decryption and encryption 32 byte aes key (required when --txt-encrypt=true) |
+| `--dynamodb-region=""` | When using the DynamoDB registry, the AWS region of the DynamoDB table (optional) |
+| `--dynamodb-table="external-dns"` | When using the DynamoDB registry, the name of the DynamoDB table (default: "external-dns") |
+| `--txt-cache-interval=0s` | The interval between cache synchronizations in duration format (default: disabled) |
+| `--interval=1m0s` | The interval between two consecutive synchronizations in duration format (default: 1m) |
+| `--min-event-sync-interval=5s` | The minimum interval between two consecutive synchronizations triggered from kubernetes events in duration format (default: 5s) |
+| `--[no-]once` | When enabled, exits the synchronization loop after the first iteration (default: disabled) |
+| `--[no-]dry-run` | When enabled, prints DNS record changes rather than actually performing them (default: disabled) |
+| `--[no-]events` | When enabled, in addition to running every interval, the reconciliation loop will get triggered when supported sources change (default: disabled) |
+| `--log-format=text` | The format in which log messages are printed (default: text, options: text, json) |
+| `--metrics-address=":7979"` | Specify where to serve the metrics and health check endpoint (default: :7979) |
+| `--log-level=info` | Set the level of logging. (default: info, options: panic, debug, info, warning, error, fatal) |
+| `--webhook-provider-url="http://localhost:8888"` | The URL of the remote endpoint to call for the webhook provider (default: http://localhost:8888) |
+| `--webhook-provider-read-timeout=5s` | The read timeout for the webhook provider in duration format (default: 5s) |
+| `--webhook-provider-write-timeout=10s` | The write timeout for the webhook provider in duration format (default: 10s) |
+| `--[no-]webhook-server` | When enabled, runs as a webhook server instead of a controller. (default: false). |
\ No newline at end of file
diff --git a/docs/registry/dynamodb.md b/docs/registry/dynamodb.md
index 545e09e164..84e29daf47 100644
--- a/docs/registry/dynamodb.md
+++ b/docs/registry/dynamodb.md
@@ -81,7 +81,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/release.md b/docs/release.md
index 2470712cbf..b7dc448aff 100644
--- a/docs/release.md
+++ b/docs/release.md
@@ -31,7 +31,7 @@ You must be an official maintainer of the project to be able to do a release.
- Branch out from the default branch and run `scripts/kustomize-version-updater.sh` to update the image tag used in the kustomization.yaml.
- Create an issue to release the corresponding Helm chart via the chart release process (below) assigned to a chart maintainer
- Create a PR with the kustomize change.
-- Create a PR to replace all versions for docker images in the tutorials. A possible script to use is `sd registry.k8s.io/external-dns/external-dns:v0.15.0`
+- Create a PR to replace all versions for docker images in the tutorials. A possible script to use is `sd registry.k8s.io/external-dns/external-dns:v0.15.1`
- Once the PR is merged, all is done :-)
## How to release a new chart version
diff --git a/docs/contributing/crd-source.md b/docs/sources/crd.md
similarity index 70%
rename from docs/contributing/crd-source.md
rename to docs/sources/crd.md
index 40e3814a5b..379741b9a5 100644
--- a/docs/contributing/crd-source.md
+++ b/docs/sources/crd.md
@@ -82,18 +82,18 @@ Create the objects of CRD type by filling in the fields of CRD and DNS record wo
### Example
-Here is an example [CRD manifest](crd-source/crd-manifest.yaml) generated by kubebuilder.
+Here is an example [CRD manifest](crd/crd-manifest.yaml) generated by kubebuilder.
Apply this to register the CRD
```
-$ kubectl apply --validate=false -f docs/contributing/crd-source/crd-manifest.yaml
+$ kubectl apply --validate=false -f docs/sources/crd/crd-manifest.yaml
customresourcedefinition.apiextensions.k8s.io "dnsendpoints.externaldns.k8s.io" created
```
-Then you can create the dns-endpoint yaml similar to [dnsendpoint-example](crd-source/dnsendpoint-example.yaml)
+Then you can create the dns-endpoint yaml similar to [dnsendpoint-example](crd/dnsendpoint-example.yaml)
```
-$ kubectl apply -f docs/contributing/crd-source/dnsendpoint-example.yaml
+$ kubectl apply -f docs/sources/crd/dnsendpoint-example.yaml
dnsendpoint.externaldns.k8s.io "examplednsrecord" created
```
@@ -107,6 +107,69 @@ INFO[0000] CREATE: foo.bar.com 180 IN A 192.168.99.216
INFO[0000] CREATE: foo.bar.com 0 IN TXT "heritage=external-dns,external-dns/owner=default"
```
+#### Using CRD source to manage DNS records in different DNS providers
+
+[CRD source](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/sources/crd.md) provides a generic mechanism and declarative way to manage DNS records in different DNS providers using external-dns.
+
+**Not all the record types are enabled by default so the required record types must be enabled by using `--managed-record-types`.**
+
+```bash
+external-dns --source=crd \
+ --domain-filter=example.com \
+ --managed-record-types=A \
+ --managed-record-types=CNAME \
+ --managed-record-types=NS
+```
+
+* Example for record type `A`
+
+```yaml
+apiVersion: externaldns.k8s.io/v1alpha1
+kind: DNSEndpoint
+metadata:
+ name: examplearecord
+spec:
+ endpoints:
+ - dnsName: example.com
+ recordTTL: 60
+ recordType: A
+ targets:
+ - 10.0.0.1
+```
+
+* Example for record type `CNAME`
+
+```yaml
+apiVersion: externaldns.k8s.io/v1alpha1
+kind: DNSEndpoint
+metadata:
+ name: examplecnamerecord
+spec:
+ endpoints:
+ - dnsName: test-a.example.com
+ recordTTL: 300
+ recordType: CNAME
+ targets:
+ - example.com
+```
+
+* Example for record type `NS`
+
+```yaml
+apiVersion: externaldns.k8s.io/v1alpha1
+kind: DNSEndpoint
+metadata:
+ name: ns-record
+spec:
+ endpoints:
+ - dnsName: zone.example.com
+ recordTTL: 300
+ recordType: NS
+ targets:
+ - ns1.example.com
+ - ns2.example.com
+```
+
### RBAC configuration
If you use RBAC, extend the `external-dns` ClusterRole with:
diff --git a/docs/contributing/crd-source/crd-manifest.yaml b/docs/sources/crd/crd-manifest.yaml
similarity index 100%
rename from docs/contributing/crd-source/crd-manifest.yaml
rename to docs/sources/crd/crd-manifest.yaml
diff --git a/docs/contributing/crd-source/dnsendpoint-aws-example.yaml b/docs/sources/crd/dnsendpoint-aws-example.yaml
similarity index 100%
rename from docs/contributing/crd-source/dnsendpoint-aws-example.yaml
rename to docs/sources/crd/dnsendpoint-aws-example.yaml
diff --git a/docs/contributing/crd-source/dnsendpoint-example.yaml b/docs/sources/crd/dnsendpoint-example.yaml
similarity index 100%
rename from docs/contributing/crd-source/dnsendpoint-example.yaml
rename to docs/sources/crd/dnsendpoint-example.yaml
diff --git a/docs/sources/gateway-api.md b/docs/sources/gateway-api.md
index 534cc03630..ec694bf0c3 100644
--- a/docs/sources/gateway-api.md
+++ b/docs/sources/gateway-api.md
@@ -87,7 +87,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
# Add desired Gateway API Route sources.
- --source=gateway-httproute
diff --git a/docs/sources/gloo-proxy.md b/docs/sources/gloo-proxy.md
index 79e9f40452..9ee06b9863 100644
--- a/docs/sources/gloo-proxy.md
+++ b/docs/sources/gloo-proxy.md
@@ -22,7 +22,7 @@ spec:
containers:
- name: external-dns
# update this to the desired external-dns version
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=gloo-proxy
- --gloo-namespace=custom-gloo-system # gloo system namespace. Specify multiple times for multiple namespaces. Omit to use the default (gloo-system)
@@ -90,7 +90,7 @@ spec:
containers:
- name: external-dns
# update this to the desired external-dns version
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=gloo-proxy
- --gloo-namespace=custom-gloo-system # gloo system namespace. Specify multiple times for multiple namespaces. Omit to use the default (gloo-system)
diff --git a/docs/sources/istio.md b/docs/sources/istio.md
index 20ce2b7cc8..9210f9bbf0 100644
--- a/docs/sources/istio.md
+++ b/docs/sources/istio.md
@@ -29,7 +29,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
@@ -100,7 +100,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/sources/kong.md b/docs/sources/kong.md
index fa4f16946b..500a23baea 100644
--- a/docs/sources/kong.md
+++ b/docs/sources/kong.md
@@ -24,7 +24,7 @@ spec:
containers:
- name: external-dns
# update this to the desired external-dns version
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=kong-tcpingress
- --provider=aws
@@ -88,7 +88,7 @@ spec:
containers:
- name: external-dns
# update this to the desired external-dns version
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=kong-tcpingress
- --provider=aws
diff --git a/docs/sources/mx-record.md b/docs/sources/mx-record.md
index 53c2b30655..3abf618a25 100644
--- a/docs/sources/mx-record.md
+++ b/docs/sources/mx-record.md
@@ -1,12 +1,12 @@
# MX record with CRD source
You can create and manage MX records with the help of [CRD source](../contributing/crd-source.md)
-and `DNSEndpoint` CRD. Currently, this feature is only supported by `aws`, `azure`, and `google` providers.
+and `DNSEndpoint` CRD. Currently, this feature is only supported by `aws`, `azure`, `google` and `digitalocean` providers.
-In order to start managing MX records you need to set the `--managed-record-types MX` flag.
+In order to start managing MX records you need to set the `--managed-record-types=MX` flag.
```console
-external-dns --source crd --provider {aws|azure|google} --managed-record-types A --managed-record-types CNAME --managed-record-types MX
+external-dns --source crd --provider {aws|azure|google|digitalocean} --managed-record-types=A --managed-record-types=CNAME --managed-record-types=MX
```
Targets within the CRD need to be specified according to the RFC 1034 (section 3.6.1). Below is an example of
diff --git a/docs/sources/nodes.md b/docs/sources/nodes.md
index 0db1c99c7e..0fbc7e2128 100644
--- a/docs/sources/nodes.md
+++ b/docs/sources/nodes.md
@@ -7,6 +7,9 @@ The node source adds an `A` record per each node `externalIP` (if not found, any
It also adds an `AAAA` record per each node IPv6 `internalIP`.
The TTL of the records can be set with the `external-dns.alpha.kubernetes.io/ttl` node annotation.
+Nodes marked as **Unschedulable** as per [core/v1/NodeSpec](https://pkg.go.dev/k8s.io/api@v0.31.1/core/v1#NodeSpec) are excluded.
+This avoid exposing Unhealthy, NotReady or SchedulingDisabled (cordon) nodes.
+
## Manifest (for cluster without RBAC enabled)
```
@@ -29,7 +32,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=node # will use nodes as source
- --provider=aws
@@ -100,7 +103,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=node # will use nodes as source
- --provider=aws
diff --git a/docs/sources/ns-record.md b/docs/sources/ns-record.md
index 4250aed750..9baba533e5 100644
--- a/docs/sources/ns-record.md
+++ b/docs/sources/ns-record.md
@@ -3,6 +3,12 @@
You can create NS records with the help of [CRD source](../contributing/crd-source.md)
and `DNSEndpoint` CRD.
+In order to start managing NS records you need to set the `--managed-record-types=NS` flag.
+
+```console
+external-dns --source crd --managed-record-types=A --managed-record-types=CNAME --managed-record-types=NS
+```
+
Consider the following example
```yaml
diff --git a/docs/sources/openshift.md b/docs/sources/openshift.md
index 74088156a3..9c3385a8bb 100644
--- a/docs/sources/openshift.md
+++ b/docs/sources/openshift.md
@@ -67,7 +67,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=openshift-route
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
@@ -134,7 +134,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=openshift-route
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
diff --git a/docs/sources/service.md b/docs/sources/service.md
index c112408d09..4a2cc3118d 100644
--- a/docs/sources/service.md
+++ b/docs/sources/service.md
@@ -35,7 +35,7 @@ the value of the Pod's `spec.hostname` field and a `.`.
## Targets
-If the Service has an `external-dns.alpha.kubernetes.io/target` annotation, uses
+If the Service has an `external-dns.alpha.kubernetes.io/target` annotation, uses
the values from that. Otherwise, the targets of the DNS entries created from a service are sourced depending
on the Service's `spec.type`:
@@ -61,7 +61,7 @@ also iterates over the Endpoints's `subsets.notReadyAddresses`.
1. If an address does not target a `Pod` that matches the Service's `spec.selector`, it is ignored.
-2. If the target pod has an `external-dns.alpha.kubernetes.io/target` annotation, uses
+2. If the target pod has an `external-dns.alpha.kubernetes.io/target` annotation, uses
the values from that.
3. Otherwise, if the Service has an `external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP`
@@ -76,7 +76,7 @@ or the `--publish-host-ip` flag was specified, uses the Pod's `status.hostIP` fi
### ClusterIP (not headless)
1. If the hostname came from an `external-dns.alpha.kubernetes.io/internal-hostname` annotation
-or the `--publish-internal-services` flag was specified, uses the `spec.ServiceIP`.
+or the `--publish-internal-services` flag was specified, uses the `spec.ClusterIP`.
2. Otherwise, does not create any targets.
@@ -87,13 +87,13 @@ and has a `status.phase` of `Running`. Otherwise iterates over all Nodes, of any
Iterates over each relevant Node's `status.addresses`:
-1. If there is an `external-dns.alpha.kubernetes.io/access: public` annotation on the Service, uses both addresses with
+1. If there is an `external-dns.alpha.kubernetes.io/access: public` annotation on the Service, uses both addresses with
a `type` of `ExternalIP` and IPv6 addresses with a `type` of `InternalIP`.
-2. Otherwise, if there is an `external-dns.alpha.kubernetes.io/access: private` annotation on the Service, uses addresses with
+2. Otherwise, if there is an `external-dns.alpha.kubernetes.io/access: private` annotation on the Service, uses addresses with
a `type` of `InternalIP`.
-3. Otherwise, if there is at least one address with a `type` of `ExternalIP`, uses both addresses with
+3. Otherwise, if there is at least one address with a `type` of `ExternalIP`, uses both addresses with
a `type` of `ExternalIP` and IPv6 addresses with a `type` of `InternalIP`.
4. Otherwise, uses addresses with a `type` of `InternalIP`.
@@ -101,9 +101,13 @@ a `type` of `ExternalIP` and IPv6 addresses with a `type` of `InternalIP`.
Also iterates over the Service's `spec.ports`, creating a SRV record for each port which has a `nodePort`.
The SRV record has a service of the Service's `name`, a protocol taken from the port's `protocol` field,
a priority of `0` and a weight of `50`.
-In order for SRV records to be created, the `--managed-record-types`must have been specified, including `SRV`
+In order for SRV records to be created, the `--managed-record-types` must have been specified, including `SRV`
as one of the values.
+```console
+external-dns ... --managed-record-types=A --managed-record-types=CNAME --managed-record-types=SRV
+```
+
### ExternalName
1. If the Service has one or more `spec.externalIPs`, uses the values in that field.
diff --git a/docs/sources/traefik-proxy.md b/docs/sources/traefik-proxy.md
index 9ecce71dcc..009bed08a5 100644
--- a/docs/sources/traefik-proxy.md
+++ b/docs/sources/traefik-proxy.md
@@ -24,7 +24,7 @@ spec:
containers:
- name: external-dns
# update this to the desired external-dns version
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=traefik-proxy
- --provider=aws
@@ -87,7 +87,7 @@ spec:
containers:
- name: external-dns
# update this to the desired external-dns version
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=traefik-proxy
- --provider=aws
diff --git a/docs/sources/txt-record.md b/docs/sources/txt-record.md
new file mode 100644
index 0000000000..89ba458b42
--- /dev/null
+++ b/docs/sources/txt-record.md
@@ -0,0 +1,30 @@
+# Creating TXT record with CRD source
+
+You can create and manage TXT records with the help of [CRD source](../contributing/crd-source.md)
+and `DNSEndpoint` CRD. Currently, this feature is only supported by `digitalocean` providers.
+
+In order to start managing TXT records you need to set the `--managed-record-types=TXT` flag.
+
+```console
+external-dns --source crd --provider {digitalocean} --managed-record-types=A --managed-record-types=CNAME --managed-record-types=TXT
+```
+
+Targets within the CRD need to be specified according to the RFC 1035 (section 3.3.14). Below is an example of
+`example.com` DNS TXT two records creation.
+
+**NOTE** Current implementation do not support RFC 6763 (section 6).
+
+```yaml
+apiVersion: externaldns.k8s.io/v1alpha1
+kind: DNSEndpoint
+metadata:
+ name: examplemxrecord
+spec:
+ endpoints:
+ - dnsName: example.com
+ recordTTL: 180
+ recordType: TXT
+ targets:
+ - SOMETXT
+ - ANOTHERTXT
+```
diff --git a/docs/tutorials/akamai-edgedns.md b/docs/tutorials/akamai-edgedns.md
index 104b1503fa..9983afa88d 100644
--- a/docs/tutorials/akamai-edgedns.md
+++ b/docs/tutorials/akamai-edgedns.md
@@ -104,7 +104,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # or ingress or both
- --provider=akamai
@@ -190,7 +190,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # or ingress or both
- --provider=akamai
diff --git a/docs/tutorials/alibabacloud.md b/docs/tutorials/alibabacloud.md
index d143e0e6dd..33085fef14 100644
--- a/docs/tutorials/alibabacloud.md
+++ b/docs/tutorials/alibabacloud.md
@@ -113,7 +113,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
@@ -187,7 +187,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/tutorials/aws-public-private-route53.md b/docs/tutorials/aws-public-private-route53.md
index bf87dfccf1..6a9a1a79cb 100644
--- a/docs/tutorials/aws-public-private-route53.md
+++ b/docs/tutorials/aws-public-private-route53.md
@@ -241,7 +241,7 @@ spec:
- --txt-owner-id=external-dns
- --ingress-class=external-ingress
- --aws-zone-type=public
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
name: external-dns-public
```
@@ -279,7 +279,7 @@ spec:
- --txt-owner-id=dev.k8s.nexus
- --ingress-class=internal-ingress
- --aws-zone-type=private
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
name: external-dns-private
```
diff --git a/docs/tutorials/aws-sd.md b/docs/tutorials/aws-sd.md
index c12616931e..beb8783245 100644
--- a/docs/tutorials/aws-sd.md
+++ b/docs/tutorials/aws-sd.md
@@ -12,7 +12,7 @@ Learn more about the API in the [AWS Cloud Map API Reference](https://docs.aws.a
## IAM Permissions
-To use the AWS Cloud Map API, a user must have permissions to create the DNS namespace. Additionally you need to make sure that your nodes (on which External DNS runs) have an IAM instance profile with the `AWSCloudMapFullAccess` managed policy attached, that provides following permissions:
+To use the AWS Cloud Map API, a user must have permissions to create the DNS namespace. You need to make sure that your nodes (on which External DNS runs) have an IAM instance profile with the `AWSCloudMapFullAccess` managed policy attached, that provides following permissions:
```
{
@@ -42,6 +42,82 @@ To use the AWS Cloud Map API, a user must have permissions to create the DNS nam
}
```
+### IAM Permissions with ABAC
+You can use Attribute-based access control(ABAC) for advanced deployments.
+
+You can define AWS tags that are applied to services created by the controller. By doing so, you can have precise control over your IAM policy to limit the scope of the permissions to services managed by the controller, rather than having to grant full permissions on your entire AWS account.
+To pass tags to service creation, use either CLI flags or environment variables:
+
+*cli:* `--aws-sd-create-tag=key1=value1 --aws-sd-create-tag=key2=value2`
+
+*environment:* `EXTERNAL_DNS_AWS_SD_CREATE_TAG=key1=value1\nkey2=value2`
+
+Using tags, your `servicediscovery` policy can become:
+
+```
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "servicediscovery:ListNamespaces",
+ "servicediscovery:ListServices"
+ ],
+ "Resource": [
+ "*"
+ ]
+ },
+ {
+ "Effect": "Allow",
+ "Action": [
+ "servicediscovery:CreateService",
+ "servicediscovery:TagResource"
+ ],
+ "Resource": [
+ "*"
+ ],
+ "Condition": {
+ "StringEquals": {
+ "aws:RequestTag/YOUR_TAG_KEY": "YOUR_TAG_VALUE"
+ }
+ }
+ },
+ {
+ "Effect": "Allow",
+ "Action": [
+ "servicediscovery:DiscoverInstances"
+ ],
+ "Resource": [
+ "*"
+ ],
+ "Condition": {
+ "StringEquals": {
+ "servicediscovery:NamespaceName": "YOUR_NAMESPACE_NAME"
+ }
+ }
+ },
+ {
+ "Effect": "Allow",
+ "Action": [
+ "servicediscovery:RegisterInstance",
+ "servicediscovery:DeregisterInstance",
+ "servicediscovery:DeleteService",
+ "servicediscovery:UpdateService"
+ ],
+ "Resource": [
+ "*"
+ ],
+ "Condition": {
+ "StringEquals": {
+ "aws:ResourceTag/YOUR_TAG_KEY": "YOUR_TAG_VALUE"
+ }
+ }
+ }
+ ]
+}
+```
+
## Set up a namespace
Create a DNS namespace using the AWS Cloud Map API:
@@ -81,7 +157,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
env:
- name: AWS_REGION
value: us-east-1 # put your CloudMap NameSpace region
@@ -148,7 +224,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
env:
- name: AWS_REGION
value: us-east-1 # put your CloudMap NameSpace region
@@ -228,6 +304,32 @@ spec:
This will set the TTL for the DNS record to 60 seconds.
+## IPv6 Support
+
+If your Kubernetes cluster is configured with IPv6 support, such as an [EKS cluster with IPv6 support](https://docs.aws.amazon.com/eks/latest/userguide/deploy-ipv6-cluster.html), ExternalDNS can
+also create AAAA DNS records.
+
+```yaml
+apiVersion: v1
+kind: Service
+metadata:
+ name: nginx
+ annotations:
+ external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com
+ external-dns.alpha.kubernetes.io/ttl: "60"
+spec:
+ ipFamilies:
+ - "IPv6"
+ type: NodePort
+ ports:
+ - port: 80
+ name: http
+ targetPort: 80
+ selector:
+ app: nginx
+```
+
+:information_source: The AWS-SD provider does not currently support dualstack load balancers and will only create A records for these at this time. See the AWS provider and the [AWS Load Balancer Controller Tutorial](./aws-load-balancer-controller.md) for dualstack load balancer support.
## Clean up
diff --git a/docs/tutorials/aws.md b/docs/tutorials/aws.md
index c5797092b9..fe0b7ce974 100644
--- a/docs/tutorials/aws.md
+++ b/docs/tutorials/aws.md
@@ -233,11 +233,11 @@ kubectl create secret generic external-dns \
Follow the steps under [Deploy ExternalDNS](#deploy-externaldns) using either RBAC or non-RBAC. Make sure to uncomment the section that mounts volumes, so that the credentials can be mounted.
> [!TIP]
-> By default ExternalDNS takes the profile named `default` from the credentials file. If you want to use a different
-> profile, you can set the environment variable `EXTERNAL_DNS_AWS_PROFILE` to the desired profile name or use the
+> By default ExternalDNS takes the profile named `default` from the credentials file. If you want to use a different
+> profile, you can set the environment variable `EXTERNAL_DNS_AWS_PROFILE` to the desired profile name or use the
> `--aws-profile` command line argument. It is even possible to use more than one profile at ones, separated by space in
-> the environment variable `EXTERNAL_DNS_AWS_PROFILE` or by using `--aws-profile` multiple times. In this case
-> ExternalDNS looks for the hosted zones in all profiles and keeps maintaining a mapping table between zone and profile
+> the environment variable `EXTERNAL_DNS_AWS_PROFILE` or by using `--aws-profile` multiple times. In this case
+> ExternalDNS looks for the hosted zones in all profiles and keeps maintaining a mapping table between zone and profile
> in order to be able to modify the zones in the correct profile.
### IAM Roles for Service Accounts
@@ -442,7 +442,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
@@ -951,7 +951,7 @@ A simple way to implement randomised startup is with an init container:
spec:
initContainers:
- name: init-jitter
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
command:
- /bin/sh
- -c
@@ -987,3 +987,7 @@ There are 3 options to control batch size for AWS provider:
Default values for flags `aws-batch-change-size-bytes` and `aws-batch-change-size-values` are taken from [AWS documentation](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests) for Route53 API. **You should not change those values until you really have to.**
Because those limits are in place, `aws-batch-change-size` can be set to any value: Even if your batch size is `4000` records, your change will be split to separate batches due to bytes/values size limits and apply request will be finished without issues.
+
+## Using CRD source to manage DNS records in AWS
+
+Please refer to the [CRD source documentation](../sources/crd.md#example) for more information.
\ No newline at end of file
diff --git a/docs/tutorials/azure-private-dns.md b/docs/tutorials/azure-private-dns.md
index d107995837..f4211760f2 100644
--- a/docs/tutorials/azure-private-dns.md
+++ b/docs/tutorials/azure-private-dns.md
@@ -98,6 +98,10 @@ $ az role assignment create --role "Reader" --assignee --scope --scope
```
+## Throttling
+
+When the ExternalDNS managed zones list doesn't change frequently, one can set `--azure-zones-cache-duration` (zones list cache time-to-live). The zones list cache is disabled by default, with a value of 0s.
+
## Deploy ExternalDNS
Configure `kubectl` to be able to communicate and authenticate with your cluster.
This is per default done through the file `~/.kube/config`.
@@ -130,7 +134,7 @@ spec:
spec:
containers:
- name: externaldns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
@@ -201,7 +205,7 @@ spec:
serviceAccountName: externaldns
containers:
- name: externaldns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
@@ -272,7 +276,7 @@ spec:
serviceAccountName: externaldns
containers:
- name: externaldns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/tutorials/azure.md b/docs/tutorials/azure.md
index e6d277e182..8f7b565b81 100644
--- a/docs/tutorials/azure.md
+++ b/docs/tutorials/azure.md
@@ -54,9 +54,10 @@ The following fields are used:
* `tenantId` (**required**) - run `az account show --query "tenantId"` or by selecting Azure Active Directory in the Azure Portal and checking the _Directory ID_ under Properties.
* `subscriptionId` (**required**) - run `az account show --query "id"` or by selecting Subscriptions in the Azure Portal.
* `resourceGroup` (**required**) is the Resource Group created in a previous step that contains the Azure DNS Zone.
-* `aadClientID` and `aadClientSecret` are associated with the Service Principal. This is only used with Service Principal method documented in the next section.
+* `aadClientID` is associated with the Service Principal. This is used with Service Principal or Workload Identity methods documented in the next section.
+* `aadClientSecret` is associated with the Service Principal. This is only used with Service Principal method documented in the next section.
* `useManagedIdentityExtension` - this is set to `true` if you use either AKS Kubelet Identity or AAD Pod Identities methods documented in the next section.
-* `userAssignedIdentityID` - this contains the client id from the Managed identitty when using the AAD Pod Identities method documented in the next setion.
+* `userAssignedIdentityID` - this contains the client id from the Managed identity when using the AAD Pod Identities method documented in the next setion.
* `activeDirectoryAuthorityHost` - this contains the uri to overwrite the default provided AAD Endpoint. This is useful for providing additional support where the endpoint is not available in the default cloud config from the [azure-sdk-for-go](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud#pkg-variables).
* `useWorkloadIdentityExtension` - this is set to `true` if you use Workload Identity method documented in the next section.
@@ -456,6 +457,7 @@ cat <<-EOF > /local/path/to/azure.json
}
EOF
```
+NOTE: it's also possible to specify (or override) ClientID specified in the next section through `aadClientId` field in this `azure.json` file.
Use the `azure.json` file to create a Kubernetes secret:
@@ -476,10 +478,14 @@ $ kubectl patch deployment external-dns --namespace "default" --patch \
'{"spec": {"template": {"metadata": {"labels": {\"azure.workload.identity/use\": \"true\"}}}}}'
```
-NOTE: it's also possible to specify (or override) ClientID through `userAssignedIdentityID` field in `azure.json`.
+NOTE: it's also possible to specify (or override) ClientID through `aadClientId` field in `azure.json`.
NOTE: make sure the pod is restarted whenever you make a configuration change.
+## Throttling
+
+When the ExternalDNS managed zones list doesn't change frequently, one can set `--azure-zones-cache-duration` (zones list cache time-to-live). The zones list cache is disabled by default, with a value of 0s.
+
## Ingress used with ExternalDNS
This deployment assumes that you will be using nginx-ingress. When using nginx-ingress do not deploy it as a Daemon Set. This causes nginx-ingress to write the Cluster IP of the backend pods in the ingress status.loadbalancer.ip property which then has external-dns write the Cluster IP(s) in DNS vs. the nginx-ingress service external IP.
@@ -517,7 +523,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
@@ -585,7 +591,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
@@ -656,7 +662,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/tutorials/civo.md b/docs/tutorials/civo.md
index 8922a9ebdf..fa9960e363 100644
--- a/docs/tutorials/civo.md
+++ b/docs/tutorials/civo.md
@@ -40,7 +40,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -104,7 +104,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/cloudflare.md b/docs/tutorials/cloudflare.md
index b2a4d40bfe..f4be1f3a81 100644
--- a/docs/tutorials/cloudflare.md
+++ b/docs/tutorials/cloudflare.md
@@ -58,7 +58,7 @@ Then apply one of the following manifests file to deploy ExternalDNS.
Create a values.yaml file to configure ExternalDNS to use CloudFlare as the DNS provider. This file should include the necessary environment variables:
```yaml
-provider:
+provider:
name: cloudflare
env:
- name: CF_API_KEY
@@ -76,7 +76,7 @@ env:
Use this in your values.yaml, if you are using API Token:
```yaml
-provider:
+provider:
name: cloudflare
env:
- name: CF_API_TOKEN
@@ -121,7 +121,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -129,17 +129,18 @@ spec:
- --provider=cloudflare
- --cloudflare-proxied # (optional) enable the proxy feature of Cloudflare (DDOS protection, CDN...)
- --cloudflare-dns-records-per-page=5000 # (optional) configure how many DNS records to fetch per request
- env:
- - name: CF_API_KEY
- valueFrom:
- secretKeyRef:
- name: cloudflare-api-key
- key: apiKey
- - name: CF_API_EMAIL
- valueFrom:
- secretKeyRef:
- name: cloudflare-api-key
- key: email
+ - --cloudflare-region-key="eu" # (optional) configure which region can decrypt HTTPS requests
+ env:
+ - name: CF_API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: cloudflare-api-key
+ key: apiKey
+ - name: CF_API_EMAIL
+ valueFrom:
+ secretKeyRef:
+ name: cloudflare-api-key
+ key: email
```
### Manifest (for clusters with RBAC enabled)
@@ -196,7 +197,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -204,6 +205,7 @@ spec:
- --provider=cloudflare
- --cloudflare-proxied # (optional) enable the proxy feature of Cloudflare (DDOS protection, CDN...)
- --cloudflare-dns-records-per-page=5000 # (optional) configure how many DNS records to fetch per request
+ - --cloudflare-region-key="eu" # (optional) configure which region can decrypt HTTPS requests
env:
- name: CF_API_KEY
valueFrom:
@@ -299,3 +301,13 @@ $ kubectl delete -f externaldns.yaml
## Setting cloudflare-proxied on a per-ingress basis
Using the `external-dns.alpha.kubernetes.io/cloudflare-proxied: "true"` annotation on your ingress, you can specify if the proxy feature of Cloudflare should be enabled for that record. This setting will override the global `--cloudflare-proxied` setting.
+
+## Setting cloudflare-region-key to configure regional services
+
+Using the `external-dns.alpha.kubernetes.io/cloudflare-region-key` annotation on your ingress, you can restrict which data centers can decrypt and serve HTTPS traffic. A list of available options can be seen [here](https://developers.cloudflare.com/data-localization/regional-services/get-started/).
+
+If not set the value will default to `global`.
+
+## Using CRD source to manage DNS records in Cloudflare
+
+Please refer to the [CRD source documentation](../sources/crd.md#example) for more information.
\ No newline at end of file
diff --git a/docs/tutorials/contour.md b/docs/tutorials/contour.md
index edd1484afb..d30999fc1f 100644
--- a/docs/tutorials/contour.md
+++ b/docs/tutorials/contour.md
@@ -24,7 +24,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
@@ -93,7 +93,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/tutorials/coredns.md b/docs/tutorials/coredns.md
index 37552b577c..ccce774d16 100644
--- a/docs/tutorials/coredns.md
+++ b/docs/tutorials/coredns.md
@@ -132,7 +132,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=ingress
- --provider=coredns
@@ -199,7 +199,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=ingress
- --provider=coredns
diff --git a/docs/tutorials/designate.md b/docs/tutorials/designate.md
index 381fde7e1d..3163aca5c1 100644
--- a/docs/tutorials/designate.md
+++ b/docs/tutorials/designate.md
@@ -59,7 +59,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -136,7 +136,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/digitalocean.md b/docs/tutorials/digitalocean.md
index e6383f2bf4..8b23357327 100644
--- a/docs/tutorials/digitalocean.md
+++ b/docs/tutorials/digitalocean.md
@@ -68,7 +68,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -135,7 +135,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/dnsimple.md b/docs/tutorials/dnsimple.md
index 1f06269714..249e188b47 100644
--- a/docs/tutorials/dnsimple.md
+++ b/docs/tutorials/dnsimple.md
@@ -39,7 +39,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple.
@@ -108,7 +108,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple.
diff --git a/docs/tutorials/exoscale.md b/docs/tutorials/exoscale.md
index 2201d831cb..0d0f6d3d7a 100644
--- a/docs/tutorials/exoscale.md
+++ b/docs/tutorials/exoscale.md
@@ -40,7 +40,7 @@ spec:
# serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=ingress # or service or both
- --provider=exoscale
diff --git a/docs/tutorials/externalname.md b/docs/tutorials/externalname.md
index e4fd2d9394..99b78bdf33 100644
--- a/docs/tutorials/externalname.md
+++ b/docs/tutorials/externalname.md
@@ -27,7 +27,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --log-level=debug
- --source=service
diff --git a/docs/tutorials/gandi.md b/docs/tutorials/gandi.md
index b9de48b753..32a667fa0d 100644
--- a/docs/tutorials/gandi.md
+++ b/docs/tutorials/gandi.md
@@ -41,7 +41,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -105,7 +105,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/gke-nginx.md b/docs/tutorials/gke-nginx.md
index ea13edcb3c..fe7e03f0fe 100644
--- a/docs/tutorials/gke-nginx.md
+++ b/docs/tutorials/gke-nginx.md
@@ -273,7 +273,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=ingress
- --domain-filter=external-dns-test.gcp.zalan.do
@@ -568,7 +568,7 @@ spec:
- --google-project=zalando-external-dns-test
- --registry=txt
- --txt-owner-id=my-identifier
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
name: external-dns
securityContext:
fsGroup: 65534
diff --git a/docs/tutorials/gke.md b/docs/tutorials/gke.md
index 5f5862091a..314c0ede1a 100644
--- a/docs/tutorials/gke.md
+++ b/docs/tutorials/gke.md
@@ -375,7 +375,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/tutorials/godaddy.md b/docs/tutorials/godaddy.md
index bd97dce9e8..460c1c4f09 100644
--- a/docs/tutorials/godaddy.md
+++ b/docs/tutorials/godaddy.md
@@ -1,6 +1,6 @@
# GoDaddy
-This tutorial describes how to setup ExternalDNS for use within a
+This tutorial describes how to set up ExternalDNS for use within a
Kubernetes cluster using GoDaddy DNS.
Make sure to use **>=0.6** version of ExternalDNS for this tutorial.
@@ -26,7 +26,7 @@ Connect your `kubectl` client to the cluster with which you want to test Externa
## Using Helm
-Create a values.yaml file to configure ExternalDNS to use NS1 as the DNS provider. This file should include the necessary environment variables:
+Create a values.yaml file to configure ExternalDNS to use GoDaddy as the DNS provider. This file should include the necessary environment variables:
```shell
provider:
@@ -36,7 +36,7 @@ extraArgs:
- --godaddy-api-secret=YOUR_API_SECRET
```
-Ensure to replace YOUR_API_KEY and YOUR_API_SECRET with your actual godaddy API key and godaddy API secret.
+Be sure to replace YOUR_API_KEY and YOUR_API_SECRET with your actual GoDaddy API key and GoDaddy API secret.
Finally, install the ExternalDNS chart with Helm using the configuration specified in your values.yaml file:
@@ -64,7 +64,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -135,7 +135,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/hostport.md b/docs/tutorials/hostport.md
index 3398325602..8490748f7f 100644
--- a/docs/tutorials/hostport.md
+++ b/docs/tutorials/hostport.md
@@ -31,7 +31,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --log-level=debug
- --source=service
@@ -96,7 +96,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --log-level=debug
- --source=service
diff --git a/docs/tutorials/ibmcloud.md b/docs/tutorials/ibmcloud.md
index 8cf362c24c..278c8da4a9 100644
--- a/docs/tutorials/ibmcloud.md
+++ b/docs/tutorials/ibmcloud.md
@@ -69,7 +69,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -142,7 +142,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/linode.md b/docs/tutorials/linode.md
index 097a891966..af0b5303be 100644
--- a/docs/tutorials/linode.md
+++ b/docs/tutorials/linode.md
@@ -41,7 +41,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -105,7 +105,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/ns1.md b/docs/tutorials/ns1.md
index 6d7300aff5..ad6f376b1c 100644
--- a/docs/tutorials/ns1.md
+++ b/docs/tutorials/ns1.md
@@ -92,7 +92,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -159,7 +159,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/oracle.md b/docs/tutorials/oracle.md
index ab64494866..541dda81d1 100644
--- a/docs/tutorials/oracle.md
+++ b/docs/tutorials/oracle.md
@@ -170,7 +170,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/tutorials/ovh.md b/docs/tutorials/ovh.md
index 8a27c1a769..6f055911d5 100644
--- a/docs/tutorials/ovh.md
+++ b/docs/tutorials/ovh.md
@@ -91,7 +91,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -165,7 +165,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/pdns.md b/docs/tutorials/pdns.md
index 7e2ad674a2..2d0b60698e 100644
--- a/docs/tutorials/pdns.md
+++ b/docs/tutorials/pdns.md
@@ -42,7 +42,7 @@ spec:
# serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # or ingress or both
- --provider=pdns
@@ -173,3 +173,7 @@ Once the API shows the record correctly, you can double check your record using:
```bash
$ dig @${PDNS_FQDN} echo.example.com.
```
+
+## Using CRD source to manage DNS records in PowerDNS
+
+Please refer to the [CRD source documentation](../sources/crd.md#example) for more information.
\ No newline at end of file
diff --git a/docs/tutorials/pihole.md b/docs/tutorials/pihole.md
index d9b32e4ad7..11d1cbdaae 100644
--- a/docs/tutorials/pihole.md
+++ b/docs/tutorials/pihole.md
@@ -81,7 +81,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
# If authentication is disabled and/or you didn't create
# a secret, you can remove this block.
envFrom:
diff --git a/docs/tutorials/plural.md b/docs/tutorials/plural.md
index 4d85a72ef2..cfed887954 100644
--- a/docs/tutorials/plural.md
+++ b/docs/tutorials/plural.md
@@ -61,7 +61,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -131,7 +131,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/rdns.md b/docs/tutorials/rdns.md
deleted file mode 100644
index 7e10403c1c..0000000000
--- a/docs/tutorials/rdns.md
+++ /dev/null
@@ -1,173 +0,0 @@
-# RancherDNS
-
-This tutorial describes how to setup ExternalDNS for usage within a kubernetes cluster that makes use of [RDNS](https://github.com/rancher/rdns-server) and [nginx ingress controller](https://github.com/kubernetes/ingress-nginx).
-
-You need to:
-
-* install RDNS with [etcd](https://github.com/etcd-io/etcd) enabled
-* install external-dns with rdns as a provider
-
-## Installing RDNS with etcdv3 backend
-
-### Clone RDNS
-```
-git clone https://github.com/rancher/rdns-server.git
-```
-
-### Installing ETCD
-```
-cd rdns-server
-docker-compose -f deploy/etcdv3/etcd-compose.yaml up -d
-```
-
-> ETCD was successfully deployed on `http://172.31.35.77:2379`
-
-### Installing RDNS
-```
-export ETCD_ENDPOINTS="http://172.31.35.77:2379"
-export DOMAIN="lb.rancher.cloud"
-./scripts/start etcdv3
-```
-
-> RDNS was successfully deployed on `172.31.35.77`
-
-## Installing ExternalDNS
-### Install external ExternalDNS
-ETCD_URLS is configured to etcd client service address.
-RDNS_ROOT_DOMAIN is configured to the same with RDNS DOMAIN environment. e.g. lb.rancher.cloud.
-
-#### Manifest (for clusters without RBAC enabled)
-```yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: external-dns
- namespace: kube-system
-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.15.0
- args:
- - --source=ingress
- - --provider=rdns
- - --log-level=debug # debug only
- env:
- - name: ETCD_URLS
- value: http://172.31.35.77:2379
- - name: RDNS_ROOT_DOMAIN
- value: lb.rancher.cloud
-```
-
-#### Manifest (for clusters with RBAC enabled)
-```yaml
-
----
-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: kube-system
----
-apiVersion: v1
-kind: ServiceAccount
-metadata:
- name: external-dns
- namespace: kube-system
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: external-dns
- namespace: kube-system
-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.15.0
- args:
- - --source=ingress
- - --provider=rdns
- - --log-level=debug # debug only
- env:
- - name: ETCD_URLS
- value: http://172.31.35.77:2379
- - name: RDNS_ROOT_DOMAIN
- value: lb.rancher.cloud
-```
-
-## Testing ingress example
-```
-$ cat ingress.yaml
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- name: nginx
-spec:
- ingressClassName: nginx
- rules:
- - host: nginx.lb.rancher.cloud
- http:
- paths:
- - backend:
- serviceName: nginx
- servicePort: 80
-
-$ kubectl apply -f ingress.yaml
-ingress.extensions "nginx" created
-```
-
-Wait a moment until DNS has the ingress IP. The RDNS IP in this example is "172.31.35.77".
-```
-$ kubectl get ingress
-NAME HOSTS ADDRESS PORTS AGE
-nginx nginx.lb.rancher.cloud 172.31.42.211 80 2m
-
-$ kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools
-If you don't see a command prompt, try pressing enter.
-dnstools# dig @172.31.35.77 nginx.lb.rancher.cloud +short
-172.31.42.211
-dnstools#
-```
diff --git a/docs/tutorials/rfc2136.md b/docs/tutorials/rfc2136.md
index a89b39aeca..04790354c2 100644
--- a/docs/tutorials/rfc2136.md
+++ b/docs/tutorials/rfc2136.md
@@ -238,7 +238,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --registry=txt
- --txt-prefix=external-dns-
@@ -281,7 +281,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --registry=txt
- --txt-prefix=external-dns-
diff --git a/docs/tutorials/scaleway.md b/docs/tutorials/scaleway.md
index 5d54143832..bf0a18e03c 100644
--- a/docs/tutorials/scaleway.md
+++ b/docs/tutorials/scaleway.md
@@ -60,7 +60,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
@@ -140,7 +140,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
diff --git a/docs/tutorials/security-context.md b/docs/tutorials/security-context.md
index cb2f3710dc..7bafa57be0 100644
--- a/docs/tutorials/security-context.md
+++ b/docs/tutorials/security-context.md
@@ -20,7 +20,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- ... # your arguments here
securityContext:
diff --git a/docs/tutorials/tencentcloud.md b/docs/tutorials/tencentcloud.md
index 5ac0879242..e38863ff2e 100644
--- a/docs/tutorials/tencentcloud.md
+++ b/docs/tutorials/tencentcloud.md
@@ -131,7 +131,7 @@ spec:
- --policy=sync # set `upsert-only` would prevent ExternalDNS from deleting any records
- --tencent-cloud-zone-type=private # only look at private hosted zones. set `public` to use the public dns service.
- --tencent-cloud-config-file=/etc/kubernetes/tencent-cloud.json
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
imagePullPolicy: Always
name: external-dns
resources: {}
diff --git a/docs/tutorials/transip.md b/docs/tutorials/transip.md
index 9de15e4fe3..85de696cb6 100644
--- a/docs/tutorials/transip.md
+++ b/docs/tutorials/transip.md
@@ -36,7 +36,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains
@@ -107,7 +107,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service # ingress is also possible
- --domain-filter=example.com # (optional) limit to only example.com domains
diff --git a/docs/tutorials/ultradns.md b/docs/tutorials/ultradns.md
index 591de6a648..f15bea8f7b 100644
--- a/docs/tutorials/ultradns.md
+++ b/docs/tutorials/ultradns.md
@@ -44,7 +44,7 @@ spec:
spec:
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress # ingress is also possible
@@ -116,7 +116,7 @@ spec:
serviceAccountName: external-dns
containers:
- name: external-dns
- image: registry.k8s.io/external-dns/external-dns:v0.15.0
+ image: registry.k8s.io/external-dns/external-dns:v0.15.1
args:
- --source=service
- --source=ingress
diff --git a/docs/tutorials/webhook-provider.md b/docs/tutorials/webhook-provider.md
index e3e52f06b6..f3115506bf 100644
--- a/docs/tutorials/webhook-provider.md
+++ b/docs/tutorials/webhook-provider.md
@@ -26,6 +26,8 @@ The following table represents the methods to implement mapped to their HTTP met
| AdjustEndpoints | POST | /adjustendpoints | Provider specific adjustments of records |
| ApplyChanges | POST | /records | Apply record |
+OpenAPI spec is [here](../../api/webhook.yaml).
+
ExternalDNS will also make requests to the `/` endpoint for negotiation and for deserialization of the `DomainFilter`.
The server needs to respond to those requests by reading the `Accept` header and responding with a corresponding `Content-Type` header specifying the supported media type format and version.
diff --git a/go.mod b/go.mod
index 92badfe27f..9db9e6cba6 100644
--- a/go.mod
+++ b/go.mod
@@ -1,37 +1,37 @@
module sigs.k8s.io/external-dns
-go 1.23
+go 1.23.3
require (
- cloud.google.com/go/compute/metadata v0.5.2
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
+ cloud.google.com/go/compute/metadata v0.6.0
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0
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.18.0
+ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0
+ github.com/F5Networks/k8s-bigip-ctlr/v2 v2.18.1
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.5.0
- github.com/IBM/go-sdk-core/v5 v5.17.5
+ github.com/IBM/go-sdk-core/v5 v5.18.3
github.com/IBM/networking-go-sdk v0.49.0
- github.com/Yamashou/gqlgenc v0.25.2
+ github.com/Yamashou/gqlgenc v0.30.2
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.63.18
- github.com/aws/aws-sdk-go-v2 v1.31.0
- github.com/aws/aws-sdk-go-v2/config v1.27.38
- github.com/aws/aws-sdk-go-v2/credentials v1.17.36
- github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.7
- github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.2
- github.com/aws/aws-sdk-go-v2/service/route53 v1.44.2
- github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.32.2
- github.com/aws/aws-sdk-go-v2/service/sts v1.31.2
+ github.com/aliyun/alibaba-cloud-sdk-go v1.63.77
+ github.com/aws/aws-sdk-go-v2 v1.32.7
+ github.com/aws/aws-sdk-go-v2/config v1.28.7
+ github.com/aws/aws-sdk-go-v2/credentials v1.17.48
+ github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.22
+ github.com/aws/aws-sdk-go-v2/service/dynamodb v1.38.1
+ github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4
+ github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.1
+ github.com/aws/aws-sdk-go-v2/service/sts v1.33.3
github.com/bodgit/tsig v1.2.2
github.com/cenkalti/backoff/v4 v4.3.0
- github.com/civo/civogo v0.3.79
- github.com/cloudflare/cloudflare-go v0.105.0
+ github.com/civo/civogo v0.3.90
+ github.com/cloudflare/cloudflare-go v0.113.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.126.0
+ github.com/digitalocean/godo v1.132.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
@@ -41,68 +41,67 @@ require (
github.com/google/uuid v1.6.0
github.com/gophercloud/gophercloud v1.14.1
github.com/linki/instrumented_http v0.3.0
- github.com/linode/linodego v1.41.0
+ github.com/linode/linodego v1.44.0
github.com/maxatome/go-testdeep v1.14.0
github.com/miekg/dns v1.1.62
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.75.0
+ github.com/oracle/oci-go-sdk/v65 v65.81.1
github.com/ovh/go-ovh v1.6.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/pluralsh/gqlclient v1.12.2
- github.com/projectcontour/contour v1.30.0
- github.com/prometheus/client_golang v1.20.4
+ github.com/projectcontour/contour v1.30.1
+ github.com/prometheus/client_golang v1.20.5
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30
github.com/sirupsen/logrus v1.9.3
- github.com/stretchr/testify v1.9.0
- github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1011
- github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1011
- github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1011
+ github.com/stretchr/testify v1.10.0
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1074
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1074
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1074
github.com/transip/gotransip/v6 v6.26.0
github.com/ultradns/ultradns-sdk-go v1.3.7
- go.etcd.io/etcd/api/v3 v3.5.16
- go.etcd.io/etcd/client/v3 v3.5.16
+ go.etcd.io/etcd/client/v3 v3.5.17
go.uber.org/ratelimit v0.3.1
- golang.org/x/net v0.29.0
- golang.org/x/oauth2 v0.23.0
- golang.org/x/sync v0.8.0
- golang.org/x/time v0.6.0
- google.golang.org/api v0.199.0
- gopkg.in/ns1/ns1-go.v2 v2.12.1
+ golang.org/x/net v0.33.0
+ golang.org/x/oauth2 v0.24.0
+ golang.org/x/sync v0.10.0
+ golang.org/x/time v0.8.0
+ google.golang.org/api v0.214.0
+ gopkg.in/ns1/ns1-go.v2 v2.13.0
gopkg.in/yaml.v2 v2.4.0
- istio.io/api v1.23.2
- istio.io/client-go v1.23.2
- k8s.io/api v0.31.1
- k8s.io/apimachinery v0.31.1
- k8s.io/client-go v0.31.1
+ istio.io/api v1.24.2
+ istio.io/client-go v1.24.2
+ k8s.io/api v0.32.0
+ k8s.io/apimachinery v0.32.0
+ k8s.io/client-go v0.32.0
k8s.io/klog/v2 v2.130.1
- sigs.k8s.io/gateway-api v1.1.0
+ sigs.k8s.io/gateway-api v1.2.1
)
require (
- cloud.google.com/go/auth v0.9.5 // indirect
- cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
+ cloud.google.com/go/auth v0.13.0 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
- github.com/99designs/gqlgen v0.17.54 // indirect
+ github.com/99designs/gqlgen v0.17.61 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/Masterminds/semver v1.4.2 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
- github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.23.2 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect
- github.com/aws/aws-sdk-go-v2/service/sso v1.23.2 // indirect
- github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2 // indirect
- github.com/aws/smithy-go v1.21.0 // indirect
+ github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.10 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.7 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect
+ github.com/aws/smithy-go v1.22.1 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
@@ -125,27 +124,25 @@ 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.13.1 // indirect
- github.com/goccy/go-json v0.10.3 // indirect
+ github.com/go-resty/resty/v2 v2.16.2 // indirect
+ github.com/goccy/go-json v0.10.4 // 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
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
- github.com/googleapis/gax-go/v2 v2.13.0 // indirect
+ github.com/googleapis/gax-go/v2 v2.14.0 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
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.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
- github.com/imdario/mergo v0.3.16 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
@@ -185,12 +182,12 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/terra-farm/udnssdk v1.3.5 // indirect
- github.com/vektah/gqlparser/v2 v2.5.16 // indirect
+ github.com/vektah/gqlparser/v2 v2.5.20 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
- go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
+ go.etcd.io/etcd/api/v3 v3.5.17 // indirect
+ go.etcd.io/etcd/client/pkg/v3 v3.5.17 // 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.54.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
@@ -198,26 +195,26 @@ 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.27.0 // indirect
- golang.org/x/mod v0.20.0 // indirect
- golang.org/x/sys v0.25.0 // indirect
- golang.org/x/term v0.24.0 // indirect
- golang.org/x/text v0.18.0 // indirect
- golang.org/x/tools v0.24.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
- google.golang.org/grpc v1.67.0 // indirect
- google.golang.org/protobuf v1.34.2 // indirect
+ golang.org/x/crypto v0.31.0 // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/sys v0.28.0 // indirect
+ golang.org/x/term v0.27.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
+ golang.org/x/tools v0.28.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
+ google.golang.org/grpc v1.67.1 // indirect
+ google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect
- k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
+ k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
+ k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
moul.io/http2curl v1.0.0 // indirect
- sigs.k8s.io/controller-runtime v0.18.4 // indirect
- sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
- sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
+ sigs.k8s.io/controller-runtime v0.18.5 // indirect
+ sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
+ sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
diff --git a/go.sum b/go.sum
index 0f62732ea9..afa4055335 100644
--- a/go.sum
+++ b/go.sum
@@ -2,34 +2,36 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxo
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go/auth v0.9.5 h1:4CTn43Eynw40aFVr3GpPqsQponx2jv0BQpjvajsbbzw=
-cloud.google.com/go/auth v0.9.5/go.mod h1:Xo0n7n66eHyOWWCnitop6870Ilwo3PiZyodVkkH1xWM=
-cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
-cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
-cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
-cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
+cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
+cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
+cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
+cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
+cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
+cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTgeiff9vxdrfdqxibzpWjxLnuXDI5m6z3GJAk=
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.lukeshu.com/go/libsystemd v0.5.3/go.mod h1:FfDoP0i92r4p5Vn4NCLxvjkd7rCOe6otPa4L6hZg9WM=
-github.com/99designs/gqlgen v0.17.54 h1:AsF49k/7RJlwA00RQYsYN0T8cQuaosnV/7G1dHC3Uh8=
-github.com/99designs/gqlgen v0.17.54/go.mod h1:77/+pVe6zlTsz++oUg2m8VLgzdUPHxjoAG3BxI5y8Rc=
+github.com/99designs/gqlgen v0.17.61 h1:vE7xLRC066n9wehgjeplILOWtwz75zbzcV2/Iv9i3pw=
+github.com/99designs/gqlgen v0.17.61/go.mod h1:rFU1T3lhv/tPeAlww/DJ4ol2YxT/pPpue+xxPbkd3r4=
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.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
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=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 h1:9Eih8XcEeQnFD0ntMlUDleKMzfeCeUfa+VbnDCI4AZs=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0/go.mod h1:wGPyTi+aURdqPAGMZDQqnNs9IrShADF8w2WZb6bKeq0=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU=
-github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 h1:yzrctSl9GMIQ5lHu7jc8olOsGjWDCsBpJhWqfGa/YIM=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0/go.mod h1:GE4m0rnnfwLGX0Y9A9A25Zx5N/90jneT5ABevqzhuFQ=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
@@ -39,18 +41,20 @@ github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxB
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
+github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
+github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
-github.com/F5Networks/k8s-bigip-ctlr/v2 v2.18.0 h1:soCQ+QIHHVk/WFQPOfS2moxtFqLHIQuFJlf1U9773aM=
-github.com/F5Networks/k8s-bigip-ctlr/v2 v2.18.0/go.mod h1:m2bDK0Bb/KdhazvWWsQeQFw7R4QpHxX4U/cefZFBtHs=
+github.com/F5Networks/k8s-bigip-ctlr/v2 v2.18.1 h1:2JJzYLLONOJR73uudihkDh2ks8jL8/h6AhO7cbYpdIo=
+github.com/F5Networks/k8s-bigip-ctlr/v2 v2.18.1/go.mod h1:NZ7znr7KT1/0+MFPA9MHBsa4e0YhVz65ZCioi/m5KiE=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.5.0 h1:+a994rHmNFwlSA609Z6SYhn9xt+lhGFF+dsgjMF75hY=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.5.0/go.mod h1:XxWyb5MQDU4GnRBSDZpGgIFwfbcn+GAUbPKS8CR8Bxc=
-github.com/IBM/go-sdk-core/v5 v5.17.5 h1:AjGC7xNee5tgDIjndekBDW5AbypdERHSgib3EZ1KNsA=
-github.com/IBM/go-sdk-core/v5 v5.17.5/go.mod h1:KsAAI7eStAWwQa4F96MLy+whYSh39JzNjklZRbN/8ns=
+github.com/IBM/go-sdk-core/v5 v5.18.3 h1:q6IDU3N2bHGwijK9pMnzKC5gqdaRII56NzB4ZNdSFvY=
+github.com/IBM/go-sdk-core/v5 v5.18.3/go.mod h1:5kILxqEWOrwMhoD2b7J6Xv9Z2M6YIdT/6Oy+XRSsCGQ=
github.com/IBM/networking-go-sdk v0.49.0 h1:lPS34u3C0JVrbxH+Ulua76Nwl6Frv8BEfq6LRkyvOv0=
github.com/IBM/networking-go-sdk v0.49.0/go.mod h1:G9CKbmPE8gSLjN+ABh4hIZ1bMx076enl5Eekvj6zQnA=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -77,8 +81,8 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
-github.com/Yamashou/gqlgenc v0.25.2 h1:4VoeXuI/8M7njUYiOsTSkkEit13rXWOjoK39AG31jPo=
-github.com/Yamashou/gqlgenc v0.25.2/go.mod h1:G0g1N81xpIklVdnyboW1zwOHcj/n4hNfhTwfN29Rjig=
+github.com/Yamashou/gqlgenc v0.30.2 h1:hkoWStLZI2JgrgZuHlJXVnDFt0/B2X6YLv6hFPYpEAg=
+github.com/Yamashou/gqlgenc v0.30.2/go.mod h1:0VNZtzXhyDofkNNZXXPw8LSLvi3vyG6LXo7XvcWv2X4=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
@@ -94,8 +98,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.63.18 h1:Q36nROtbIQhLN6VZ4KJzZkISj4SWlq8pEwtqYBNz0is=
-github.com/aliyun/alibaba-cloud-sdk-go v1.63.18/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
+github.com/aliyun/alibaba-cloud-sdk-go v1.63.77 h1:rSZ5T2oKYfsGGxG5t53zl0Hy2KGpdbNYlNhPXtTIydg=
+github.com/aliyun/alibaba-cloud-sdk-go v1.63.77/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=
@@ -117,44 +121,44 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ
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-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
-github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U=
-github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA=
-github.com/aws/aws-sdk-go-v2/config v1.27.38 h1:mMVyJJuSUdbD4zKXoxDgWrgM60QwlFEg+JhihCq6wCw=
-github.com/aws/aws-sdk-go-v2/config v1.27.38/go.mod h1:6xOiNEn58bj/64MPKx89r6G/el9JZn8pvVbquSqTKK4=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.36 h1:zwI5WrT+oWWfzSKoTNmSyeBKQhsFRJRv+PGW/UZW+Yk=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.36/go.mod h1:3AG/sY1rc9NJrNWcN/3KPU4SIDPGTrd/qegKB0TnFdE=
-github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.7 h1:ZzyrqQfMX4lagelhV90h7QKiKyoVfV7eXTPS3dOX5GY=
-github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.7/go.mod h1:YYffpxyQJqvscSWs4Sh3h0rALEiCePKbaJlw6N+pPy0=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc=
+github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw=
+github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
+github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE=
+github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs=
+github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.22 h1:p2LDiYhvM9mMExEY1meHMAmjmVlzD1J1jVG+fGut+mE=
+github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.15.22/go.mod h1:fo5T2fYMHVF2rHrym50h7Ue/+SECRJlUHUFZLjSX18g=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
-github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.2 h1:EGvR8KwbxUXEUCS4HAgSRcxeFT1/0bqvS5tRR0WZSbM=
-github.com/aws/aws-sdk-go-v2/service/dynamodb v1.35.2/go.mod h1:k5XW8MoMxsNZ20RJmsokakvENUwQyjv69R9GqrI4xdQ=
-github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.23.2 h1:h4sDZaE8OcfPdR5C2m8MEkmQ0PXKYj9BQcYZH6Kc0GQ=
-github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.23.2/go.mod h1:NZQWaOwOszI7jnQ7s1i5kN/FUAglaaJIm2htZG7BJKw=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik=
-github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19 h1:dOxqOlOEa2e2heC/74+ZzcJOa27+F1aXFZpYgY/4QfA=
-github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.19/go.mod h1:aV6U1beLFvk3qAgognjS3wnGGoDId8hlPEiBsLHXVZE=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg=
-github.com/aws/aws-sdk-go-v2/service/route53 v1.44.2 h1:ssvjp8LJrv7x/sPr15E5igCARw00MoIWl54SXZ1FIr0=
-github.com/aws/aws-sdk-go-v2/service/route53 v1.44.2/go.mod h1:l2ABSKg3AibEJeR/l60cfeGU54UqF3VTgd51pq+vYhU=
-github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.32.2 h1:29YTjasLjpAjb9RMacMkwWJ2PgDipZqzDS3TOkqUsl4=
-github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.32.2/go.mod h1:hbMVfSdZneCht4UmPOsejDt93QnetQPFuLOOqbuybqs=
-github.com/aws/aws-sdk-go-v2/service/sso v1.23.2 h1:yzi/y/vKlLyzOfG7pSu5ONNGRxHIgLeDrV4w2AMRCo0=
-github.com/aws/aws-sdk-go-v2/service/sso v1.23.2/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2 h1:3gb6pYhYLjo8rB1h2Tqs61wpjRd3rQymYcVq/pp0yxI=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E=
-github.com/aws/aws-sdk-go-v2/service/sts v1.31.2 h1:O6tyji8mXmBGsHvTCB0VIhrDw19lGTUSbKIyjnw79s8=
-github.com/aws/aws-sdk-go-v2/service/sts v1.31.2/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI=
-github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA=
-github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
+github.com/aws/aws-sdk-go-v2/service/dynamodb v1.38.1 h1:AnSNs7Ogi0LXHPMDBx4RE7imU4/JmzWFziqkMKJA2AY=
+github.com/aws/aws-sdk-go-v2/service/dynamodb v1.38.1/go.mod h1:J8xqRbx7HIc8ids2P8JbrKx9irONPEYq7Z1FpLDpi3I=
+github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.10 h1:aWEbNPNdGiTGSR6/Yy9S0Ad07sMVaT/CFaVq7GuDGx4=
+github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.24.10/go.mod h1:HywkMgYwY0uaybPvvctx6fkm3L1ssRKeGv7TPZ6OQ/M=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
+github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.7 h1:EqGlayejoCRXmnVC6lXl6phCm9R2+k35e0gWsO9G5DI=
+github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.7/go.mod h1:BTw+t+/E5F3ZnDai/wSOYM54WUVjSdewE7Jvwtb7o+w=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0=
+github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 h1:0jMtawybbfpFEIMy4wvfyW2Z4YLr7mnuzT0fhR67Nrc=
+github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4/go.mod h1:xlMODgumb0Pp8bzfpojqelDrf8SL9rb5ovwmwKJl+oU=
+github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.1 h1:d8dH4PATJiEI2yXrEVNBi38osCIm3I3KMYe/tkgykrY=
+github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.34.1/go.mod h1:KmNFSoNNh6qNFUCfNAVf3yW+gZXgEPc//PGttodQ1KU=
+github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g=
+github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc=
+github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
+github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -183,12 +187,12 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
-github.com/civo/civogo v0.3.79 h1:Z1MbEG9CsGqSZV7UFBA0xsjk7TBGUPHjW9sM7cS5yZM=
-github.com/civo/civogo v0.3.79/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM=
+github.com/civo/civogo v0.3.90 h1:HDqws0bPn1pxk0kesbj/N1Vl40UOJV3fTklgVwKWt+g=
+github.com/civo/civogo v0.3.90/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.105.0 h1:yu2IatITLZ4dw7/byzRrlE5DfUvtub0k9CHZ5zBlj90=
-github.com/cloudflare/cloudflare-go v0.105.0/go.mod h1:pfUQ4PIG4ISI0/Mmc21Bp86UnFU0ktmPf3iTgbSL+cM=
+github.com/cloudflare/cloudflare-go v0.113.0 h1:qnOXmA6RbgZ4rg5gNBK5QGk0Pzbv8pnUYV3C4+8CU6w=
+github.com/cloudflare/cloudflare-go v0.113.0/go.mod h1:Dlm4BAnycHc0i8yLxQZb9b+OlMwYOAoDJsUOEFgpVvo=
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=
@@ -250,9 +254,11 @@ github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace h1:1SnCTPFh2AA
github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace/go.mod h1:TK05uvk4XXfK2kdvRwfcZ1NaxjDxmm7H3aQLko0mJxA=
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-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/digitalocean/godo v1.126.0 h1:+Znh7VMQj/E8ArbjWnc7OKGjWfzC+I8OCSRp7r1MdD8=
-github.com/digitalocean/godo v1.126.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
+github.com/digitalocean/godo v1.132.0 h1:n0x6+ZkwbyQBtIU1wwBhv26EINqHg0wWQiBXlwYg/HQ=
+github.com/digitalocean/godo v1.132.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY=
github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8=
@@ -297,8 +303,8 @@ github.com/exoscale/egoscale v0.102.3/go.mod h1:RPf2Gah6up+6kAEayHTQwqapzXlm93f0
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
-github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
+github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -414,8 +420,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.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
-github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
+github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
+github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
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=
@@ -431,8 +437,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.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
-github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
+github.com/goccy/go-json v0.10.4/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=
@@ -459,9 +465,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -474,9 +477,7 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -495,9 +496,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -512,22 +511,21 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
-github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
-github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
+github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
+github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
@@ -605,8 +603,6 @@ github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73t
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-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/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
@@ -654,6 +650,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
+github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
+github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -704,8 +702,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.41.0 h1:GcP7JIBr9iLRJ9FwAtb9/WCT1DuPJS/xUApapfdjtiY=
-github.com/linode/linodego v1.41.0/go.mod h1:Ow4/XZ0yvWBzt3iAHwchvhSx30AyLintsSMvvQ2/SJY=
+github.com/linode/linodego v1.44.0 h1:JZLLWzCAx3CmHSV9NmCoXisuqKtrmPhfY9MrgvaHMUY=
+github.com/linode/linodego v1.44.0/go.mod h1:umdoNOmtbqAdGQbmQnPFZ2YS4US+/mU/1bA7MjoKAvg=
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=
@@ -811,16 +809,16 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0=
-github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA=
+github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
+github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os=
-github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo=
+github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
+github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@@ -849,8 +847,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.75.0 h1:tifYRSqCjxANJb0xnMSZ6N2bF2xGyqcCIMg7xihgk+s=
-github.com/oracle/oci-go-sdk/v65 v65.75.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
+github.com/oracle/oci-go-sdk/v65 v65.81.1 h1:JYc47bk8n/MUchA2KHu1ggsCQzlJZQLJ+tTKfOho00E=
+github.com/oracle/oci-go-sdk/v65 v65.81.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI=
github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
@@ -885,8 +883,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/projectcontour/contour v1.30.0 h1:VrZFHhCD3jd3+dLvLuqrK/T++uKbKvRKM5zjQelUaEM=
-github.com/projectcontour/contour v1.30.0/go.mod h1:64BJdN6uIkGGt3jJ6b3OLLapysgfEUNX/6K8vsaTRIg=
+github.com/projectcontour/contour v1.30.1 h1:JSeQPA6D+g4MHq7O8L2SwrwR3ZmzIJWLvYbdr6A/uSg=
+github.com/projectcontour/contour v1.30.1/go.mod h1:KpBsMbiW1VoGUf2OXbRPkgKHW34eSd0aKv5CAtlDqvg=
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
@@ -894,8 +892,8 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
-github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
-github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
+github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
+github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -926,6 +924,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
+github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -1014,15 +1014,15 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1011 h1:cjqeXTwKKtGVqrf6luwunUnA77buzmzbk+G42US1Sns=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1011/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1011 h1:O3QK5t7HIWhzo9ZtQcWjq+voOL2Anko8ON6bikKTcl0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1011/go.mod h1:HbdUWIghvwUPfIhgFkKZqwaoyALK6GxBzayhflN9wY8=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1011 h1:Ak4EA3TtzcFtqY+2E9z+mYBC8gjlw4X8WXlVPoRVtQU=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1011/go.mod h1:FYFEkG/OCQ4vDPiuV7LijHSNq+kr14ABC1IswVtHNmM=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1074 h1:rlVhKPIEMhod0wm5KymTqBnC9PQIYVobQjV3uPdd2u8=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1074/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1074 h1:5FGTNYiL/S5x9uHAeBOGzyx4jo9gmXLnwMFVoxnJqws=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1074/go.mod h1:CzY5UXmmJjgp5ZmpO9wzLpQBJhdU4gKpKGT/mpKb/QU=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1074 h1:Qnr8VtwWaGz0mmK2Gq5kO//BnIe41kjHBcMjFavvH14=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1074/go.mod h1:2jQxZ8bOqtLNOqIwK8hu8XDbvlTNnX0xzKXV0EAuLKY=
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=
@@ -1049,8 +1049,8 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
-github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8=
-github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww=
+github.com/vektah/gqlparser/v2 v2.5.20 h1:kPaWbhBntxoZPaNdBaIPT1Kh0i1b/onb5kXgEdP5JCo=
+github.com/vektah/gqlparser/v2 v2.5.20/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -1067,7 +1067,6 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
@@ -1075,12 +1074,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.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0=
-go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28=
-go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q=
-go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E=
-go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE=
-go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50=
+go.etcd.io/etcd/api/v3 v3.5.17 h1:cQB8eb8bxwuxOilBpMJAEo8fAONyrdXTHUNcMd8yT1w=
+go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4=
+go.etcd.io/etcd/client/pkg/v3 v3.5.17 h1:XxnDXAWq2pnxqx76ljWwiQ9jylbpC4rvkAeRVOUKKVw=
+go.etcd.io/etcd/client/pkg/v3 v3.5.17/go.mod h1:4DqK1TKacp/86nJk4FLQqo6Mn2vvQFBmruW3pP14H/w=
+go.etcd.io/etcd/client/v3 v3.5.17 h1:o48sINNeWz5+pjy/Z0+HKpj/xSnBkuVhVvXkjEXbqZY=
+go.etcd.io/etcd/client/v3 v3.5.17/go.mod h1:j2d4eXTHWkT2ClBgnnEPm/Wuu7jsqku41v9DZ3OtjQo=
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=
@@ -1091,8 +1090,6 @@ go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
-go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
@@ -1145,10 +1142,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
-golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
+golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
+golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
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=
@@ -1172,10 +1167,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
-golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1205,27 +1198,21 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
-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.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
-golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
-golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
+golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
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=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
-golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
+golang.org/x/oauth2 v0.24.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=
@@ -1235,10 +1222,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180425194835-bb9c189858d9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1287,25 +1272,17 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
-golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.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.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
-golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
-golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
+golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
+golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1313,21 +1290,16 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
-golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
-golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
-golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
+golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1360,10 +1332,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
-golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
+golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
+golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
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=
@@ -1376,8 +1346,8 @@ gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZ
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.199.0 h1:aWUXClp+VFJmqE0JPvpZOK3LDQMyFKYIow4etYd9qxs=
-google.golang.org/api v0.199.0/go.mod h1:ohG4qSztDJmZdjK/Ar6MhbAmb/Rpi4JHOqagsh90K28=
+google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA=
+google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE=
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=
@@ -1391,11 +1361,10 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0=
-google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
+google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 h1:pgr/4QbFyktUv9CtQ/Fq4gzEE6/Xs7iCXbktaGzLHbQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
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=
@@ -1409,23 +1378,19 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
-google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
+google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
+google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
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=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
-google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
+google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1452,8 +1417,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.12.1 h1:GiiZPB8JusUF/ruyUDzddd70b3HZGa5A3njtKUe84jA=
-gopkg.in/ns1/ns1-go.v2 v2.12.1/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
+gopkg.in/ns1/ns1-go.v2 v2.13.0 h1:I5NNqI9Bi1SGK92TVkOvLTwux5LNrix/99H2datVh48=
+gopkg.in/ns1/ns1-go.v2 v2.13.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
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=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
@@ -1481,23 +1446,23 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-istio.io/api v1.23.2 h1:FvWi7GC+rWD60/ZFPuulX/h3k+f2Q9qot3dP8CIL8Ss=
-istio.io/api v1.23.2/go.mod h1:QPSTGXuIQdnZFEm3myf9NZ5uBMwCdJWUvfj9ZZ+2oBM=
-istio.io/client-go v1.23.2 h1:BIt6A+KaUOFin3SzXiDq2Fr/TMBev1+c836R0BfUfhU=
-istio.io/client-go v1.23.2/go.mod h1:E08wpMtUulJk2tlWOCUVakjy1bKFxUNm22tM1R1QY0Y=
+istio.io/api v1.24.2 h1:jYjcN6Iq0RPtQj/3KMFsybxmfqmjGN/dxhL7FGJEdIM=
+istio.io/api v1.24.2/go.mod h1:MQnRok7RZ20/PE56v0LxmoWH0xVxnCQPNuf9O7PAN1I=
+istio.io/client-go v1.24.2 h1:JTTfBV6dv+AAW+AfccyrdX4T1f9CpsXd1Yzo1s/IYAI=
+istio.io/client-go v1.24.2/go.mod h1:dgZ9EmJzh1EECzf6nQhwNL4R6RvlyeH/RXeNeNp/MRg=
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=
-k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
-k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
+k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
+k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo=
k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio=
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
-k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
-k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
+k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
+k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw=
k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8=
@@ -1506,8 +1471,8 @@ k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g
k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g=
-k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
-k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
+k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
+k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
@@ -1525,8 +1490,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
-k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM=
-k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro=
+k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
+k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU=
k8s.io/kubectl v0.18.4/go.mod h1:EzB+nfeUWk6fm6giXQ8P4Fayw3dsN+M7Wjy23mTRtB0=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
@@ -1534,26 +1499,26 @@ k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4=
k8s.io/metrics v0.18.4/go.mod h1:luze4fyI9JG4eLDZy0kFdYEebqNfi0QrG4xNEbPkHOs=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
-k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
+k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A=
-sigs.k8s.io/controller-runtime v0.18.4 h1:87+guW1zhvuPLh1PHybKdYFLU0YJp4FhJRmiHvm5BZw=
-sigs.k8s.io/controller-runtime v0.18.4/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg=
+sigs.k8s.io/controller-runtime v0.18.5 h1:nTHio/W+Q4aBlQMgbnC5hZb4IjIidyrizMai9P6n4Rk=
+sigs.k8s.io/controller-runtime v0.18.5/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg=
sigs.k8s.io/controller-tools v0.3.1-0.20200517180335-820a4a27ea84/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
-sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM=
-sigs.k8s.io/gateway-api v1.1.0/go.mod h1:ZH4lHrL2sDi0FHZ9jjneb8kKnGzFWyrTya35sWUTrRs=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
+sigs.k8s.io/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM=
+sigs.k8s.io/gateway-api v1.2.1/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
+sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
diff --git a/internal/gen/docs/flags/main.go b/internal/gen/docs/flags/main.go
new file mode 100644
index 0000000000..17f6ba1623
--- /dev/null
+++ b/internal/gen/docs/flags/main.go
@@ -0,0 +1,115 @@
+/*
+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 main
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "strings"
+ "text/template"
+
+ cfg "sigs.k8s.io/external-dns/pkg/apis/externaldns"
+)
+
+type Flag struct {
+ Name string
+ Description string
+}
+type Flags []Flag
+
+// AddFlag adds a new flag to the Flags struct
+func (f *Flags) addFlag(name, description string) {
+ *f = append(*f, Flag{Name: name, Description: description})
+}
+
+const markdownTemplate = `# Flags
+
+
+
+
+| Flag | Description |
+| :------ | :----------- |
+{{- range . }}
+| {{ .Name }} | {{ .Description }} | {{- end -}}
+`
+
+// It generates a markdown file
+// with the supported flags and writes it to the 'docs/flags.md' file.
+// to re-generate `docs/flags.md` execute 'go run internal/gen/main.go'
+func main() {
+ testPath, _ := os.Getwd()
+ path := fmt.Sprintf("%s/docs/flags.md", testPath)
+ fmt.Printf("generate file '%s' with supported flags\n", path)
+
+ flags := computeFlags()
+ content, err := flags.generateMarkdownTable()
+ if err != nil {
+ _ = fmt.Errorf("failed to generate markdown file '%s': %v\n", path, err.Error())
+ }
+ _ = writeToFile(path, content)
+}
+
+func computeFlags() Flags {
+ app := cfg.App(&cfg.Config{})
+ modelFlags := app.Model().Flags
+
+ flags := Flags{}
+
+ for _, flag := range modelFlags {
+ // do not include helpers and completion flags
+ if strings.Contains(flag.Name, "help") || strings.Contains(flag.Name, "completion-") {
+ continue
+ }
+ flagString := ""
+ flagName := flag.Name
+ if flag.IsBoolFlag() {
+ flagName = "[no-]" + flagName
+ }
+ flagString += fmt.Sprintf("--%s", flagName)
+
+ if !flag.IsBoolFlag() {
+ flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder())
+ }
+ flags.addFlag(fmt.Sprintf("`%s`", flagString), flag.HelpWithEnvar())
+ }
+ return flags
+}
+
+func (f *Flags) generateMarkdownTable() (string, error) {
+ tmpl := template.Must(template.New("flags.md.tpl").Parse(markdownTemplate))
+
+ var b bytes.Buffer
+ err := tmpl.Execute(&b, f)
+ if err != nil {
+ return "", err
+ }
+ return b.String(), nil
+}
+
+func writeToFile(filename string, content string) error {
+ file, fileErr := os.Create(filename)
+ if fileErr != nil {
+ _ = fmt.Errorf("failed to create file: %v", fileErr)
+ }
+ defer file.Close()
+ _, writeErr := file.WriteString(content)
+ if writeErr != nil {
+ _ = fmt.Errorf("failed to write to file: %s", filename)
+ }
+ return nil
+}
diff --git a/internal/gen/docs/flags/main_test.go b/internal/gen/docs/flags/main_test.go
new file mode 100644
index 0000000000..a3a72e819d
--- /dev/null
+++ b/internal/gen/docs/flags/main_test.go
@@ -0,0 +1,92 @@
+/*
+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 main
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+const pathToDocs = "%s/../../../../docs"
+
+func TestComputeFlags(t *testing.T) {
+ flags := computeFlags()
+
+ if len(flags) == 0 {
+ t.Errorf("Expected non-zero flags, got %d", len(flags))
+ }
+
+ for _, flag := range flags {
+ if strings.Contains(flag.Name, "help") || strings.Contains(flag.Name, "completion-") {
+ t.Errorf("Unexpected flag: %s", flag.Name)
+ }
+ }
+}
+
+func TestGenerateMarkdownTableRenderer(t *testing.T) {
+ flags := Flags{
+ {Name: "flag1", Description: "description1"},
+ }
+
+ got, err := flags.generateMarkdownTable()
+ assert.NoError(t, err)
+
+ assert.Contains(t, got, "")
+ assert.Contains(t, got, "| flag1 | description1 |")
+}
+
+func TestFlagsMdExists(t *testing.T) {
+ testPath, _ := os.Getwd()
+ fsys := os.DirFS(fmt.Sprintf(pathToDocs, testPath))
+ fileName := "flags.md"
+ st, err := fs.Stat(fsys, fileName)
+ assert.NoError(t, err, "expected file %s to exist", fileName)
+ assert.Equal(t, fileName, st.Name())
+}
+
+func TestFlagsMdUpToDate(t *testing.T) {
+ testPath, _ := os.Getwd()
+ fsys := os.DirFS(fmt.Sprintf(pathToDocs, testPath))
+ fileName := "flags.md"
+ expected, err := fs.ReadFile(fsys, fileName)
+ assert.NoError(t, err, "expected file %s to exist", fileName)
+
+ flags := computeFlags()
+ actual, err := flags.generateMarkdownTable()
+ assert.NoError(t, err)
+ assert.True(t, len(expected) == len(actual), "expected file '%s' to be up to date. execute 'make generate-flags-documentation", fileName)
+}
+
+func TestFlagsMdExtraFlagAdded(t *testing.T) {
+ testPath, _ := os.Getwd()
+ fsys := os.DirFS(fmt.Sprintf(pathToDocs, testPath))
+ filePath := "flags.md"
+ expected, err := fs.ReadFile(fsys, filePath)
+ assert.NoError(t, err, "expected file %s to exist", filePath)
+
+ flags := computeFlags()
+ flags.addFlag("new-flag", "description2")
+ actual, err := flags.generateMarkdownTable()
+
+ assert.NoError(t, err)
+ assert.NotEqual(t, string(expected), actual)
+}
diff --git a/kustomize/kustomization.yaml b/kustomize/kustomization.yaml
index 679d569744..0632b35e12 100644
--- a/kustomize/kustomization.yaml
+++ b/kustomize/kustomization.yaml
@@ -3,7 +3,7 @@ kind: Kustomization
images:
- name: registry.k8s.io/external-dns/external-dns
- newTag: v0.15.0
+ newTag: v0.15.1
resources:
- ./external-dns-deployment.yaml
diff --git a/main.go b/main.go
index 619d25fcee..7d32da4a94 100644
--- a/main.go
+++ b/main.go
@@ -65,7 +65,6 @@ import (
"sigs.k8s.io/external-dns/provider/pdns"
"sigs.k8s.io/external-dns/provider/pihole"
"sigs.k8s.io/external-dns/provider/plural"
- "sigs.k8s.io/external-dns/provider/rdns"
"sigs.k8s.io/external-dns/provider/rfc2136"
"sigs.k8s.io/external-dns/provider/scaleway"
"sigs.k8s.io/external-dns/provider/tencentcloud"
@@ -101,10 +100,12 @@ func main() {
}
log.SetLevel(ll)
- // Klog V2 is used by k8s.io/apimachinery/pkg/labels and can throw (a lot) of irrelevant logs
- // See https://github.com/kubernetes-sigs/external-dns/issues/2348
- defer klog.ClearLogger()
- klog.SetLogger(logr.Discard())
+ if ll < log.DebugLevel {
+ // Klog V2 is used by k8s.io/apimachinery/pkg/labels and can throw (a lot) of irrelevant logs
+ // See https://github.com/kubernetes-sigs/external-dns/issues/2348
+ defer klog.ClearLogger()
+ klog.SetLogger(logr.Discard())
+ }
ctx, cancel := context.WithCancel(context.Background())
@@ -235,17 +236,17 @@ func main() {
log.Infof("Registry \"%s\" cannot be used with AWS Cloud Map. Switching to \"aws-sd\".", cfg.Registry)
cfg.Registry = "aws-sd"
}
- p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg)))
+ p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, cfg.AWSSDCreateTag, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg)))
case "azure-dns", "azure":
- p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.DryRun)
+ p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.DryRun)
case "azure-private-dns":
- p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.DryRun)
+ p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.DryRun)
case "ultradns":
p, err = ultradns.NewUltraDNSProvider(domainFilter, cfg.DryRun)
case "civo":
p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun)
case "cloudflare":
- p, err = cloudflare.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareProxied, cfg.DryRun, cfg.CloudflareDNSRecordsPerPage)
+ p, err = cloudflare.NewCloudFlareProvider(domainFilter, zoneIDFilter, cfg.CloudflareProxied, cfg.DryRun, cfg.CloudflareDNSRecordsPerPage, cfg.CloudflareRegionKey)
case "google":
p, err = google.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.GoogleZoneVisibility, cfg.DryRun)
case "digitalocean":
@@ -258,13 +259,6 @@ func main() {
p, err = dnsimple.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
case "coredns", "skydns":
p, err = coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.DryRun)
- case "rdns":
- p, err = rdns.NewRDNSProvider(
- rdns.RDNSConfig{
- DomainFilter: domainFilter,
- DryRun: cfg.DryRun,
- },
- )
case "exoscale":
p, err = exoscale.NewExoscaleProvider(
cfg.ExoscaleAPIEnvironment,
diff --git a/mkdocs.yml b/mkdocs.yml
index b96fa998e0..e7bb2566c2 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -12,6 +12,7 @@ nav:
- Changelog: charts/external-dns/CHANGELOG.md
- About:
- FAQ: docs/faq.md
+ - Flags: docs/flags.md
- Out of Incubator: docs/20190708-external-dns-incubator.md
- Code of Conduct: code-of-conduct.md
- License: LICENSE.md
diff --git a/pkg/apis/externaldns/types.go b/pkg/apis/externaldns/types.go
index 9155bccd3f..393f97d2a2 100644
--- a/pkg/apis/externaldns/types.go
+++ b/pkg/apis/externaldns/types.go
@@ -86,7 +86,7 @@ type Config struct {
AWSZoneTagFilter []string
AWSAssumeRole string
AWSProfiles []string
- AWSAssumeRoleExternalID string
+ AWSAssumeRoleExternalID string `secure:"yes"`
AWSBatchChangeSize int
AWSBatchChangeSizeBytes int
AWSBatchChangeSizeValues int
@@ -96,6 +96,7 @@ type Config struct {
AWSPreferCNAME bool
AWSZoneCacheDuration time.Duration
AWSSDServiceCleanup bool
+ AWSSDCreateTag map[string]string
AWSZoneMatchParent bool
AWSDynamoDBRegion string
AWSDynamoDBTable string
@@ -104,8 +105,10 @@ type Config struct {
AzureSubscriptionID string
AzureUserAssignedIdentityClientID string
AzureActiveDirectoryAuthorityHost string
+ AzureZonesCacheDuration time.Duration
CloudflareProxied bool
CloudflareDNSRecordsPerPage int
+ CloudflareRegionKey string
CoreDNSPrefix string
AkamaiServiceConsumerDomain string
AkamaiClientToken string
@@ -256,13 +259,16 @@ var defaultConfig = &Config{
AWSPreferCNAME: false,
AWSZoneCacheDuration: 0 * time.Second,
AWSSDServiceCleanup: false,
+ AWSSDCreateTag: map[string]string{},
AWSDynamoDBRegion: "",
AWSDynamoDBTable: "external-dns",
AzureConfigFile: "/etc/kubernetes/azure.json",
AzureResourceGroup: "",
AzureSubscriptionID: "",
+ AzureZonesCacheDuration: 0 * time.Second,
CloudflareProxied: false,
CloudflareDNSRecordsPerPage: 100,
+ CloudflareRegionKey: "earth",
CoreDNSPrefix: "/skydns/",
AkamaiServiceConsumerDomain: "",
AkamaiClientToken: "",
@@ -357,7 +363,9 @@ var defaultConfig = &Config{
// NewConfig returns new Config object
func NewConfig() *Config {
- return &Config{}
+ return &Config{
+ AWSSDCreateTag: map[string]string{},
+ }
}
func (cfg *Config) String() string {
@@ -392,6 +400,17 @@ func allLogLevelsAsStrings() []string {
// ParseFlags adds and parses flags from command line
func (cfg *Config) ParseFlags(args []string) error {
+ app := App(cfg)
+
+ _, err := app.Parse(args)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func App(cfg *Config) *kingpin.Application {
app := kingpin.New("external-dns", "ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.\n\nNote that all flags may be replaced with env vars - `--flag` -> `EXTERNAL_DNS_FLAG=1` or `--flag value` -> `EXTERNAL_DNS_FLAG=value`")
app.Version(Version)
app.DefaultEnvars()
@@ -445,7 +464,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("nat64-networks", "Adding an A record for each AAAA record in NAT64-enabled networks; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.NAT64Networks)
// Flags related to providers
- providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "designate", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rdns", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "webhook"}
+ providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "designate", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "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("provider-cache-time", "The time to cache the DNS provider record list requests.").Default(defaultConfig.ProviderCacheTime.String()).DurationVar(&cfg.ProviderCacheTime)
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
@@ -475,15 +494,18 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("aws-zones-cache-duration", "When using the AWS provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AWSZoneCacheDuration.String()).DurationVar(&cfg.AWSZoneCacheDuration)
app.Flag("aws-zone-match-parent", "Expand limit possible target by sub-domains (default: disabled)").BoolVar(&cfg.AWSZoneMatchParent)
app.Flag("aws-sd-service-cleanup", "When using the AWS CloudMap provider, delete empty Services without endpoints (default: disabled)").BoolVar(&cfg.AWSSDServiceCleanup)
+ app.Flag("aws-sd-create-tag", "When using the AWS CloudMap provider, add tag to created services. The flag can be used multiple times").StringMapVar(&cfg.AWSSDCreateTag)
app.Flag("azure-config-file", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure)").Default(defaultConfig.AzureConfigFile).StringVar(&cfg.AzureConfigFile)
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (optional)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
app.Flag("azure-subscription-id", "When using the Azure provider, override the Azure subscription to use (optional)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID)
app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID)
+ app.Flag("azure-zones-cache-duration", "When using the Azure provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.AzureZonesCacheDuration.String()).DurationVar(&cfg.AzureZonesCacheDuration)
app.Flag("tencent-cloud-config-file", "When using the Tencent Cloud provider, specify the Tencent Cloud configuration file (required when --provider=tencentcloud)").Default(defaultConfig.TencentCloudConfigFile).StringVar(&cfg.TencentCloudConfigFile)
app.Flag("tencent-cloud-zone-type", "When using the Tencent Cloud provider, filter for zones with visibility (optional, options: public, private)").Default(defaultConfig.TencentCloudZoneType).EnumVar(&cfg.TencentCloudZoneType, "", "public", "private")
app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied)
app.Flag("cloudflare-dns-records-per-page", "When using the Cloudflare provider, specify how many DNS records listed per page, max possible 5,000 (default: 100)").Default(strconv.Itoa(defaultConfig.CloudflareDNSRecordsPerPage)).IntVar(&cfg.CloudflareDNSRecordsPerPage)
+ app.Flag("cloudflare-region-key", "When using the Cloudflare provider, specify the region (default: earth)").StringVar(&cfg.CloudflareRegionKey)
app.Flag("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix)
app.Flag("akamai-serviceconsumerdomain", "When using the Akamai provider, specify the base URL (required when --provider=akamai and edgerc-path not specified)").Default(defaultConfig.AkamaiServiceConsumerDomain).StringVar(&cfg.AkamaiServiceConsumerDomain)
app.Flag("akamai-client-token", "When using the Akamai provider, specify the client token (required when --provider=akamai and edgerc-path not specified)").Default(defaultConfig.AkamaiClientToken).StringVar(&cfg.AkamaiClientToken)
@@ -592,10 +614,5 @@ func (cfg *Config) ParseFlags(args []string) error {
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 {
- return err
- }
-
- return nil
+ return app
}
diff --git a/pkg/apis/externaldns/types_test.go b/pkg/apis/externaldns/types_test.go
index 70b959b561..ab77cc9ec8 100644
--- a/pkg/apis/externaldns/types_test.go
+++ b/pkg/apis/externaldns/types_test.go
@@ -68,12 +68,14 @@ var (
AWSProfiles: []string{""},
AWSZoneCacheDuration: 0 * time.Second,
AWSSDServiceCleanup: false,
+ AWSSDCreateTag: map[string]string{},
AWSDynamoDBTable: "external-dns",
AzureConfigFile: "/etc/kubernetes/azure.json",
AzureResourceGroup: "",
AzureSubscriptionID: "",
CloudflareProxied: false,
CloudflareDNSRecordsPerPage: 100,
+ CloudflareRegionKey: "",
CoreDNSPrefix: "/skydns/",
AkamaiServiceConsumerDomain: "",
AkamaiClientToken: "",
@@ -167,12 +169,14 @@ var (
AWSProfiles: []string{"profile1", "profile2"},
AWSZoneCacheDuration: 10 * time.Second,
AWSSDServiceCleanup: true,
+ AWSSDCreateTag: map[string]string{"key1": "value1", "key2": "value2"},
AWSDynamoDBTable: "custom-table",
AzureConfigFile: "azure.json",
AzureResourceGroup: "arg",
AzureSubscriptionID: "arg",
CloudflareProxied: true,
CloudflareDNSRecordsPerPage: 5000,
+ CloudflareRegionKey: "us",
CoreDNSPrefix: "/coredns/",
AkamaiServiceConsumerDomain: "oooo-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net",
AkamaiClientToken: "o184671d5307a388180fbf7f11dbdf46",
@@ -275,6 +279,7 @@ func TestParseFlags(t *testing.T) {
"--azure-subscription-id=arg",
"--cloudflare-proxied",
"--cloudflare-dns-records-per-page=5000",
+ "--cloudflare-region-key=us",
"--coredns-prefix=/coredns/",
"--akamai-serviceconsumerdomain=oooo-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net",
"--akamai-client-token=o184671d5307a388180fbf7f11dbdf46",
@@ -325,6 +330,8 @@ func TestParseFlags(t *testing.T) {
"--aws-profile=profile2",
"--aws-zones-cache-duration=10s",
"--aws-sd-service-cleanup",
+ "--aws-sd-create-tag=key1=value1",
+ "--aws-sd-create-tag=key2=value2",
"--no-aws-evaluate-target-health",
"--policy=upsert-only",
"--registry=noop",
@@ -392,6 +399,7 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_AZURE_SUBSCRIPTION_ID": "arg",
"EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1",
"EXTERNAL_DNS_CLOUDFLARE_DNS_RECORDS_PER_PAGE": "5000",
+ "EXTERNAL_DNS_CLOUDFLARE_REGION_KEY": "us",
"EXTERNAL_DNS_COREDNS_PREFIX": "/coredns/",
"EXTERNAL_DNS_AKAMAI_SERVICECONSUMERDOMAIN": "oooo-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net",
"EXTERNAL_DNS_AKAMAI_CLIENT_TOKEN": "o184671d5307a388180fbf7f11dbdf46",
@@ -436,6 +444,7 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_AWS_PROFILE": "profile1\nprofile2",
"EXTERNAL_DNS_AWS_ZONES_CACHE_DURATION": "10s",
"EXTERNAL_DNS_AWS_SD_SERVICE_CLEANUP": "true",
+ "EXTERNAL_DNS_AWS_SD_CREATE_TAG": "key1=value1\nkey2=value2",
"EXTERNAL_DNS_DYNAMODB_TABLE": "custom-table",
"EXTERNAL_DNS_POLICY": "upsert-only",
"EXTERNAL_DNS_REGISTRY": "noop",
@@ -504,8 +513,8 @@ func restoreEnv(t *testing.T, originalEnv map[string]string) {
func TestPasswordsNotLogged(t *testing.T) {
cfg := Config{
- PDNSAPIKey: "pdns-api-key",
- RFC2136TSIGSecret: "tsig-secret",
+ PDNSAPIKey: "pdns-api-key",
+ RFC2136TSIGSecret: "tsig-secret",
}
s := cfg.String()
diff --git a/plan/plan.go b/plan/plan.go
index d0ca7f96a2..6124b76406 100644
--- a/plan/plan.go
+++ b/plan/plan.go
@@ -240,6 +240,10 @@ func (p *Plan) Calculate() *Plan {
if ownersMatch {
changes.Create = append(changes.Create, creates...)
+ } else if log.GetLevel() == log.DebugLevel {
+ for _, current := range row.current {
+ log.Debugf(`Skipping endpoint %v because owner id does not match for one or more items to create, found: "%s", required: "%s"`, current, current.Labels[endpoint.OwnerLabelKey], p.OwnerID)
+ }
}
}
}
diff --git a/provider/aws/aws.go b/provider/aws/aws.go
index f4ad9bdeaa..db1ce06c16 100644
--- a/provider/aws/aws.go
+++ b/provider/aws/aws.go
@@ -82,6 +82,7 @@ var canonicalHostedZones = map[string]string{
"ap-southeast-2.elb.amazonaws.com": "Z1GM3OXH4ZPM65",
"ap-southeast-3.elb.amazonaws.com": "Z08888821HLRG5A9ZRTER",
"ap-southeast-4.elb.amazonaws.com": "Z09517862IB2WZLPXG76F",
+ "ap-southeast-5.elb.amazonaws.com": "Z06010284QMVVW7WO5J",
"ap-northeast-1.elb.amazonaws.com": "Z14GRHDCWA56QT",
"eu-central-1.elb.amazonaws.com": "Z215JYRZR1TBD5",
"eu-central-2.elb.amazonaws.com": "Z06391101F2ZOEP8P5EB3",
@@ -116,6 +117,7 @@ var canonicalHostedZones = map[string]string{
"elb.ap-southeast-2.amazonaws.com": "ZCT6FZBF4DROD",
"elb.ap-southeast-3.amazonaws.com": "Z01971771FYVNCOVWJU1G",
"elb.ap-southeast-4.amazonaws.com": "Z01156963G8MIIL7X90IV",
+ "elb.ap-southeast-5.amazonaws.com": "Z026317210H9ACVTRO6FB",
"elb.ap-northeast-1.amazonaws.com": "Z31USIVHYNEOWT",
"elb.eu-central-1.amazonaws.com": "Z3F0SRJ5LGBH90",
"elb.eu-central-2.amazonaws.com": "Z02239872DOALSIDCX66S",
@@ -445,7 +447,7 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*profiledZon
for paginator.HasMorePages() {
resp, err := paginator.NextPage(ctx)
if err != nil {
- return nil, fmt.Errorf("failed to list resource records sets for zone %s using aws profile %q: %w", *z.zone.Id, z.profile, err)
+ return nil, provider.NewSoftError(fmt.Errorf("failed to list resource records sets for zone %s using aws profile %q: %w", *z.zone.Id, z.profile, err))
}
for _, r := range resp.ResourceRecordSets {
diff --git a/provider/aws/aws_test.go b/provider/aws/aws_test.go
index 4681aff281..12d9f6940d 100644
--- a/provider/aws/aws_test.go
+++ b/provider/aws/aws_test.go
@@ -81,6 +81,10 @@ func NewRoute53APIStub(t *testing.T) *Route53APIStub {
}
func (r *Route53APIStub) ListResourceRecordSets(ctx context.Context, input *route53.ListResourceRecordSetsInput, optFns ...func(options *route53.Options)) (*route53.ListResourceRecordSetsOutput, error) {
+ if r.m.isMocked("ListResourceRecordSets", input) {
+ return r.m.ListResourceRecordSets(ctx, input, optFns...)
+ }
+
output := &route53.ListResourceRecordSetsOutput{} // TODO: Support optional input args.
require.NotNil(r.t, input.MaxItems)
assert.EqualValues(r.t, route53PageSize, *input.MaxItems)
@@ -254,6 +258,14 @@ type dynamicMock struct {
mock.Mock
}
+func (m *dynamicMock) ListResourceRecordSets(ctx context.Context, input *route53.ListResourceRecordSetsInput, optFns ...func(options *route53.Options)) (*route53.ListResourceRecordSetsOutput, error) {
+ args := m.Called(input)
+ if args.Get(0) != nil {
+ return args.Get(0).(*route53.ListResourceRecordSetsOutput), args.Error(1)
+ }
+ return nil, args.Error(1)
+}
+
func (m *dynamicMock) ChangeResourceRecordSets(input *route53.ChangeResourceRecordSetsInput) (*route53.ChangeResourceRecordSetsOutput, error) {
args := m.Called(input)
if args.Get(0) != nil {
@@ -557,6 +569,22 @@ func TestAWSRecords(t *testing.T) {
})
}
+func TestAWSRecordsSoftError(t *testing.T) {
+ pvd, subClient := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), false, false, []route53types.ResourceRecordSet{
+ {
+ Name: aws.String("list-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
+ Type: route53types.RRTypeA,
+ TTL: aws.Int64(recordTTL),
+ ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
+ },
+ })
+
+ subClient.MockMethod("ListResourceRecordSets", mock.Anything).Return(nil, fmt.Errorf("Mock route53 failure"))
+ _, err := pvd.Records(context.Background())
+ require.Error(t, err)
+ require.ErrorIs(t, err, provider.SoftError)
+}
+
func TestAWSAdjustEndpoints(t *testing.T) {
provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.teapot.zalan.do."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil)
diff --git a/provider/aws/config.go b/provider/aws/config.go
index bbfca9e97d..5908150e77 100644
--- a/provider/aws/config.go
+++ b/provider/aws/config.go
@@ -102,7 +102,8 @@ func newV2Config(awsConfig AWSSessionConfig) (awsv2.Config, error) {
stsSvc := sts.NewFromConfig(cfg)
var assumeRoleOpts []func(*stscredsv2.AssumeRoleOptions)
if awsConfig.AssumeRoleExternalID != "" {
- logrus.Infof("Assuming role: %s with external id %s", awsConfig.AssumeRole, awsConfig.AssumeRoleExternalID)
+ logrus.Infof("Assuming role %s with external id", awsConfig.AssumeRole)
+ logrus.Debugf("External id: %s", awsConfig.AssumeRoleExternalID)
assumeRoleOpts = []func(*stscredsv2.AssumeRoleOptions){
func(opts *stscredsv2.AssumeRoleOptions) {
opts.ExternalID = &awsConfig.AssumeRoleExternalID
diff --git a/provider/awssd/aws_sd.go b/provider/awssd/aws_sd.go
index 14a9778735..babe485933 100644
--- a/provider/awssd/aws_sd.go
+++ b/provider/awssd/aws_sd.go
@@ -41,6 +41,7 @@ const (
sdNamespaceTypePrivate = "private"
sdInstanceAttrIPV4 = "AWS_INSTANCE_IPV4"
+ sdInstanceAttrIPV6 = "AWS_INSTANCE_IPV6"
sdInstanceAttrCname = "AWS_INSTANCE_CNAME"
sdInstanceAttrAlias = "AWS_ALIAS_DNS_NAME"
)
@@ -79,10 +80,12 @@ type AWSSDProvider struct {
cleanEmptyService bool
// filter services for removal
ownerID string
+ // tags to be added to the service
+ tags []sdtypes.Tag
}
// NewAWSSDProvider initializes a new AWS Cloud Map based Provider.
-func NewAWSSDProvider(domainFilter endpoint.DomainFilter, namespaceType string, dryRun, cleanEmptyService bool, ownerID string, client AWSSDClient) (*AWSSDProvider, error) {
+func NewAWSSDProvider(domainFilter endpoint.DomainFilter, namespaceType string, dryRun, cleanEmptyService bool, ownerID string, tags map[string]string, client AWSSDClient) (*AWSSDProvider, error) {
p := &AWSSDProvider{
client: client,
dryRun: dryRun,
@@ -90,6 +93,7 @@ func NewAWSSDProvider(domainFilter endpoint.DomainFilter, namespaceType string,
namespaceTypeFilter: newSdNamespaceFilter(namespaceType),
cleanEmptyService: cleanEmptyService,
ownerID: ownerID,
+ tags: awsTags(tags),
}
return p, nil
@@ -113,6 +117,15 @@ func newSdNamespaceFilter(namespaceTypeConfig string) sdtypes.NamespaceFilter {
}
}
+// awsTags converts user supplied tags to AWS format
+func awsTags(tags map[string]string) []sdtypes.Tag {
+ awsTags := make([]sdtypes.Tag, 0, len(tags))
+ for k, v := range tags {
+ awsTags = append(awsTags, sdtypes.Tag{Key: aws.String(k), Value: aws.String(v)})
+ }
+ return awsTags
+}
+
// Records returns list of all endpoints.
func (p *AWSSDProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpoint, err error) {
namespaces, err := p.ListNamespaces(ctx)
@@ -174,10 +187,15 @@ func (p *AWSSDProvider) instancesToEndpoint(ns *sdtypes.NamespaceSummary, srv *s
newEndpoint.RecordType = endpoint.RecordTypeCNAME
newEndpoint.Targets = append(newEndpoint.Targets, inst.Attributes[sdInstanceAttrAlias])
- // IP-based target
+ // IPv4-based target
} else if inst.Attributes[sdInstanceAttrIPV4] != "" {
newEndpoint.RecordType = endpoint.RecordTypeA
newEndpoint.Targets = append(newEndpoint.Targets, inst.Attributes[sdInstanceAttrIPV4])
+
+ // IPv6-based target
+ } else if inst.Attributes[sdInstanceAttrIPV6] != "" {
+ newEndpoint.RecordType = endpoint.RecordTypeAAAA
+ newEndpoint.Targets = append(newEndpoint.Targets, inst.Attributes[sdInstanceAttrIPV6])
} else {
log.Warnf("Invalid instance \"%v\" found in service \"%v\"", inst, srv.Name)
}
@@ -400,6 +418,7 @@ func (p *AWSSDProvider) CreateService(ctx context.Context, namespaceID *string,
}},
},
NamespaceId: namespaceID,
+ Tags: p.tags,
})
if err != nil {
return nil, err
@@ -472,15 +491,18 @@ func (p *AWSSDProvider) RegisterInstance(ctx context.Context, service *sdtypes.S
attr := make(map[string]string)
- if ep.RecordType == endpoint.RecordTypeCNAME {
+ switch ep.RecordType {
+ case endpoint.RecordTypeCNAME:
if p.isAWSLoadBalancer(target) {
attr[sdInstanceAttrAlias] = target
} else {
attr[sdInstanceAttrCname] = target
}
- } else if ep.RecordType == endpoint.RecordTypeA {
+ case endpoint.RecordTypeA:
attr[sdInstanceAttrIPV4] = target
- } else {
+ case endpoint.RecordTypeAAAA:
+ attr[sdInstanceAttrIPV6] = target
+ default:
return fmt.Errorf("invalid endpoint type (%v)", ep)
}
@@ -584,16 +606,17 @@ func (p *AWSSDProvider) parseHostname(hostname string) (namespace string, servic
// determine service routing policy based on endpoint type
func (p *AWSSDProvider) routingPolicyFromEndpoint(ep *endpoint.Endpoint) sdtypes.RoutingPolicy {
- if ep.RecordType == endpoint.RecordTypeA {
+ if ep.RecordType == endpoint.RecordTypeA || ep.RecordType == endpoint.RecordTypeAAAA {
return sdtypes.RoutingPolicyMultivalue
}
return sdtypes.RoutingPolicyWeighted
}
-// determine service type (A, CNAME) from given endpoint
+// determine service type (A, AAAA, CNAME) from given endpoint
func (p *AWSSDProvider) serviceTypeFromEndpoint(ep *endpoint.Endpoint) sdtypes.RecordType {
- if ep.RecordType == endpoint.RecordTypeCNAME {
+ switch ep.RecordType {
+ case endpoint.RecordTypeCNAME:
// FIXME service type is derived from the first target only. Theoretically this may be problem.
// But I don't see a scenario where one endpoint contains targets of different types.
if p.isAWSLoadBalancer(ep.Targets[0]) {
@@ -601,8 +624,11 @@ func (p *AWSSDProvider) serviceTypeFromEndpoint(ep *endpoint.Endpoint) sdtypes.R
return sdtypes.RecordTypeA
}
return sdtypes.RecordTypeCname
+ case endpoint.RecordTypeAAAA:
+ return sdtypes.RecordTypeAaaa
+ default:
+ return sdtypes.RecordTypeA
}
- return sdtypes.RecordTypeA
}
// determine if a given hostname belongs to an AWS load balancer
diff --git a/provider/awssd/aws_sd_test.go b/provider/awssd/aws_sd_test.go
index 2cc5431436..38cc0711ba 100644
--- a/provider/awssd/aws_sd_test.go
+++ b/provider/awssd/aws_sd_test.go
@@ -307,6 +307,19 @@ func TestAWSSDProvider_Records(t *testing.T) {
}},
},
},
+ "aaaa-srv": {
+ Id: aws.String("aaaa-srv"),
+ Name: aws.String("service4"),
+ Description: aws.String("owner-id"),
+ DnsConfig: &sdtypes.DnsConfig{
+ NamespaceId: aws.String("private"),
+ RoutingPolicy: sdtypes.RoutingPolicyWeighted,
+ DnsRecords: []sdtypes.DnsRecord{{
+ Type: sdtypes.RecordTypeAaaa,
+ TTL: aws.Int64(100),
+ }},
+ },
+ },
},
}
@@ -341,12 +354,21 @@ func TestAWSSDProvider_Records(t *testing.T) {
},
},
},
+ "aaaa-srv": {
+ "0000:0000:0000:0000:abcd:abcd:abcd:abcd": {
+ Id: aws.String("0000:0000:0000:0000:abcd:abcd:abcd:abcd"),
+ Attributes: map[string]string{
+ sdInstanceAttrIPV6: "0000:0000:0000:0000:abcd:abcd:abcd:abcd",
+ },
+ },
+ },
}
expectedEndpoints := []*endpoint.Endpoint{
{DNSName: "service1.private.com", Targets: endpoint.Targets{"1.2.3.4", "1.2.3.5"}, RecordType: endpoint.RecordTypeA, RecordTTL: 100, Labels: map[string]string{endpoint.AWSSDDescriptionLabel: "owner-id"}},
{DNSName: "service2.private.com", Targets: endpoint.Targets{"load-balancer.us-east-1.elb.amazonaws.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 100, Labels: map[string]string{endpoint.AWSSDDescriptionLabel: "owner-id"}},
{DNSName: "service3.private.com", Targets: endpoint.Targets{"cname.target.com"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 80, Labels: map[string]string{endpoint.AWSSDDescriptionLabel: "owner-id"}},
+ {DNSName: "service4.private.com", Targets: endpoint.Targets{"0000:0000:0000:0000:abcd:abcd:abcd:abcd"}, RecordType: endpoint.RecordTypeAAAA, RecordTTL: 100, Labels: map[string]string{endpoint.AWSSDDescriptionLabel: "owner-id"}},
}
api := &AWSSDClientStub{
@@ -557,6 +579,28 @@ func TestAWSSDProvider_CreateService(t *testing.T) {
NamespaceId: aws.String("private"),
}
+ // AAAA type
+ provider.CreateService(context.Background(), aws.String("private"), aws.String("AAAA-srv"), &endpoint.Endpoint{
+ Labels: map[string]string{
+ endpoint.AWSSDDescriptionLabel: "AAAA-srv",
+ },
+ RecordType: endpoint.RecordTypeAAAA,
+ RecordTTL: 60,
+ Targets: endpoint.Targets{"::1234:5678:"},
+ })
+ expectedServices["AAAA-srv"] = &sdtypes.Service{
+ Name: aws.String("AAAA-srv"),
+ Description: aws.String("AAAA-srv"),
+ DnsConfig: &sdtypes.DnsConfig{
+ RoutingPolicy: sdtypes.RoutingPolicyMultivalue,
+ DnsRecords: []sdtypes.DnsRecord{{
+ Type: sdtypes.RecordTypeAaaa,
+ TTL: aws.Int64(60),
+ }},
+ },
+ NamespaceId: aws.String("private"),
+ }
+
// CNAME type
provider.CreateService(context.Background(), aws.String("private"), aws.String("CNAME-srv"), &endpoint.Endpoint{
Labels: map[string]string{
@@ -768,6 +812,19 @@ func TestAWSSDProvider_RegisterInstance(t *testing.T) {
}},
},
},
+ "aaaa-srv": {
+ Id: aws.String("aaaa-srv"),
+ Name: aws.String("service4"),
+ Description: aws.String("owner-id"),
+ DnsConfig: &sdtypes.DnsConfig{
+ NamespaceId: aws.String("private"),
+ RoutingPolicy: sdtypes.RoutingPolicyWeighted,
+ DnsRecords: []sdtypes.DnsRecord{{
+ Type: sdtypes.RecordTypeAaaa,
+ TTL: aws.Int64(100),
+ }},
+ },
+ },
},
}
@@ -781,7 +838,7 @@ func TestAWSSDProvider_RegisterInstance(t *testing.T) {
expectedInstances := make(map[string]*sdtypes.Instance)
- // IP-based instance
+ // IPv4-based instance
provider.RegisterInstance(context.Background(), services["private"]["a-srv"], &endpoint.Endpoint{
RecordType: endpoint.RecordTypeA,
DNSName: "service1.private.com.",
@@ -849,6 +906,20 @@ func TestAWSSDProvider_RegisterInstance(t *testing.T) {
},
}
+ // IPv6-based instance
+ provider.RegisterInstance(context.Background(), services["private"]["aaaa-srv"], &endpoint.Endpoint{
+ RecordType: endpoint.RecordTypeAAAA,
+ DNSName: "service4.private.com.",
+ RecordTTL: 300,
+ Targets: endpoint.Targets{"0000:0000:0000:0000:abcd:abcd:abcd:abcd"},
+ })
+ expectedInstances["0000:0000:0000:0000:abcd:abcd:abcd:abcd"] = &sdtypes.Instance{
+ Id: aws.String("0000:0000:0000:0000:abcd:abcd:abcd:abcd"),
+ Attributes: map[string]string{
+ sdInstanceAttrIPV6: "0000:0000:0000:0000:abcd:abcd:abcd:abcd",
+ },
+ }
+
// validate instances
for _, srvInst := range api.instances {
for id, inst := range srvInst {
@@ -900,3 +971,39 @@ func TestAWSSDProvider_DeregisterInstance(t *testing.T) {
assert.Len(t, instances["srv1"], 0)
}
+
+func TestAWSSDProvider_awsTags(t *testing.T) {
+ tests := []struct {
+ Expectation []sdtypes.Tag
+ Input map[string]string
+ }{
+ {
+ Expectation: []sdtypes.Tag{
+ {
+ Key: aws.String("key1"),
+ Value: aws.String("value1"),
+ },
+ {
+ Key: aws.String("key2"),
+ Value: aws.String("value2"),
+ },
+ },
+ Input: map[string]string{
+ "key1": "value1",
+ "key2": "value2",
+ },
+ },
+ {
+ Expectation: []sdtypes.Tag{},
+ Input: map[string]string{},
+ },
+ {
+ Expectation: []sdtypes.Tag{},
+ Input: nil,
+ },
+ }
+
+ for _, test := range tests {
+ require.ElementsMatch(t, test.Expectation, awsTags(test.Input))
+ }
+}
diff --git a/provider/azure/azure.go b/provider/azure/azure.go
index 904fa62239..e80345e6f0 100644
--- a/provider/azure/azure.go
+++ b/provider/azure/azure.go
@@ -21,6 +21,7 @@ import (
"context"
"fmt"
"strings"
+ "time"
log "github.com/sirupsen/logrus"
@@ -60,13 +61,14 @@ type AzureProvider struct {
userAssignedIdentityClientID string
activeDirectoryAuthorityHost string
zonesClient ZonesClient
+ zonesCache *zonesCache[dns.Zone]
recordSetsClient RecordSetsClient
}
// NewAzureProvider creates a new Azure provider.
//
// Returns the provider or an error if a provider could not be created.
-func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, dryRun bool) (*AzureProvider, error) {
+func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesCacheDuration time.Duration, dryRun bool) (*AzureProvider, error) {
cfg, err := getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost)
if err != nil {
return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err)
@@ -93,6 +95,7 @@ func NewAzureProvider(configFile string, domainFilter endpoint.DomainFilter, zon
userAssignedIdentityClientID: cfg.UserAssignedIdentityID,
activeDirectoryAuthorityHost: cfg.ActiveDirectoryAuthorityHost,
zonesClient: zonesClient,
+ zonesCache: &zonesCache[dns.Zone]{duration: zonesCacheDuration},
recordSetsClient: recordSetsClient,
}, nil
}
@@ -167,6 +170,10 @@ func (p *AzureProvider) ApplyChanges(ctx context.Context, changes *plan.Changes)
func (p *AzureProvider) zones(ctx context.Context) ([]dns.Zone, error) {
log.Debugf("Retrieving Azure DNS zones for resource group: %s.", p.resourceGroup)
+ if !p.zonesCache.Expired() {
+ log.Debugf("Using cached Azure DNS zones for resource group: %s zone count: %d.", p.resourceGroup, len(p.zonesCache.Get()))
+ return p.zonesCache.Get(), nil
+ }
var zones []dns.Zone
pager := p.zonesClient.NewListByResourceGroupPager(p.resourceGroup, &dns.ZonesClientListByResourceGroupOptions{Top: nil})
for pager.More() {
@@ -183,7 +190,8 @@ func (p *AzureProvider) zones(ctx context.Context) ([]dns.Zone, error) {
}
}
}
- log.Debugf("Found %d Azure DNS zone(s).", len(zones))
+ log.Debugf("Found %d Azure DNS zone(s). Updating zones cache", len(zones))
+ p.zonesCache.Reset(zones)
return zones, nil
}
@@ -384,6 +392,19 @@ func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet
MxRecords: mxRecords,
},
}, nil
+ case dns.RecordTypeNS:
+ nsRecords := make([]*dns.NsRecord, len(endpoint.Targets))
+ for i, target := range endpoint.Targets {
+ nsRecords[i] = &dns.NsRecord{
+ Nsdname: to.Ptr(target),
+ }
+ }
+ return dns.RecordSet{
+ Properties: &dns.RecordSetProperties{
+ TTL: to.Ptr(ttl),
+ NsRecords: nsRecords,
+ },
+ }, nil
case dns.RecordTypeTXT:
return dns.RecordSet{
Properties: &dns.RecordSetProperties{
@@ -452,6 +473,16 @@ func extractAzureTargets(recordSet *dns.RecordSet) []string {
return targets
}
+ // Check for NS records
+ nsRecords := properties.NsRecords
+ if len(nsRecords) > 0 && (nsRecords)[0].Nsdname != nil {
+ targets := make([]string, len(nsRecords))
+ for i, nsRecord := range nsRecords {
+ targets[i] = *nsRecord.Nsdname
+ }
+ return targets
+ }
+
// Check for TXT records
txtRecords := properties.TxtRecords
if len(txtRecords) > 0 && (txtRecords)[0].Value != nil {
diff --git a/provider/azure/azure_private_dns.go b/provider/azure/azure_private_dns.go
index 051b13c877..5ca3f5928f 100644
--- a/provider/azure/azure_private_dns.go
+++ b/provider/azure/azure_private_dns.go
@@ -21,6 +21,7 @@ import (
"context"
"fmt"
"strings"
+ "time"
azcoreruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
@@ -55,13 +56,14 @@ type AzurePrivateDNSProvider struct {
userAssignedIdentityClientID string
activeDirectoryAuthorityHost string
zonesClient PrivateZonesClient
+ zonesCache *zonesCache[privatedns.PrivateZone]
recordSetsClient PrivateRecordSetsClient
}
// NewAzurePrivateDNSProvider creates a new Azure Private DNS provider.
//
// Returns the provider or an error if a provider could not be created.
-func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, dryRun bool) (*AzurePrivateDNSProvider, error) {
+func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainFilter, zoneNameFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, subscriptionID string, resourceGroup string, userAssignedIdentityClientID string, activeDirectoryAuthorityHost string, zonesCacheDuration time.Duration, dryRun bool) (*AzurePrivateDNSProvider, error) {
cfg, err := getConfig(configFile, subscriptionID, resourceGroup, userAssignedIdentityClientID, activeDirectoryAuthorityHost)
if err != nil {
return nil, fmt.Errorf("failed to read Azure config file '%s': %v", configFile, err)
@@ -88,6 +90,7 @@ func NewAzurePrivateDNSProvider(configFile string, domainFilter endpoint.DomainF
userAssignedIdentityClientID: cfg.UserAssignedIdentityID,
activeDirectoryAuthorityHost: cfg.ActiveDirectoryAuthorityHost,
zonesClient: zonesClient,
+ zonesCache: &zonesCache[privatedns.PrivateZone]{duration: zonesCacheDuration},
recordSetsClient: recordSetsClient,
}, nil
}
@@ -177,7 +180,10 @@ func (p *AzurePrivateDNSProvider) ApplyChanges(ctx context.Context, changes *pla
func (p *AzurePrivateDNSProvider) zones(ctx context.Context) ([]privatedns.PrivateZone, error) {
log.Debugf("Retrieving Azure Private DNS zones for Resource Group '%s'", p.resourceGroup)
-
+ if !p.zonesCache.Expired() {
+ log.Debugf("Using cached Azure Private DNS zones for resource group: %s zone count: %d.", p.resourceGroup, len(p.zonesCache.Get()))
+ return p.zonesCache.Get(), nil
+ }
var zones []privatedns.PrivateZone
pager := p.zonesClient.NewListByResourceGroupPager(p.resourceGroup, &privatedns.PrivateZonesClientListByResourceGroupOptions{Top: nil})
@@ -198,7 +204,8 @@ func (p *AzurePrivateDNSProvider) zones(ctx context.Context) ([]privatedns.Priva
}
}
- log.Debugf("Found %d Azure Private DNS zone(s).", len(zones))
+ log.Debugf("Found %d Azure Private DNS zone(s). Updating zones cache", len(zones))
+ p.zonesCache.Reset(zones)
return zones, nil
}
diff --git a/provider/azure/azure_privatedns_test.go b/provider/azure/azure_privatedns_test.go
index dea74c10b1..cc9223499e 100644
--- a/provider/azure/azure_privatedns_test.go
+++ b/provider/azure/azure_privatedns_test.go
@@ -238,6 +238,7 @@ func newAzurePrivateDNSProvider(domainFilter endpoint.DomainFilter, zoneNameFilt
dryRun: dryRun,
resourceGroup: resourceGroup,
zonesClient: privateZonesClient,
+ zonesCache: &zonesCache[privatedns.PrivateZone]{duration: 0},
recordSetsClient: privateRecordsClient,
}
}
@@ -353,9 +354,9 @@ func TestAzurePrivateDNSApplyChanges(t *testing.T) {
}
func TestAzurePrivateDNSApplyChangesDryRun(t *testing.T) {
- recordsClient := mockRecordSetsClient{}
+ recordsClient := mockPrivateRecordSetsClient{}
- testAzureApplyChangesInternal(t, true, &recordsClient)
+ testAzurePrivateDNSApplyChangesInternal(t, true, &recordsClient)
validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{})
@@ -470,9 +471,9 @@ func TestAzurePrivateDNSNameFilter(t *testing.T) {
}
func TestAzurePrivateDNSApplyChangesZoneName(t *testing.T) {
- recordsClient := mockRecordSetsClient{}
+ recordsClient := mockPrivateRecordSetsClient{}
- testAzureApplyChangesInternalZoneName(t, false, &recordsClient)
+ testAzurePrivateDNSApplyChangesInternalZoneName(t, false, &recordsClient)
validateAzureEndpoints(t, recordsClient.deletedEndpoints, []*endpoint.Endpoint{
endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, ""),
diff --git a/provider/azure/azure_test.go b/provider/azure/azure_test.go
index f2031d1398..a27f2eb124 100644
--- a/provider/azure/azure_test.go
+++ b/provider/azure/azure_test.go
@@ -172,6 +172,19 @@ func mxRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetPrope
}
}
+func nsRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties {
+ nsRecords := make([]*dns.NsRecord, len(values))
+ for i, value := range values {
+ nsRecords[i] = &dns.NsRecord{
+ Nsdname: to.Ptr(value),
+ }
+ }
+ return &dns.RecordSetProperties{
+ TTL: to.Ptr(ttl),
+ NsRecords: nsRecords,
+ }
+}
+
func txtRecordSetPropertiesGetter(values []string, ttl int64) *dns.RecordSetProperties {
return &dns.RecordSetProperties{
TTL: to.Ptr(ttl),
@@ -209,6 +222,8 @@ func createMockRecordSetMultiWithTTL(name, recordType string, ttl int64, values
getterFunc = cNameRecordSetPropertiesGetter
case endpoint.RecordTypeMX:
getterFunc = mxRecordSetPropertiesGetter
+ case endpoint.RecordTypeNS:
+ getterFunc = nsRecordSetPropertiesGetter
case endpoint.RecordTypeTXT:
getterFunc = txtRecordSetPropertiesGetter
default:
@@ -238,12 +253,13 @@ func newAzureProvider(domainFilter endpoint.DomainFilter, zoneNameFilter endpoin
userAssignedIdentityClientID: userAssignedIdentityClientID,
activeDirectoryAuthorityHost: activeDirectoryAuthorityHost,
zonesClient: zonesClient,
+ zonesCache: &zonesCache[dns.Zone]{duration: 0},
recordSetsClient: recordsClient,
}
}
func validateAzureEndpoints(t *testing.T, endpoints []*endpoint.Endpoint, expected []*endpoint.Endpoint) {
- assert.True(t, testutils.SameEndpoints(endpoints, expected), "expected and actual endpoints don't match. %s:%s", endpoints, expected)
+ assert.True(t, testutils.SameEndpoints(endpoints, expected), "actual and expected endpoints don't match. %s:%s", endpoints, expected)
}
func TestAzureRecord(t *testing.T) {
@@ -252,13 +268,15 @@ func TestAzureRecord(t *testing.T) {
createMockZone("example.com", "/dnszones/example.com"),
},
[]*dns.RecordSet{
- createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."),
+ createMockRecordSet("@", endpoint.RecordTypeNS, "ns1-03.azure-dns.com."),
createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"),
createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122"),
createMockRecordSet("@", endpoint.RecordTypeAAAA, "2001::123:123:123:122"),
+ createMockRecordSet("cloud", endpoint.RecordTypeNS, "ns1.example.com."),
createMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeAAAA, "2001::123:123:123:123", 3600),
+ createMockRecordSetWithTTL("cloud-ttl", endpoint.RecordTypeNS, "ns1-ttl.example.com.", 10),
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com"),
@@ -273,11 +291,14 @@ func TestAzureRecord(t *testing.T) {
t.Fatal(err)
}
expected := []*endpoint.Endpoint{
+ endpoint.NewEndpoint("example.com", endpoint.RecordTypeNS, "ns1-03.azure-dns.com."),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeAAAA, "2001::123:123:123:122"),
+ endpoint.NewEndpoint("cloud.example.com", endpoint.RecordTypeNS, "ns1.example.com."),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeAAAA, 3600, "2001::123:123:123:123"),
+ endpoint.NewEndpointWithTTL("cloud-ttl.example.com", endpoint.RecordTypeNS, 10, "ns1-ttl.example.com."),
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"),
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com"),
@@ -292,13 +313,15 @@ func TestAzureMultiRecord(t *testing.T) {
createMockZone("example.com", "/dnszones/example.com"),
},
[]*dns.RecordSet{
- createMockRecordSet("@", "NS", "ns1-03.azure-dns.com."),
+ createMockRecordSet("@", endpoint.RecordTypeNS, "ns1-03.azure-dns.com."),
createMockRecordSet("@", "SOA", "Email: azuredns-hostmaster.microsoft.com"),
createMockRecordSet("@", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"),
createMockRecordSet("@", endpoint.RecordTypeAAAA, "2001::123:123:123:122", "2001::234:234:234:233"),
+ createMockRecordSet("cloud", endpoint.RecordTypeNS, "ns1.example.com.", "ns2.example.com."),
createMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
createMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
createMockRecordSetMultiWithTTL("nginx", endpoint.RecordTypeAAAA, 3600, "2001::123:123:123:123", "2001::234:234:234:234"),
+ createMockRecordSetMultiWithTTL("cloud-ttl", endpoint.RecordTypeNS, 10, "ns1-ttl.example.com.", "ns2-ttl.example.com."),
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
createMockRecordSetMultiWithTTL("mail", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"),
@@ -313,11 +336,14 @@ func TestAzureMultiRecord(t *testing.T) {
t.Fatal(err)
}
expected := []*endpoint.Endpoint{
+ endpoint.NewEndpoint("example.com", endpoint.RecordTypeNS, "ns1-03.azure-dns.com."),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeA, "123.123.123.122", "234.234.234.233"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeAAAA, "2001::123:123:123:122", "2001::234:234:234:233"),
+ endpoint.NewEndpoint("cloud.example.com", endpoint.RecordTypeNS, "ns1.example.com.", "ns2.example.com."),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123", "234.234.234.234"),
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeAAAA, 3600, "2001::123:123:123:123", "2001::234:234:234:234"),
+ endpoint.NewEndpointWithTTL("cloud-ttl.example.com", endpoint.RecordTypeNS, 10, "ns1-ttl.example.com.", "ns2-ttl.example.com."),
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
endpoint.NewEndpointWithTTL("hack.example.com", endpoint.RecordTypeCNAME, 10, "hack.azurewebsites.net"),
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, 4000, "10 example.com", "20 backup.example.com"),
@@ -335,6 +361,7 @@ func TestAzureApplyChanges(t *testing.T) {
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, ""),
endpoint.NewEndpoint("deletedaaaa.example.com", endpoint.RecordTypeAAAA, ""),
endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, ""),
+ endpoint.NewEndpoint("deletedns.example.com", endpoint.RecordTypeNS, ""),
})
validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{
@@ -343,15 +370,18 @@ func TestAzureApplyChanges(t *testing.T) {
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "1.2.3.5"),
endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "2001::1:2:3:4", "2001::1:2:3:5"),
+ endpoint.NewEndpointWithTTL("cloud.example.com", endpoint.RecordTypeNS, endpoint.TTL(recordTTL), "ns1.example.com."),
endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
endpoint.NewEndpointWithTTL("bar.example.com", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "other.com"),
endpoint.NewEndpointWithTTL("bar.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "5.6.7.8"),
endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "2001::5:6:7:8"),
+ endpoint.NewEndpointWithTTL("cloud.other.com", endpoint.RecordTypeNS, endpoint.TTL(recordTTL), "ns2.other.com."),
endpoint.NewEndpointWithTTL("other.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeAAAA, 3600, "2001::111:222:111:222"),
endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
+ endpoint.NewEndpointWithTTL("newns.example.com", endpoint.RecordTypeNS, 10, "ns1.example.com."),
endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"),
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 other.com"),
endpoint.NewEndpointWithTTL("mail.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
@@ -393,14 +423,17 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.5", "1.2.3.4"),
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeAAAA, "2001::1:2:3:5", "2001::1:2:3:4"),
+ endpoint.NewEndpoint("cloud.example.com", endpoint.RecordTypeNS, "ns1.example.com."),
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.RecordTypeAAAA, "2001::5:6:7:8"),
endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
+ endpoint.NewEndpoint("cloud.other.com", endpoint.RecordTypeNS, "ns2.other.com."),
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"),
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeAAAA, "2001::4:4:4:4"),
+ endpoint.NewEndpoint("cloud.nope.com", endpoint.RecordTypeNS, "ns1.nope.com."),
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeMX, "10 other.com"),
endpoint.NewEndpoint("mail.example.com", endpoint.RecordTypeTXT, "tag"),
@@ -409,6 +442,7 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
currentRecords := []*endpoint.Endpoint{
endpoint.NewEndpoint("old.example.com", endpoint.RecordTypeA, "121.212.121.212"),
endpoint.NewEndpoint("oldcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
+ endpoint.NewEndpoint("oldcloud.example.com", endpoint.RecordTypeNS, "ns1.example.com."),
endpoint.NewEndpoint("old.nope.com", endpoint.RecordTypeA, "121.212.121.212"),
endpoint.NewEndpoint("oldmail.example.com", endpoint.RecordTypeMX, "20 foo.other.com"),
}
@@ -416,8 +450,10 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
endpoint.NewEndpointWithTTL("new.example.com", endpoint.RecordTypeAAAA, 3600, "2001::111:222:111:222"),
endpoint.NewEndpointWithTTL("newcname.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
+ endpoint.NewEndpointWithTTL("newns.example.com", endpoint.RecordTypeNS, 10, "ns1.example.com."),
endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeA, "222.111.222.111"),
endpoint.NewEndpoint("new.nope.com", endpoint.RecordTypeAAAA, "2001::222:111:222:111"),
+ endpoint.NewEndpoint("newns.nope.com", endpoint.RecordTypeNS, "ns1.example.com"),
endpoint.NewEndpointWithTTL("newmail.example.com", endpoint.RecordTypeMX, 7200, "40 bar.other.com"),
}
@@ -425,8 +461,10 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
endpoint.NewEndpoint("deleted.example.com", endpoint.RecordTypeA, "111.222.111.222"),
endpoint.NewEndpoint("deletedaaaa.example.com", endpoint.RecordTypeAAAA, "2001::111:222:111:222"),
endpoint.NewEndpoint("deletedcname.example.com", endpoint.RecordTypeCNAME, "other.com"),
+ endpoint.NewEndpoint("deletedns.example.com", endpoint.RecordTypeNS, "ns1.example.com."),
endpoint.NewEndpoint("deleted.nope.com", endpoint.RecordTypeA, "222.111.222.111"),
endpoint.NewEndpoint("deleted.nope.com", endpoint.RecordTypeAAAA, "2001::222:111:222:111"),
+ endpoint.NewEndpoint("deletedns.nope.com", endpoint.RecordTypeNS, "ns1.example.com."),
}
changes := &plan.Changes{
@@ -454,9 +492,11 @@ func TestAzureNameFilter(t *testing.T) {
createMockRecordSet("@", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"),
createMockRecordSetWithTTL("test.nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeA, "123.123.123.123", 3600),
+ createMockRecordSetWithTTL("nginx", endpoint.RecordTypeNS, "ns1.example.com.", 3600),
createMockRecordSetWithTTL("nginx", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default", recordTTL),
createMockRecordSetWithTTL("mail.nginx", endpoint.RecordTypeMX, "20 example.com", recordTTL),
createMockRecordSetWithTTL("hack", endpoint.RecordTypeCNAME, "hack.azurewebsites.net", 10),
+ createMockRecordSetWithTTL("hack", endpoint.RecordTypeNS, "ns1.example.com.", 3600),
})
if err != nil {
t.Fatal(err)
@@ -470,6 +510,7 @@ func TestAzureNameFilter(t *testing.T) {
expected := []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("test.nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeA, 3600, "123.123.123.123"),
+ endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeNS, 3600, "ns1.example.com."),
endpoint.NewEndpointWithTTL("nginx.example.com", endpoint.RecordTypeTXT, recordTTL, "heritage=external-dns,external-dns/owner=default"),
endpoint.NewEndpointWithTTL("mail.nginx.example.com", endpoint.RecordTypeMX, recordTTL, "20 example.com"),
}
@@ -486,14 +527,17 @@ func TestAzureApplyChangesZoneName(t *testing.T) {
endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, ""),
endpoint.NewEndpoint("deletedaaaa.foo.example.com", endpoint.RecordTypeAAAA, ""),
endpoint.NewEndpoint("deletedcname.foo.example.com", endpoint.RecordTypeCNAME, ""),
+ endpoint.NewEndpoint("deletedns.foo.example.com", endpoint.RecordTypeNS, ""),
})
validateAzureEndpoints(t, recordsClient.updatedEndpoints, []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4", "1.2.3.5"),
endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "2001::1:2:3:4", "2001::1:2:3:5"),
+ endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeNS, endpoint.TTL(recordTTL), "ns1.example.com."),
endpoint.NewEndpointWithTTL("foo.example.com", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "tag"),
endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeAAAA, 3600, "2001::111:222:111:222"),
+ endpoint.NewEndpointWithTTL("newns.foo.example.com", endpoint.RecordTypeNS, 10, "ns1.foo.example.com."),
endpoint.NewEndpointWithTTL("newcname.foo.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
})
}
@@ -519,10 +563,13 @@ func testAzureApplyChangesInternalZoneName(t *testing.T, dryRun bool, client Rec
endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.5", "1.2.3.4"),
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeAAAA, "2001::1:2:3:5", "2001::1:2:3:4"),
+ endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeNS, "ns1.example.com."),
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeCNAME, "other.com"),
+ endpoint.NewEndpoint("barns.example.com", endpoint.RecordTypeNS, "ns1.example.com."),
endpoint.NewEndpoint("bar.example.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("other.com", endpoint.RecordTypeA, "5.6.7.8"),
+ endpoint.NewEndpoint("foons.other.com", endpoint.RecordTypeNS, "ns1.other.com"),
endpoint.NewEndpoint("other.com", endpoint.RecordTypeTXT, "tag"),
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeA, "4.4.4.4"),
endpoint.NewEndpoint("nope.com", endpoint.RecordTypeTXT, "tag"),
@@ -537,14 +584,17 @@ func testAzureApplyChangesInternalZoneName(t *testing.T, dryRun bool, client Rec
endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeA, 3600, "111.222.111.222"),
endpoint.NewEndpointWithTTL("new.foo.example.com", endpoint.RecordTypeAAAA, 3600, "2001::111:222:111:222"),
endpoint.NewEndpointWithTTL("newcname.foo.example.com", endpoint.RecordTypeCNAME, 10, "other.com"),
+ endpoint.NewEndpointWithTTL("newns.foo.example.com", endpoint.RecordTypeNS, 10, "ns1.foo.example.com."),
endpoint.NewEndpoint("new.nope.example.com", endpoint.RecordTypeA, "222.111.222.111"),
endpoint.NewEndpoint("new.nope.example.com", endpoint.RecordTypeAAAA, "2001::222:111:222:111"),
+ endpoint.NewEndpointWithTTL("newns.nope.example.com", endpoint.RecordTypeNS, 10, "ns1.nope.example.com."),
}
deleteRecords := []*endpoint.Endpoint{
endpoint.NewEndpoint("deleted.foo.example.com", endpoint.RecordTypeA, "111.222.111.222"),
endpoint.NewEndpoint("deletedaaaa.foo.example.com", endpoint.RecordTypeAAAA, "2001::111:222:111:222"),
endpoint.NewEndpoint("deletedcname.foo.example.com", endpoint.RecordTypeCNAME, "other.com"),
+ endpoint.NewEndpoint("deletedns.foo.example.com", endpoint.RecordTypeNS, "ns1.foo.example.com."),
endpoint.NewEndpoint("deleted.nope.example.com", endpoint.RecordTypeA, "222.111.222.111"),
}
diff --git a/provider/azure/cache.go b/provider/azure/cache.go
new file mode 100644
index 0000000000..9cde58fe20
--- /dev/null
+++ b/provider/azure/cache.go
@@ -0,0 +1,50 @@
+/*
+Copyright 2024 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 azure
+
+import (
+ "time"
+)
+
+// zonesCache is a cache for Azure zones(private or public)
+type zonesCache[T any] struct {
+ age time.Time
+ duration time.Duration
+ zones []T
+}
+
+// Reset method to reset the zones and update the age. This will be used to update the cache
+// after making a new API call to get the zones.
+func (z *zonesCache[T]) Reset(zones []T) {
+ if z.duration > time.Duration(0) {
+ z.age = time.Now()
+ z.zones = zones
+ }
+}
+
+// Get method to retrieve the cached zones. If cache is not expired, this will be used
+// instead of making a new API call to get the zones.
+func (z *zonesCache[T]) Get() []T {
+ return z.zones
+}
+
+// Expired method to check if the cache has expired based on duration or if zones are empty.
+// If cache is expired, a new API call will be made to get the zones. If zones are empty, a new
+// API call will be made to get the zones. This case comes in at the time of initialization.
+func (z *zonesCache[T]) Expired() bool {
+ return len(z.zones) < 1 || time.Since(z.age) > z.duration
+}
diff --git a/provider/azure/cache_test.go b/provider/azure/cache_test.go
new file mode 100644
index 0000000000..f3df028c9f
--- /dev/null
+++ b/provider/azure/cache_test.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2024 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 azure
+
+import (
+ "testing"
+ "time"
+
+ dns "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestzonesCache(t *testing.T) {
+ now := time.Now()
+ zoneName := "example.com"
+ var testCases = map[string]struct {
+ z *zonesCache[dns.Zone]
+ expired bool
+ }{
+ "inactive-zone-cache": {
+ &zonesCache[dns.Zone]{
+ duration: 0 * time.Second,
+ },
+ true,
+ },
+ "empty-active-zone-cache": {
+ &zonesCache[dns.Zone]{
+ duration: 30 * time.Second,
+ },
+ true,
+ },
+ "expired-zone-cache": {
+ &zonesCache[dns.Zone]{
+ age: now.Add(-300 * time.Second),
+ duration: 30 * time.Second,
+ },
+ true,
+ },
+ "active-zone-cache": {
+ &zonesCache[dns.Zone]{
+ zones: []dns.Zone{{
+ Name: &zoneName,
+ }},
+ duration: 30 * time.Second,
+ age: now,
+ },
+ false,
+ },
+ }
+
+ for name, testCase := range testCases {
+ t.Run(name, func(t *testing.T) {
+ assert.Equal(t, testCase.expired, testCase.z.Expired())
+ var resetZoneLength = 1
+ if testCase.z.duration == 0 {
+ resetZoneLength = 0
+ }
+ testCase.z.Reset([]dns.Zone{{
+ Name: &zoneName,
+ }})
+ assert.Len(t, testCase.z.Get(), resetZoneLength)
+ })
+ }
+}
diff --git a/provider/cloudflare/cloudflare.go b/provider/cloudflare/cloudflare.go
index c693ddd72f..9c83a9c414 100644
--- a/provider/cloudflare/cloudflare.go
+++ b/provider/cloudflare/cloudflare.go
@@ -23,6 +23,7 @@ import (
"os"
"strconv"
"strings"
+ "time"
cloudflare "github.com/cloudflare/cloudflare-go"
log "github.com/sirupsen/logrus"
@@ -73,6 +74,7 @@ type cloudFlareDNS interface {
CreateDNSRecord(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.CreateDNSRecordParams) (cloudflare.DNSRecord, error)
DeleteDNSRecord(ctx context.Context, rc *cloudflare.ResourceContainer, recordID string) error
UpdateDNSRecord(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.UpdateDNSRecordParams) error
+ UpdateDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.UpdateDataLocalizationRegionalHostnameParams) error
}
type zoneService struct {
@@ -104,6 +106,11 @@ func (z zoneService) UpdateDNSRecord(ctx context.Context, rc *cloudflare.Resourc
return err
}
+func (z zoneService) UpdateDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.UpdateDataLocalizationRegionalHostnameParams) error {
+ _, err := z.service.UpdateDataLocalizationRegionalHostname(ctx, rc, rp)
+ return err
+}
+
func (z zoneService) DeleteDNSRecord(ctx context.Context, rc *cloudflare.ResourceContainer, recordID string) error {
return z.service.DeleteDNSRecord(ctx, rc, recordID)
}
@@ -126,12 +133,14 @@ type CloudFlareProvider struct {
proxiedByDefault bool
DryRun bool
DNSRecordsPerPage int
+ RegionKey string
}
// cloudFlareChange differentiates between ChangActions
type cloudFlareChange struct {
- Action string
- ResourceRecord cloudflare.DNSRecord
+ Action string
+ ResourceRecord cloudflare.DNSRecord
+ RegionalHostname cloudflare.RegionalHostname
}
// RecordParamsTypes is a typeset of the possible Record Params that can be passed to cloudflare-go library
@@ -139,8 +148,8 @@ type RecordParamsTypes interface {
cloudflare.UpdateDNSRecordParams | cloudflare.CreateDNSRecordParams
}
-// getUpdateDNSRecordParam is a function that returns the appropriate Record Param based on the cloudFlareChange passed in
-func getUpdateDNSRecordParam(cfc cloudFlareChange) cloudflare.UpdateDNSRecordParams {
+// updateDNSRecordParam is a function that returns the appropriate Record Param based on the cloudFlareChange passed in
+func updateDNSRecordParam(cfc cloudFlareChange) cloudflare.UpdateDNSRecordParams {
return cloudflare.UpdateDNSRecordParams{
Name: cfc.ResourceRecord.Name,
TTL: cfc.ResourceRecord.TTL,
@@ -150,6 +159,14 @@ func getUpdateDNSRecordParam(cfc cloudFlareChange) cloudflare.UpdateDNSRecordPar
}
}
+// updateDataLocalizationRegionalHostnameParams is a function that returns the appropriate RegionalHostname Param based on the cloudFlareChange passed in
+func updateDataLocalizationRegionalHostnameParams(cfc cloudFlareChange) cloudflare.UpdateDataLocalizationRegionalHostnameParams {
+ return cloudflare.UpdateDataLocalizationRegionalHostnameParams{
+ Hostname: cfc.RegionalHostname.Hostname,
+ RegionKey: cfc.RegionalHostname.RegionKey,
+ }
+}
+
// getCreateDNSRecordParam is a function that returns the appropriate Record Param based on the cloudFlareChange passed in
func getCreateDNSRecordParam(cfc cloudFlareChange) cloudflare.CreateDNSRecordParams {
return cloudflare.CreateDNSRecordParams{
@@ -162,7 +179,7 @@ func getCreateDNSRecordParam(cfc cloudFlareChange) cloudflare.CreateDNSRecordPar
}
// NewCloudFlareProvider initializes a new CloudFlare DNS based Provider.
-func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, proxiedByDefault bool, dryRun bool, dnsRecordsPerPage int) (*CloudFlareProvider, error) {
+func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, proxiedByDefault bool, dryRun bool, dnsRecordsPerPage int, regionKey string) (*CloudFlareProvider, error) {
// initialize via chosen auth method and returns new API object
var (
config *cloudflare.API
@@ -192,6 +209,7 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov
proxiedByDefault: proxiedByDefault,
DryRun: dryRun,
DNSRecordsPerPage: dnsRecordsPerPage,
+ RegionKey: regionKey,
}
return provider, nil
}
@@ -350,13 +368,19 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
log.WithFields(logFields).Errorf("failed to find previous record: %v", change.ResourceRecord)
continue
}
- recordParam := getUpdateDNSRecordParam(*change)
+ recordParam := updateDNSRecordParam(*change)
+ regionalHostnameParam := updateDataLocalizationRegionalHostnameParams(*change)
recordParam.ID = recordID
err := p.Client.UpdateDNSRecord(ctx, resourceContainer, recordParam)
if err != nil {
failedChange = true
log.WithFields(logFields).Errorf("failed to update record: %v", err)
}
+ regionalHostnameErr := p.Client.UpdateDataLocalizationRegionalHostname(ctx, resourceContainer, regionalHostnameParam)
+ if regionalHostnameErr != nil {
+ failedChange = true
+ log.WithFields(logFields).Errorf("failed to update record: %v", regionalHostnameErr)
+ }
} else if change.Action == cloudFlareDelete {
recordID := p.getRecordID(records, change.ResourceRecord)
if recordID == "" {
@@ -443,7 +467,7 @@ func (p *CloudFlareProvider) newCloudFlareChange(action string, endpoint *endpoi
if endpoint.RecordTTL.IsConfigured() {
ttl = int(endpoint.RecordTTL)
}
-
+ dt := time.Now()
return &cloudFlareChange{
Action: action,
ResourceRecord: cloudflare.DNSRecord{
@@ -452,6 +476,14 @@ func (p *CloudFlareProvider) newCloudFlareChange(action string, endpoint *endpoi
Proxied: &proxied,
Type: endpoint.RecordType,
Content: target,
+ Meta: map[string]interface{}{
+ "region": p.RegionKey,
+ },
+ },
+ RegionalHostname: cloudflare.RegionalHostname{
+ Hostname: endpoint.DNSName,
+ RegionKey: p.RegionKey,
+ CreatedOn: &dt,
},
}
}
diff --git a/provider/cloudflare/cloudflare_test.go b/provider/cloudflare/cloudflare_test.go
index 564fa04612..aeb929ca0e 100644
--- a/provider/cloudflare/cloudflare_test.go
+++ b/provider/cloudflare/cloudflare_test.go
@@ -204,6 +204,18 @@ func (m *mockCloudFlareClient) UpdateDNSRecord(ctx context.Context, rc *cloudfla
return nil
}
+func (m *mockCloudFlareClient) UpdateDataLocalizationRegionalHostname(ctx context.Context, rc *cloudflare.ResourceContainer, rp cloudflare.UpdateDataLocalizationRegionalHostnameParams) error {
+ m.Actions = append(m.Actions, MockAction{
+ Name: "UpdateDataLocalizationRegionalHostname",
+ ZoneId: rc.Identifier,
+ RecordId: "",
+ RecordData: cloudflare.DNSRecord{
+ Name: rp.Hostname,
+ },
+ })
+ return nil
+}
+
func (m *mockCloudFlareClient) DeleteDNSRecord(ctx context.Context, rc *cloudflare.ResourceContainer, recordID string) error {
m.Actions = append(m.Actions, MockAction{
Name: "Delete",
@@ -706,7 +718,8 @@ func TestCloudflareProvider(t *testing.T) {
provider.NewZoneIDFilter([]string{""}),
false,
true,
- 5000)
+ 5000,
+ "")
if err != nil {
t.Errorf("should not fail, %s", err)
}
@@ -722,7 +735,8 @@ func TestCloudflareProvider(t *testing.T) {
provider.NewZoneIDFilter([]string{""}),
false,
true,
- 5000)
+ 5000,
+ "")
if err != nil {
t.Errorf("should not fail, %s", err)
}
@@ -735,7 +749,8 @@ func TestCloudflareProvider(t *testing.T) {
provider.NewZoneIDFilter([]string{""}),
false,
true,
- 5000)
+ 5000,
+ "")
if err != nil {
t.Errorf("should not fail, %s", err)
}
@@ -747,7 +762,8 @@ func TestCloudflareProvider(t *testing.T) {
provider.NewZoneIDFilter([]string{""}),
false,
true,
- 5000)
+ 5000,
+ "")
if err == nil {
t.Errorf("expected to fail")
}
@@ -1225,7 +1241,6 @@ func TestCloudflareComplexUpdate(t *testing.T) {
client := NewMockCloudFlareClientWithRecords(map[string][]cloudflare.DNSRecord{
"001": ExampleDomain,
})
-
provider := &CloudFlareProvider{
Client: client,
}
@@ -1267,7 +1282,7 @@ func TestCloudflareComplexUpdate(t *testing.T) {
t.Errorf("should not fail, %s", err)
}
- td.CmpDeeply(t, client.Actions, []MockAction{
+ mockAction := []MockAction{
{
Name: "Delete",
ZoneId: "001",
@@ -1296,7 +1311,17 @@ func TestCloudflareComplexUpdate(t *testing.T) {
Proxied: proxyEnabled,
},
},
- })
+ {
+ Name: "UpdateDataLocalizationRegionalHostname",
+ ZoneId: "001",
+ RecordData: cloudflare.DNSRecord{
+ Name: "foobar.bar.com",
+ TTL: 0,
+ Proxiable: false,
+ },
+ },
+ }
+ td.CmpDeeply(t, client.Actions, mockAction)
}
func TestCustomTTLWithEnabledProxyNotChanged(t *testing.T) {
@@ -1355,3 +1380,53 @@ func TestCustomTTLWithEnabledProxyNotChanged(t *testing.T) {
assert.Equal(t, 0, len(planned.Changes.UpdateOld), "no new changes should be here")
assert.Equal(t, 0, len(planned.Changes.Delete), "no new changes should be here")
}
+
+func TestCloudFlareProvider_Region(t *testing.T) {
+ _ = os.Setenv("CF_API_TOKEN", "abc123def")
+ _ = os.Setenv("CF_API_EMAIL", "test@test.com")
+ provider, err := NewCloudFlareProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.ZoneIDFilter{}, true, false, 50, "us")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if provider.RegionKey != "us" {
+ t.Errorf("expected region key to be 'us', but got '%s'", provider.RegionKey)
+ }
+}
+
+func TestCloudFlareProvider_updateDataLocalizationRegionalHostnameParams(t *testing.T) {
+ change := &cloudFlareChange{
+ RegionalHostname: cloudflare.RegionalHostname{
+ Hostname: "example.com",
+ RegionKey: "us",
+ },
+ }
+
+ params := updateDataLocalizationRegionalHostnameParams(*change)
+ if params.Hostname != "example.com" {
+ t.Errorf("expected hostname to be 'example.com', but got '%s'", params.Hostname)
+ }
+
+ if params.RegionKey != "us" {
+ t.Errorf("expected region key to be 'us', but got '%s'", params.RegionKey)
+ }
+}
+
+func TestCloudFlareProvider_newCloudFlareChange(t *testing.T) {
+ _ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx")
+ _ = os.Setenv("CF_API_EMAIL", "test@test.com")
+ provider, err := NewCloudFlareProvider(endpoint.NewDomainFilter([]string{"example.com"}), provider.ZoneIDFilter{}, true, false, 50, "us")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ endpoint := &endpoint.Endpoint{
+ DNSName: "example.com",
+ Targets: []string{"192.0.2.1"},
+ }
+
+ change := provider.newCloudFlareChange(cloudFlareCreate, endpoint, endpoint.Targets[0])
+ if change.RegionalHostname.RegionKey != "us" {
+ t.Errorf("expected region key to be 'us', but got '%s'", change.RegionalHostname.RegionKey)
+ }
+}
diff --git a/provider/digitalocean/digital_ocean.go b/provider/digitalocean/digital_ocean.go
index 20cfcefa05..29412e84d8 100644
--- a/provider/digitalocean/digital_ocean.go
+++ b/provider/digitalocean/digital_ocean.go
@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"os"
+ "strconv"
"strings"
"github.com/digitalocean/godo"
@@ -158,13 +159,15 @@ func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoin
endpoints := []*endpoint.Endpoint{}
for _, zone := range zones {
records, err := p.fetchRecords(ctx, zone.Name)
+
if err != nil {
return nil, err
}
for _, r := range records {
- if provider.SupportedRecordType(r.Type) {
+ if p.SupportedRecordType(r.Type) {
name := r.Name + "." + zone.Name
+ data := r.Data
// root name is identified by @ and should be
// translated to zone name for the endpoint entry.
@@ -172,7 +175,11 @@ func (p *DigitalOceanProvider) Records(ctx context.Context) ([]*endpoint.Endpoin
name = zone.Name
}
- ep := endpoint.NewEndpointWithTTL(name, r.Type, endpoint.TTL(r.TTL), r.Data)
+ if r.Type == endpoint.RecordTypeMX {
+ data = fmt.Sprintf("%d %s", r.Priority, r.Data)
+ }
+
+ ep := endpoint.NewEndpointWithTTL(name, r.Type, endpoint.TTL(r.TTL), data)
endpoints = append(endpoints, ep)
}
@@ -283,16 +290,33 @@ func makeDomainEditRequest(domain, name, recordType, data string, ttl int) *godo
// For some reason the DO API requires the '.' at the end of "data" in case of CNAME request.
// Example: {"type":"CNAME","name":"hello","data":"www.example.com."}
- if recordType == endpoint.RecordTypeCNAME && !strings.HasSuffix(data, ".") {
+ if (recordType == endpoint.RecordTypeCNAME || recordType == endpoint.RecordTypeMX) && !strings.HasSuffix(data, ".") {
data += "."
}
- return &godo.DomainRecordEditRequest{
+ request := &godo.DomainRecordEditRequest{
Name: adjustedName,
Type: recordType,
Data: data,
TTL: ttl,
}
+
+ if recordType == endpoint.RecordTypeMX {
+ priority, domain, err := parseMxTarget(data)
+ if err == nil {
+ request.Priority = int(priority)
+ request.Data = provider.EnsureTrailingDot(domain)
+ } else {
+ log.WithFields(log.Fields{
+ "domain": domain,
+ "dnsName": name,
+ "recordType": recordType,
+ "data": data,
+ }).Warn("Unable to parse MX target")
+ }
+ }
+
+ return request
}
// submitChanges applies an instance of `digitalOceanChanges` to the DigitalOcean API.
@@ -303,13 +327,19 @@ func (p *DigitalOceanProvider) submitChanges(ctx context.Context, changes *digit
}
for _, c := range changes.Creates {
- log.WithFields(log.Fields{
+ logFields := log.Fields{
"domain": c.Domain,
"dnsName": c.Options.Name,
"recordType": c.Options.Type,
"data": c.Options.Data,
"ttl": c.Options.TTL,
- }).Debug("Creating domain record")
+ }
+
+ if c.Options.Type == endpoint.RecordTypeMX {
+ logFields["priority"] = c.Options.Priority
+ }
+
+ log.WithFields(logFields).Debug("Creating domain record")
if p.DryRun {
continue
@@ -322,13 +352,17 @@ func (p *DigitalOceanProvider) submitChanges(ctx context.Context, changes *digit
}
for _, u := range changes.Updates {
- log.WithFields(log.Fields{
+ logFields := log.Fields{
"domain": u.Domain,
"dnsName": u.Options.Name,
"recordType": u.Options.Type,
"data": u.Options.Data,
"ttl": u.Options.TTL,
- }).Debug("Updating domain record")
+ }
+ if u.Options.Type == endpoint.RecordTypeMX {
+ logFields["priority"] = u.Options.Priority
+ }
+ log.WithFields(logFields).Debug("Updating domain record")
if p.DryRun {
continue
@@ -589,6 +623,16 @@ func processDeleteActions(
return nil
}
+// SupportedRecordType returns true if the record type is supported by the provider
+func (p *DigitalOceanProvider) SupportedRecordType(recordType string) bool {
+ switch recordType {
+ case "MX":
+ return true
+ default:
+ return provider.SupportedRecordType(recordType)
+ }
+}
+
// ApplyChanges applies the given set of generic changes to the provider.
func (p *DigitalOceanProvider) ApplyChanges(ctx context.Context, planChanges *plan.Changes) error {
// TODO: This should only retrieve zones affected by the given `planChanges`.
@@ -617,3 +661,18 @@ func (p *DigitalOceanProvider) ApplyChanges(ctx context.Context, planChanges *pl
return p.submitChanges(ctx, &changes)
}
+
+func parseMxTarget(mxTarget string) (priority int64, exchange string, err error) {
+ targetParts := strings.SplitN(mxTarget, " ", 2)
+ if len(targetParts) != 2 {
+ return priority, exchange, fmt.Errorf("mx target needs to be of form '10 example.com'")
+ }
+
+ priorityRaw, exchange := targetParts[0], targetParts[1]
+ priority, err = strconv.ParseInt(priorityRaw, 10, 32)
+ if err != nil {
+ return priority, exchange, fmt.Errorf("invalid priority specified")
+ }
+
+ return priority, exchange, nil
+}
diff --git a/provider/digitalocean/digital_ocean_test.go b/provider/digitalocean/digital_ocean_test.go
index d16be2471a..cd5555b324 100644
--- a/provider/digitalocean/digital_ocean_test.go
+++ b/provider/digitalocean/digital_ocean_test.go
@@ -100,6 +100,9 @@ func (m *mockDigitalOceanClient) Records(ctx context.Context, domain string, opt
{ID: 1, Name: "foo.ext-dns-test", Type: "CNAME"},
{ID: 2, Name: "bar.ext-dns-test", Type: "CNAME"},
{ID: 3, Name: "@", Type: endpoint.RecordTypeCNAME},
+ {ID: 4, Name: "@", Type: endpoint.RecordTypeMX, Priority: 10, Data: "mx1.foo.com."},
+ {ID: 5, Name: "@", Type: endpoint.RecordTypeMX, Priority: 10, Data: "mx2.foo.com."},
+ {ID: 6, Name: "@", Type: endpoint.RecordTypeTXT, Data: "SOME-TXT-TEXT"},
}, &godo.Response{
Links: &godo.Links{
Pages: &godo.Pages{
@@ -344,6 +347,28 @@ func TestDigitalOceanMakeDomainEditRequest(t *testing.T) {
Data: "bar.example.com.",
TTL: customTTL,
}, r4)
+
+ // Ensure that MX records have `.` appended.
+ r5 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX,
+ "10 mx.example.com", digitalOceanRecordTTL)
+ assert.Equal(t, &godo.DomainRecordEditRequest{
+ Type: endpoint.RecordTypeMX,
+ Name: "foo",
+ Data: "mx.example.com.",
+ Priority: 10,
+ TTL: digitalOceanRecordTTL,
+ }, r5)
+
+ // Ensure that MX records do not have an extra `.` appended if they already have a `.`
+ r6 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX,
+ "10 mx.example.com.", digitalOceanRecordTTL)
+ assert.Equal(t, &godo.DomainRecordEditRequest{
+ Type: endpoint.RecordTypeMX,
+ Name: "foo",
+ Data: "mx.example.com.",
+ Priority: 10,
+ TTL: digitalOceanRecordTTL,
+ }, r6)
}
func TestDigitalOceanApplyChanges(t *testing.T) {
@@ -375,6 +400,8 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
"example.com": {
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeCNAME, "foo.example.com"),
+ endpoint.NewEndpoint("example.com", endpoint.RecordTypeMX, "10 mx.example.com"),
+ endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "SOME-TXT-TEXT"),
},
}
@@ -382,7 +409,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
err := processCreateActions(recordsByDomain, createsByDomain, &changes)
require.NoError(t, err)
- assert.Equal(t, 2, len(changes.Creates))
+ assert.Equal(t, 4, len(changes.Creates))
assert.Equal(t, 0, len(changes.Updates))
assert.Equal(t, 0, len(changes.Deletes))
@@ -405,6 +432,25 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
TTL: digitalOceanRecordTTL,
},
},
+ {
+ Domain: "example.com",
+ Options: &godo.DomainRecordEditRequest{
+ Name: "@",
+ Type: endpoint.RecordTypeMX,
+ Priority: 10,
+ Data: "mx.example.com.",
+ TTL: digitalOceanRecordTTL,
+ },
+ },
+ {
+ Domain: "example.com",
+ Options: &godo.DomainRecordEditRequest{
+ Name: "@",
+ Type: endpoint.RecordTypeTXT,
+ Data: "SOME-TXT-TEXT",
+ TTL: digitalOceanRecordTTL,
+ },
+ },
}
if !elementsMatch(t, expectedCreates, changes.Creates) {
@@ -436,6 +482,29 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Data: "foo.example.com.",
TTL: digitalOceanRecordTTL,
},
+ {
+ ID: 4,
+ Name: "@",
+ Type: endpoint.RecordTypeMX,
+ Data: "mx1.example.com.",
+ Priority: 10,
+ TTL: digitalOceanRecordTTL,
+ },
+ {
+ ID: 5,
+ Name: "@",
+ Type: endpoint.RecordTypeMX,
+ Data: "mx2.example.com.",
+ Priority: 10,
+ TTL: digitalOceanRecordTTL,
+ },
+ {
+ ID: 6,
+ Name: "@",
+ Type: endpoint.RecordTypeTXT,
+ Data: "SOME_TXTX_TEXT",
+ TTL: digitalOceanRecordTTL,
+ },
},
}
@@ -443,6 +512,8 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
"example.com": {
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "10.11.12.13"),
endpoint.NewEndpoint("example.com", endpoint.RecordTypeCNAME, "bar.example.com"),
+ endpoint.NewEndpoint("example.com", endpoint.RecordTypeMX, "10 mx3.example.com"),
+ endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT, "ANOTHER-TXT"),
},
}
@@ -450,9 +521,9 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
err := processUpdateActions(recordsByDomain, updatesByDomain, &changes)
require.NoError(t, err)
- assert.Equal(t, 2, len(changes.Creates))
+ assert.Equal(t, 4, len(changes.Creates))
assert.Equal(t, 0, len(changes.Updates))
- assert.Equal(t, 3, len(changes.Deletes))
+ assert.Equal(t, 6, len(changes.Deletes))
expectedCreates := []*digitalOceanChangeCreate{
{
@@ -473,6 +544,25 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
TTL: digitalOceanRecordTTL,
},
},
+ {
+ Domain: "example.com",
+ Options: &godo.DomainRecordEditRequest{
+ Name: "@",
+ Type: endpoint.RecordTypeMX,
+ Data: "mx3.example.com.",
+ Priority: 10,
+ TTL: digitalOceanRecordTTL,
+ },
+ },
+ {
+ Domain: "example.com",
+ Options: &godo.DomainRecordEditRequest{
+ Name: "@",
+ Type: endpoint.RecordTypeTXT,
+ Data: "ANOTHER-TXT",
+ TTL: digitalOceanRecordTTL,
+ },
+ },
}
if !elementsMatch(t, expectedCreates, changes.Creates) {
@@ -492,6 +582,18 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Domain: "example.com",
RecordID: 3,
},
+ {
+ Domain: "example.com",
+ RecordID: 4,
+ },
+ {
+ Domain: "example.com",
+ RecordID: 5,
+ },
+ {
+ Domain: "example.com",
+ RecordID: 6,
+ },
}
if !elementsMatch(t, expectedDeletes, changes.Deletes) {
@@ -597,6 +699,26 @@ func TestDigitalOceanGetMatchingDomainRecords(t *testing.T) {
Type: endpoint.RecordTypeA,
Data: "9.10.11.12",
},
+ {
+ ID: 5,
+ Name: "@",
+ Type: endpoint.RecordTypeMX,
+ Priority: 10,
+ Data: "mx1.foo.com.",
+ },
+ {
+ ID: 6,
+ Name: "@",
+ Type: endpoint.RecordTypeMX,
+ Priority: 10,
+ Data: "mx2.foo.com.",
+ },
+ {
+ ID: 7,
+ Name: "@",
+ Type: endpoint.RecordTypeTXT,
+ Data: "MYTXT",
+ },
}
ep1 := endpoint.NewEndpoint("foo.com", endpoint.RecordTypeCNAME)
@@ -627,6 +749,17 @@ func TestDigitalOceanGetMatchingDomainRecords(t *testing.T) {
r2 := getMatchingDomainRecords(records, "example.com", ep4)
assert.Equal(t, 1, len(r2))
assert.Equal(t, "9.10.11.12", r2[0].Data)
+
+ ep5 := endpoint.NewEndpoint("example.com", endpoint.RecordTypeMX)
+ r3 := getMatchingDomainRecords(records, "example.com", ep5)
+ assert.Equal(t, 2, len(r3))
+ assert.Equal(t, "mx1.foo.com.", r3[0].Data)
+ assert.Equal(t, "mx2.foo.com.", r3[1].Data)
+
+ ep6 := endpoint.NewEndpoint("example.com", endpoint.RecordTypeTXT)
+ r4 := getMatchingDomainRecords(records, "example.com", ep6)
+ assert.Equal(t, 1, len(r4))
+ assert.Equal(t, "MYTXT", r4[0].Data)
}
func validateDigitalOceanZones(t *testing.T, zones []godo.Domain, expected []godo.Domain) {
@@ -663,7 +796,7 @@ func TestDigitalOceanAllRecords(t *testing.T) {
if err != nil {
t.Errorf("should not fail, %s", err)
}
- require.Equal(t, 5, len(records))
+ require.Equal(t, 7, len(records))
provider.Client = &mockDigitalOceanRecordsFail{}
_, err = provider.Records(ctx)
@@ -678,11 +811,15 @@ func TestDigitalOceanMergeRecordsByNameType(t *testing.T) {
endpoint.NewEndpoint("bar.example.com", "A", "1.2.3.4"),
endpoint.NewEndpoint("foo.example.com", "A", "5.6.7.8"),
endpoint.NewEndpoint("foo.example.com", "CNAME", "somewhere.out.there.com"),
+ endpoint.NewEndpoint("bar.example.com", "MX", "10 bar.mx1.com"),
+ endpoint.NewEndpoint("bar.example.com", "MX", "10 bar.mx2.com"),
+ endpoint.NewEndpoint("foo.example.com", "TXT", "txtone"),
+ endpoint.NewEndpoint("foo.example.com", "TXT", "txttwo"),
}
merged := mergeEndpointsByNameType(xs)
- assert.Equal(t, 3, len(merged))
+ assert.Equal(t, 5, len(merged))
sort.SliceStable(merged, func(i, j int) bool {
if merged[i].DNSName != merged[j].DNSName {
return merged[i].DNSName < merged[j].DNSName
@@ -693,14 +830,22 @@ func TestDigitalOceanMergeRecordsByNameType(t *testing.T) {
assert.Equal(t, "A", merged[0].RecordType)
assert.Equal(t, 1, len(merged[0].Targets))
assert.Equal(t, "1.2.3.4", merged[0].Targets[0])
-
- assert.Equal(t, "foo.example.com", merged[1].DNSName)
- assert.Equal(t, "A", merged[1].RecordType)
+ assert.Equal(t, "MX", merged[1].RecordType)
assert.Equal(t, 2, len(merged[1].Targets))
- assert.ElementsMatch(t, []string{"1.2.3.4", "5.6.7.8"}, merged[1].Targets)
+ assert.ElementsMatch(t, []string{"10 bar.mx1.com", "10 bar.mx2.com"}, merged[1].Targets)
assert.Equal(t, "foo.example.com", merged[2].DNSName)
- assert.Equal(t, "CNAME", merged[2].RecordType)
- assert.Equal(t, 1, len(merged[2].Targets))
- assert.Equal(t, "somewhere.out.there.com", merged[2].Targets[0])
+ assert.Equal(t, "A", merged[2].RecordType)
+ assert.Equal(t, 2, len(merged[2].Targets))
+ assert.ElementsMatch(t, []string{"1.2.3.4", "5.6.7.8"}, merged[2].Targets)
+
+ assert.Equal(t, "foo.example.com", merged[3].DNSName)
+ assert.Equal(t, "CNAME", merged[3].RecordType)
+ assert.Equal(t, 1, len(merged[3].Targets))
+ assert.Equal(t, "somewhere.out.there.com", merged[3].Targets[0])
+
+ assert.Equal(t, "foo.example.com", merged[4].DNSName)
+ assert.Equal(t, "TXT", merged[4].RecordType)
+ assert.Equal(t, 2, len(merged[4].Targets))
+ assert.ElementsMatch(t, []string{"txtone", "txttwo"}, merged[4].Targets)
}
diff --git a/provider/google/google.go b/provider/google/google.go
index 3502d6474a..a3222ad59e 100644
--- a/provider/google/google.go
+++ b/provider/google/google.go
@@ -445,6 +445,12 @@ func newRecord(ep *endpoint.Endpoint) *dns.ResourceRecordSet {
}
}
+ if ep.RecordType == endpoint.RecordTypeNS {
+ for i, nsRecord := range ep.Targets {
+ targets[i] = provider.EnsureTrailingDot(nsRecord)
+ }
+ }
+
// no annotation results in a Ttl of 0, default to 300 for backwards-compatibility
var ttl int64 = googleRecordTTL
if ep.RecordTTL.IsConfigured() {
diff --git a/provider/google/google_test.go b/provider/google/google_test.go
index 89a51b08be..0cec40a386 100644
--- a/provider/google/google_test.go
+++ b/provider/google/google_test.go
@@ -465,21 +465,25 @@ func TestNewFilteredRecords(t *testing.T) {
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, 1, "8.8.4.4"),
endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, 120, "8.8.4.4"),
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, 4000, "bar.elb.amazonaws.com"),
+ endpoint.NewEndpointWithTTL("update-test-ns.zone-1.ext-dns-test-2.gcp.zalan.do.", endpoint.RecordTypeNS, 120, "foo.elb.amazonaws.com"),
// test fallback to Ttl:300 when Ttl==0 :
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, 0, "8.8.8.8"),
endpoint.NewEndpointWithTTL("update-test-mx.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeMX, 6000, "10 mail.elb.amazonaws.com"),
endpoint.NewEndpoint("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, "8.8.8.8"),
endpoint.NewEndpoint("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, "qux.elb.amazonaws.com"),
+ endpoint.NewEndpoint("delete-test-ns.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeNS, "foo.elb.amazonaws.com"),
})
validateChangeRecords(t, records, []*dns.ResourceRecordSet{
{Name: "update-test.zone-2.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.4.4"}, Type: "A", Ttl: 1},
{Name: "delete-test.zone-2.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.4.4"}, Type: "A", Ttl: 120},
{Name: "update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"bar.elb.amazonaws.com."}, Type: "CNAME", Ttl: 4000},
+ {Name: "update-test-ns.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"foo.elb.amazonaws.com."}, Type: "NS", Ttl: 120},
{Name: "update-test.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.8.8"}, Type: "A", Ttl: 300},
{Name: "update-test-mx.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"10 mail.elb.amazonaws.com."}, Type: "MX", Ttl: 6000},
{Name: "delete-test.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"8.8.8.8"}, Type: "A", Ttl: 300},
{Name: "delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"qux.elb.amazonaws.com."}, Type: "CNAME", Ttl: 300},
+ {Name: "delete-test-ns.zone-1.ext-dns-test-2.gcp.zalan.do.", Rrdatas: []string{"foo.elb.amazonaws.com."}, Type: "NS", Ttl: 300},
})
}
diff --git a/provider/pdns/pdns.go b/provider/pdns/pdns.go
index 5a924b58a3..c0180725ab 100644
--- a/provider/pdns/pdns.go
+++ b/provider/pdns/pdns.go
@@ -22,6 +22,7 @@ import (
"crypto/tls"
"encoding/json"
"errors"
+ "fmt"
"math"
"net"
"net/http"
@@ -156,8 +157,7 @@ func (c *PDNSAPIClient) ListZones() (zones []pgo.Zone, resp *http.Response, err
return zones, resp, err
}
- log.Errorf("Unable to fetch zones. %v", err)
- return zones, resp, err
+ return zones, resp, provider.NewSoftError(fmt.Errorf("unable to list zones: %v", err))
}
// PartitionZones : Method returns a slice of zones that adhere to the domain filter and a slice of ones that does not adhere to the filter
@@ -190,8 +190,7 @@ func (c *PDNSAPIClient) ListZone(zoneID string) (zone pgo.Zone, resp *http.Respo
return zone, resp, err
}
- log.Errorf("Unable to list zone. %v", err)
- return zone, resp, err
+ return zone, resp, provider.NewSoftError(fmt.Errorf("unable to list zone: %v", err))
}
// PatchZone : Method used to update the contents of a particular zone from PowerDNS
@@ -208,8 +207,7 @@ func (c *PDNSAPIClient) PatchZone(zoneID string, zoneStruct pgo.Zone) (resp *htt
return resp, err
}
- log.Errorf("Unable to patch zone. %v", err)
- return resp, err
+ return resp, provider.NewSoftError(fmt.Errorf("unable to patch zone: %v", err))
}
// PDNSProvider is an implementation of the Provider interface for PowerDNS
@@ -315,7 +313,7 @@ func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changet
records := []pgo.Record{}
RecordType_ := ep.RecordType
for _, t := range ep.Targets {
- if ep.RecordType == "CNAME" || ep.RecordType == "ALIAS" {
+ if ep.RecordType == "CNAME" || ep.RecordType == "ALIAS" || ep.RecordType == "MX" || ep.RecordType == "SRV" {
t = provider.EnsureTrailingDot(t)
}
records = append(records, pgo.Record{Content: t})
@@ -336,7 +334,7 @@ func (p *PDNSProvider) ConvertEndpointsToZones(eps []*endpoint.Endpoint, changet
// DELETEs explicitly forbid a TTL, therefore only PATCHes need the TTL
if changetype == PdnsReplace {
if int64(ep.RecordTTL) > int64(math.MaxInt32) {
- return nil, errors.New("value of record TTL overflows, limited to int32")
+ return nil, provider.NewSoftError(fmt.Errorf("value of record TTL overflows, limited to int32"))
}
if ep.RecordTTL == 0 {
// No TTL was specified for the record, we use the default
@@ -419,8 +417,7 @@ func (p *PDNSProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpo
for _, zone := range filteredZones {
z, _, err := p.client.ListZone(zone.Id)
if err != nil {
- log.Warnf("Unable to fetch Records")
- return nil, err
+ return nil, provider.NewSoftError(fmt.Errorf("unable to fetch records: %v", err))
}
for _, rr := range z.Rrsets {
diff --git a/provider/pdns/pdns_test.go b/provider/pdns/pdns_test.go
index 01489da9c7..47df67cb10 100644
--- a/provider/pdns/pdns_test.go
+++ b/provider/pdns/pdns_test.go
@@ -18,7 +18,7 @@ package pdns
import (
"context"
- "errors"
+ "fmt"
"net/http"
"regexp"
"strings"
@@ -29,6 +29,7 @@ import (
"github.com/stretchr/testify/suite"
"sigs.k8s.io/external-dns/endpoint"
+ "sigs.k8s.io/external-dns/provider"
)
// FIXME: What do we do about labels?
@@ -117,6 +118,27 @@ var (
},
}
+ // RRSet with MX record
+ RRSetMXRecord = pgo.RrSet{
+ Name: "example.com.",
+ Type_: "MX",
+ Ttl: 300,
+ Records: []pgo.Record{
+ {Content: "10 mailhost1.example.com", Disabled: false, SetPtr: false},
+ {Content: "10 mailhost2.example.com", Disabled: false, SetPtr: false},
+ },
+ }
+
+ // RRSet with SRV record
+ RRSetSRVRecord = pgo.RrSet{
+ Name: "_service._tls.example.com.",
+ Type_: "SRV",
+ Ttl: 300,
+ Records: []pgo.Record{
+ {Content: "100 1 443 service.example.com", Disabled: false, SetPtr: false},
+ },
+ }
+
endpointsDisabledRecord = []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(300), "8.8.8.8"),
}
@@ -144,6 +166,8 @@ var (
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeTXT, endpoint.TTL(300), "'would smell as sweet'"),
endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeA, endpoint.TTL(300), "8.8.8.8", "8.8.4.4", "4.4.4.4"),
endpoint.NewEndpointWithTTL("alias.example.com", endpoint.RecordTypeCNAME, endpoint.TTL(300), "example.by.any.other.name.com"),
+ endpoint.NewEndpointWithTTL("example.com", endpoint.RecordTypeMX, endpoint.TTL(300), "10 mailhost1.example.com", "10 mailhost2.example.com"),
+ endpoint.NewEndpointWithTTL("_service._tls.example.com", endpoint.RecordTypeSRV, endpoint.TTL(300), "100 1 443 service.example.com"),
}
endpointsMultipleZones = []*endpoint.Endpoint{
@@ -233,7 +257,7 @@ var (
Type_: "Zone",
Url: "/api/v1/servers/localhost/zones/example.com.",
Kind: "Native",
- Rrsets: []pgo.RrSet{RRSetCNAMERecord, RRSetTXTRecord, RRSetMultipleRecords, RRSetALIASRecord},
+ Rrsets: []pgo.RrSet{RRSetCNAMERecord, RRSetTXTRecord, RRSetMultipleRecords, RRSetALIASRecord, RRSetMXRecord, RRSetSRVRecord},
}
ZoneEmptyToSimplePatch = pgo.Zone{
@@ -691,7 +715,7 @@ type PDNSAPIClientStubPatchZoneFailure struct {
// Just overwrite the PatchZone method to introduce a failure
func (c *PDNSAPIClientStubPatchZoneFailure) PatchZone(zoneID string, zoneStruct pgo.Zone) (*http.Response, error) {
- return nil, errors.New("Generic PDNS Error")
+ return nil, provider.NewSoftError(fmt.Errorf("Generic PDNS Error"))
}
/******************************************************************************/
@@ -703,7 +727,7 @@ type PDNSAPIClientStubListZoneFailure struct {
// Just overwrite the ListZone method to introduce a failure
func (c *PDNSAPIClientStubListZoneFailure) ListZone(zoneID string) (pgo.Zone, *http.Response, error) {
- return pgo.Zone{}, nil, errors.New("Generic PDNS Error")
+ return pgo.Zone{}, nil, provider.NewSoftError(fmt.Errorf("Generic PDNS Error"))
}
/******************************************************************************/
@@ -715,7 +739,7 @@ type PDNSAPIClientStubListZonesFailure struct {
// Just overwrite the ListZones method to introduce a failure
func (c *PDNSAPIClientStubListZonesFailure) ListZones() ([]pgo.Zone, *http.Response, error) {
- return []pgo.Zone{}, nil, errors.New("Generic PDNS Error")
+ return []pgo.Zone{}, nil, provider.NewSoftError(fmt.Errorf("Generic PDNS Error"))
}
/******************************************************************************/
@@ -879,12 +903,14 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSRecords() {
}
_, err = p.Records(ctx)
assert.NotNil(suite.T(), err)
+ assert.ErrorIs(suite.T(), err, provider.SoftError)
p = &PDNSProvider{
client: &PDNSAPIClientStubListZonesFailure{},
}
_, err = p.Records(ctx)
assert.NotNil(suite.T(), err)
+ assert.ErrorIs(suite.T(), err, provider.SoftError)
}
func (suite *NewPDNSProviderTestSuite) TestPDNSConvertEndpointsToZones() {
@@ -944,6 +970,20 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSConvertEndpointsToZones() {
}
}
+ // Check endpoints of type MX and SRV always have their values end with a trailing dot.
+ zlist, err = p.ConvertEndpointsToZones(endpointsMixedRecords, PdnsReplace)
+ assert.Nil(suite.T(), err)
+
+ for _, z := range zlist {
+ for _, rs := range z.Rrsets {
+ if rs.Type_ == "MX" || rs.Type_ == "SRV" {
+ for _, r := range rs.Records {
+ assert.Equal(suite.T(), uint8(0x2e), r.Content[len(r.Content)-1])
+ }
+ }
+ }
+ }
+
// Check endpoints of type CNAME are converted to ALIAS on the domain apex
zlist, err = p.ConvertEndpointsToZones(endpointsApexRecords, PdnsReplace)
assert.Nil(suite.T(), err)
@@ -1024,6 +1064,7 @@ func (suite *NewPDNSProviderTestSuite) TestPDNSmutateRecords() {
// Check inserting endpoints from a single zone
err = p.mutateRecords(endpointsSimpleRecord, pdnsChangeType("REPLACE"))
assert.NotNil(suite.T(), err)
+ assert.ErrorIs(suite.T(), err, provider.SoftError)
}
func (suite *NewPDNSProviderTestSuite) TestPDNSClientPartitionZones() {
diff --git a/provider/pihole/client.go b/provider/pihole/client.go
index f7410b10a0..0d1c715200 100644
--- a/provider/pihole/client.go
+++ b/provider/pihole/client.go
@@ -33,6 +33,7 @@ import (
"golang.org/x/net/html"
"sigs.k8s.io/external-dns/endpoint"
+ "sigs.k8s.io/external-dns/provider"
)
// piholeAPI declares the "API" actions performed against the Pihole server.
@@ -224,6 +225,9 @@ func (p *piholeClient) apply(ctx context.Context, action string, ep *endpoint.En
log.Infof("%s %s IN %s -> %s", action, ep.DNSName, ep.RecordType, ep.Targets[0])
form := p.newDNSActionForm(action, ep)
+ if strings.Contains(ep.DNSName, "*") {
+ return provider.NewSoftError(errors.New("UNSUPPORTED: Pihole DNS names cannot return wildcard"))
+ }
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, strings.NewReader(form.Encode()))
if err != nil {
return err
diff --git a/provider/pihole/client_test.go b/provider/pihole/client_test.go
index 8e87d2bde3..92b0a87560 100644
--- a/provider/pihole/client_test.go
+++ b/provider/pihole/client_test.go
@@ -353,6 +353,16 @@ func TestCreateRecord(t *testing.T) {
if err := cl.createRecord(context.Background(), ep); err != nil {
t.Fatal(err)
}
+
+ // Test create a wildcard record and ensure it fails
+ ep = &endpoint.Endpoint{
+ DNSName: "*.example.com",
+ Targets: []string{"192.168.1.1"},
+ RecordType: endpoint.RecordTypeA,
+ }
+ if err := cl.createRecord(context.Background(), ep); err == nil {
+ t.Fatal(err)
+ }
}
func TestDeleteRecord(t *testing.T) {
diff --git a/provider/rdns/rdns.go b/provider/rdns/rdns.go
deleted file mode 100644
index 10766c8765..0000000000
--- a/provider/rdns/rdns.go
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
-Copyright 2019 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 rdns
-
-import (
- "context"
- "crypto/tls"
- "crypto/x509"
- "encoding/json"
- "fmt"
- "math/rand"
- "os"
- "regexp"
- "strings"
- "time"
-
- "github.com/pkg/errors"
- log "github.com/sirupsen/logrus"
- clientv3 "go.etcd.io/etcd/client/v3"
-
- "sigs.k8s.io/external-dns/endpoint"
- "sigs.k8s.io/external-dns/plan"
- "sigs.k8s.io/external-dns/provider"
-)
-
-const (
- etcdTimeout = 5 * time.Second
- rdnsMaxHosts = 10
- rdnsOriginalLabel = "originalText"
- rdnsPrefix = "/rdnsv3"
- rdnsTimeout = 5 * time.Second
-)
-
-func init() {
- rand.New(rand.NewSource(time.Now().UnixNano()))
-}
-
-// RDNSClient is an interface to work with Rancher DNS(RDNS) records in etcdv3 backend.
-type RDNSClient interface {
- Get(key string) ([]RDNSRecord, error)
- List(rootDomain string) ([]RDNSRecord, error)
- Set(value RDNSRecord) error
- Delete(key string) error
-}
-
-// RDNSConfig contains configuration to create a new Rancher DNS(RDNS) provider.
-type RDNSConfig struct {
- DryRun bool
- DomainFilter endpoint.DomainFilter
- RootDomain string
-}
-
-// RDNSProvider is an implementation of Provider for Rancher DNS(RDNS).
-type RDNSProvider struct {
- provider.BaseProvider
- client RDNSClient
- dryRun bool
- domainFilter endpoint.DomainFilter
- rootDomain string
-}
-
-// RDNSRecord represents Rancher DNS(RDNS) etcdv3 record.
-type RDNSRecord struct {
- AggregationHosts []string `json:"aggregation_hosts,omitempty"`
- Host string `json:"host,omitempty"`
- Text string `json:"text,omitempty"`
- TTL uint32 `json:"ttl,omitempty"`
- Key string `json:"-"`
-}
-
-// RDNSRecordType represents Rancher DNS(RDNS) etcdv3 record type.
-type RDNSRecordType struct {
- Type string `json:"type,omitempty"`
- Domain string `json:"domain,omitempty"`
-}
-
-type etcdv3Client struct {
- client *clientv3.Client
- ctx context.Context
-}
-
-var _ RDNSClient = etcdv3Client{}
-
-// NewRDNSProvider initializes a new Rancher DNS(RDNS) based Provider.
-func NewRDNSProvider(config RDNSConfig) (*RDNSProvider, error) {
- client, err := newEtcdv3Client()
- if err != nil {
- return nil, err
- }
- domain := os.Getenv("RDNS_ROOT_DOMAIN")
- if domain == "" {
- return nil, errors.New("needed root domain environment")
- }
- return &RDNSProvider{
- client: client,
- dryRun: config.DryRun,
- domainFilter: config.DomainFilter,
- rootDomain: domain,
- }, nil
-}
-
-// Records returns all DNS records found in Rancher DNS(RDNS) etcdv3 backend. Depending on the record fields
-// it may be mapped to one or two records of type A, TXT, A+TXT.
-func (p RDNSProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
- var result []*endpoint.Endpoint
-
- rs, err := p.client.List(p.rootDomain)
- if err != nil {
- return nil, err
- }
-
- for _, r := range rs {
- domains := strings.Split(strings.TrimPrefix(r.Key, rdnsPrefix+"/"), "/")
- keyToDNSNameSplits(domains)
- dnsName := strings.Join(domains, ".")
- if !p.domainFilter.Match(dnsName) {
- continue
- }
-
- // only return rdnsMaxHosts at most
- if len(r.AggregationHosts) > 0 {
- if len(r.AggregationHosts) > rdnsMaxHosts {
- r.AggregationHosts = r.AggregationHosts[:rdnsMaxHosts]
- }
- ep := endpoint.NewEndpointWithTTL(
- dnsName,
- endpoint.RecordTypeA,
- endpoint.TTL(r.TTL),
- r.AggregationHosts...,
- )
- ep.Labels[rdnsOriginalLabel] = r.Text
- result = append(result, ep)
- }
- if r.Text != "" {
- ep := endpoint.NewEndpoint(
- dnsName,
- endpoint.RecordTypeTXT,
- r.Text,
- )
- result = append(result, ep)
- }
- }
-
- return result, nil
-}
-
-// ApplyChanges stores changes back to etcdv3 converting them to Rancher DNS(RDNS) format and aggregating A and TXT records.
-func (p RDNSProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
- grouped := map[string][]*endpoint.Endpoint{}
-
- for _, ep := range changes.Create {
- grouped[ep.DNSName] = append(grouped[ep.DNSName], ep)
- }
-
- for _, ep := range changes.UpdateNew {
- if ep.RecordType == endpoint.RecordTypeA {
- // append useless domain records to the changes.Delete
- if err := p.filterAndRemoveUseless(ep, changes); err != nil {
- return err
- }
- }
- grouped[ep.DNSName] = append(grouped[ep.DNSName], ep)
- }
-
- for dnsName, group := range grouped {
- if !p.domainFilter.Match(dnsName) {
- log.Debugf("Skipping record %s because it was filtered out by the specified --domain-filter", dnsName)
- continue
- }
-
- var rs []RDNSRecord
-
- for _, ep := range group {
- if ep.RecordType == endpoint.RecordTypeTXT {
- continue
- }
- for _, target := range ep.Targets {
- rs = append(rs, RDNSRecord{
- Host: target,
- Text: ep.Labels[rdnsOriginalLabel],
- Key: keyFor(ep.DNSName) + "/" + formatKey(target),
- TTL: uint32(ep.RecordTTL),
- })
- }
- }
-
- // Add the TXT attribute to the existing A record
- for _, ep := range group {
- if ep.RecordType != endpoint.RecordTypeTXT {
- continue
- }
- for i, r := range rs {
- if strings.Contains(r.Key, keyFor(ep.DNSName)) {
- r.Text = ep.Targets[0]
- rs[i] = r
- }
- }
- }
-
- for _, r := range rs {
- log.Infof("Add/set key %s to Host=%s, Text=%s, TTL=%d", r.Key, r.Host, r.Text, r.TTL)
- if !p.dryRun {
- err := p.client.Set(r)
- if err != nil {
- return err
- }
- }
- }
- }
-
- for _, ep := range changes.Delete {
- key := keyFor(ep.DNSName)
- log.Infof("Delete key %s", key)
- if !p.dryRun {
- err := p.client.Delete(key)
- if err != nil {
- return err
- }
- }
- }
-
- return nil
-}
-
-// filterAndRemoveUseless filter and remove useless records.
-func (p *RDNSProvider) filterAndRemoveUseless(ep *endpoint.Endpoint, changes *plan.Changes) error {
- rs, err := p.client.Get(keyFor(ep.DNSName))
- if err != nil {
- return err
- }
- for _, r := range rs {
- exist := false
- for _, target := range ep.Targets {
- if strings.Contains(r.Key, formatKey(target)) {
- exist = true
- continue
- }
- }
- if !exist {
- ds := strings.Split(strings.TrimPrefix(r.Key, rdnsPrefix+"/"), "/")
- keyToDNSNameSplits(ds)
- changes.Delete = append(changes.Delete, &endpoint.Endpoint{
- DNSName: strings.Join(ds, "."),
- })
- }
- }
- return nil
-}
-
-// newEtcdv3Client is an etcdv3 client constructor.
-func newEtcdv3Client() (RDNSClient, error) {
- cfg := &clientv3.Config{}
-
- endpoints := os.Getenv("ETCD_URLS")
- ca := os.Getenv("ETCD_CA_FILE")
- cert := os.Getenv("ETCD_CERT_FILE")
- key := os.Getenv("ETCD_KEY_FILE")
- name := os.Getenv("ETCD_TLS_SERVER_NAME")
- insecure := os.Getenv("ETCD_TLS_INSECURE")
-
- if endpoints == "" {
- endpoints = "http://localhost:2379"
- }
-
- urls := strings.Split(endpoints, ",")
- scheme := strings.ToLower(urls[0])[0:strings.Index(strings.ToLower(urls[0]), "://")]
-
- switch scheme {
- case "http":
- cfg.Endpoints = urls
- case "https":
- var certificates []tls.Certificate
-
- insecure = strings.ToLower(insecure)
- isInsecure := insecure == "true" || insecure == "yes" || insecure == "1"
-
- if ca != "" && key == "" || cert == "" && key != "" {
- return nil, errors.New("either both cert and key or none must be provided")
- }
-
- if cert != "" {
- cert, err := tls.LoadX509KeyPair(cert, key)
- if err != nil {
- return nil, fmt.Errorf("could not load TLS cert: %w", err)
- }
- certificates = append(certificates, cert)
- }
-
- config := &tls.Config{
- Certificates: certificates,
- InsecureSkipVerify: isInsecure,
- ServerName: name,
- }
-
- if ca != "" {
- roots := x509.NewCertPool()
- pem, err := os.ReadFile(ca)
- if err != nil {
- return nil, fmt.Errorf("error reading %s: %w", ca, err)
- }
- ok := roots.AppendCertsFromPEM(pem)
- if !ok {
- return nil, fmt.Errorf("could not read root certs: %w", err)
- }
- config.RootCAs = roots
- }
-
- cfg.Endpoints = urls
- cfg.TLS = config
- default:
- return nil, errors.New("etcdv3 URLs must start with either http:// or https://")
- }
-
- c, err := clientv3.New(*cfg)
- if err != nil {
- return nil, err
- }
-
- return etcdv3Client{c, context.Background()}, nil
-}
-
-// Get return A records stored in etcdv3 stored anywhere under the given key (recursively).
-func (c etcdv3Client) Get(key string) ([]RDNSRecord, error) {
- ctx, cancel := context.WithTimeout(c.ctx, rdnsTimeout)
- defer cancel()
-
- result, err := c.client.Get(ctx, key, clientv3.WithPrefix())
- if err != nil {
- return nil, err
- }
-
- rs := make([]RDNSRecord, 0)
- for _, v := range result.Kvs {
- r := new(RDNSRecord)
- if err := json.Unmarshal(v.Value, r); err != nil {
- return nil, fmt.Errorf("%s: %w", v.Key, err)
- }
- r.Key = string(v.Key)
- rs = append(rs, *r)
- }
-
- return rs, nil
-}
-
-// List return all records stored in etcdv3 stored anywhere under the given rootDomain (recursively).
-func (c etcdv3Client) List(rootDomain string) ([]RDNSRecord, error) {
- ctx, cancel := context.WithTimeout(c.ctx, rdnsTimeout)
- defer cancel()
-
- path := keyFor(rootDomain)
-
- result, err := c.client.Get(ctx, path, clientv3.WithPrefix())
- if err != nil {
- return nil, err
- }
-
- return c.aggregationRecords(result)
-}
-
-// Set persists records data into etcdv3.
-func (c etcdv3Client) Set(r RDNSRecord) error {
- ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
- defer cancel()
-
- v, err := json.Marshal(&r)
- if err != nil {
- return err
- }
-
- if r.Text == "" && r.Host == "" {
- return nil
- }
-
- _, err = c.client.Put(ctx, r.Key, string(v))
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// Delete deletes record from etcdv3.
-func (c etcdv3Client) Delete(key string) error {
- ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
- defer cancel()
-
- _, err := c.client.Delete(ctx, key, clientv3.WithPrefix())
- return err
-}
-
-// aggregationRecords will aggregation multi A records under the given path.
-// e.g. A: 1_1_1_1.xxx.lb.rancher.cloud & 2_2_2_2.sample.lb.rancher.cloud => sample.lb.rancher.cloud {"aggregation_hosts": ["1.1.1.1", "2.2.2.2"]}
-// e.g. TXT: sample.lb.rancher.cloud => sample.lb.rancher.cloud => {"text": "xxx"}
-func (c etcdv3Client) aggregationRecords(result *clientv3.GetResponse) ([]RDNSRecord, error) {
- var rs []RDNSRecord
- bx := make(map[RDNSRecordType]RDNSRecord)
-
- for _, n := range result.Kvs {
- r := new(RDNSRecord)
- if err := json.Unmarshal(n.Value, r); err != nil {
- return nil, fmt.Errorf("%s: %w", n.Key, err)
- }
-
- r.Key = string(n.Key)
-
- if r.Host == "" && r.Text == "" {
- continue
- }
-
- if r.Host != "" {
- c := RDNSRecord{
- AggregationHosts: r.AggregationHosts,
- Host: r.Host,
- Text: r.Text,
- TTL: r.TTL,
- Key: r.Key,
- }
- n, isContinue := appendRecords(c, endpoint.RecordTypeA, bx, rs)
- if isContinue {
- continue
- }
- rs = n
- }
-
- if r.Text != "" && r.Host == "" {
- c := RDNSRecord{
- AggregationHosts: []string{},
- Host: r.Host,
- Text: r.Text,
- TTL: r.TTL,
- Key: r.Key,
- }
- n, isContinue := appendRecords(c, endpoint.RecordTypeTXT, bx, rs)
- if isContinue {
- continue
- }
- rs = n
- }
- }
-
- return rs, nil
-}
-
-// appendRecords append record to an array
-func appendRecords(r RDNSRecord, dnsType string, bx map[RDNSRecordType]RDNSRecord, rs []RDNSRecord) ([]RDNSRecord, bool) {
- dnsName := keyToParentDNSName(r.Key)
- bt := RDNSRecordType{Domain: dnsName, Type: dnsType}
- if v, ok := bx[bt]; ok {
- // skip the TXT records if already added to record list.
- // append A record if dnsName already added to record list but not found the value.
- // the same record might be found in multiple etcdv3 nodes.
- if bt.Type == endpoint.RecordTypeA {
- exist := false
- for _, h := range v.AggregationHosts {
- if h == r.Host {
- exist = true
- break
- }
- }
- if !exist {
- for i, t := range rs {
- if !strings.HasPrefix(r.Key, t.Key) {
- continue
- }
- t.Host = ""
- t.AggregationHosts = append(t.AggregationHosts, r.Host)
- bx[bt] = t
- rs[i] = t
- }
- }
- }
- return rs, true
- }
-
- if bt.Type == endpoint.RecordTypeA {
- r.AggregationHosts = append(r.AggregationHosts, r.Host)
- }
-
- r.Key = rdnsPrefix + dnsNameToKey(dnsName)
- r.Host = ""
- bx[bt] = r
- rs = append(rs, r)
- return rs, false
-}
-
-// keyFor used to get a path as etcdv3 preferred.
-// e.g. sample.lb.rancher.cloud => /rdnsv3/cloud/rancher/lb/sample
-func keyFor(fqdn string) string {
- return rdnsPrefix + dnsNameToKey(fqdn)
-}
-
-// keyToParentDNSName used to get dnsName.
-// e.g. /rdnsv3/cloud/rancher/lb/sample/xxx => xxx.sample.lb.rancher.cloud
-// e.g. /rdnsv3/cloud/rancher/lb/sample/xxx/1_1_1_1 => xxx.sample.lb.rancher.cloud
-func keyToParentDNSName(key string) string {
- ds := strings.Split(strings.TrimPrefix(key, rdnsPrefix+"/"), "/")
- keyToDNSNameSplits(ds)
-
- dns := strings.Join(ds, ".")
- prefix := strings.Split(dns, ".")[0]
-
- p := `^\d{1,3}_\d{1,3}_\d{1,3}_\d{1,3}$`
- m, _ := regexp.MatchString(p, prefix)
- if prefix != "" && strings.Contains(prefix, "_") && m {
- // 1_1_1_1.xxx.sample.lb.rancher.cloud => xxx.sample.lb.rancher.cloud
- return strings.Join(strings.Split(dns, ".")[1:], ".")
- }
-
- return dns
-}
-
-// dnsNameToKey used to convert domain to a path as etcdv3 preferred.
-// e.g. sample.lb.rancher.cloud => /cloud/rancher/lb/sample
-func dnsNameToKey(domain string) string {
- ss := strings.Split(domain, ".")
- last := len(ss) - 1
- for i := 0; i < len(ss)/2; i++ {
- ss[i], ss[last-i] = ss[last-i], ss[i]
- }
- return "/" + strings.Join(ss, "/")
-}
-
-// keyToDNSNameSplits used to reverse etcdv3 path to domain splits.
-// e.g. /cloud/rancher/lb/sample => [sample lb rancher cloud]
-func keyToDNSNameSplits(ss []string) {
- for i := 0; i < len(ss)/2; i++ {
- j := len(ss) - i - 1
- ss[i], ss[j] = ss[j], ss[i]
- }
-}
-
-// formatKey used to format a key as etcdv3 preferred
-// e.g. 1.1.1.1 => 1_1_1_1
-// e.g. sample.lb.rancher.cloud => sample_lb_rancher_cloud
-func formatKey(key string) string {
- return strings.Replace(key, ".", "_", -1)
-}
diff --git a/provider/rdns/rdns_test.go b/provider/rdns/rdns_test.go
deleted file mode 100644
index 99358f4ce3..0000000000
--- a/provider/rdns/rdns_test.go
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
-Copyright 2019 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 rdns
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "strings"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "go.etcd.io/etcd/api/v3/mvccpb"
- clientv3 "go.etcd.io/etcd/client/v3"
-
- "sigs.k8s.io/external-dns/endpoint"
- "sigs.k8s.io/external-dns/plan"
-)
-
-type fakeEtcdv3Client struct {
- rs map[string]RDNSRecord
-}
-
-func (c fakeEtcdv3Client) Get(key string) ([]RDNSRecord, error) {
- rs := make([]RDNSRecord, 0)
- for k, v := range c.rs {
- if strings.Contains(k, key) {
- rs = append(rs, v)
- }
- }
- return rs, nil
-}
-
-func (c fakeEtcdv3Client) List(rootDomain string) ([]RDNSRecord, error) {
- var result []RDNSRecord
- for key, value := range c.rs {
- rootPath := rdnsPrefix + dnsNameToKey(rootDomain)
- if strings.HasPrefix(key, rootPath) {
- value.Key = key
- result = append(result, value)
- }
- }
-
- r := &clientv3.GetResponse{}
-
- for _, v := range result {
- b, err := json.Marshal(v)
- if err != nil {
- return nil, err
- }
-
- k := &mvccpb.KeyValue{
- Key: []byte(v.Key),
- Value: b,
- }
-
- r.Kvs = append(r.Kvs, k)
- }
-
- return c.aggregationRecords(r)
-}
-
-func (c fakeEtcdv3Client) Set(r RDNSRecord) error {
- c.rs[r.Key] = r
- return nil
-}
-
-func (c fakeEtcdv3Client) Delete(key string) error {
- ks := make([]string, 0)
- for k := range c.rs {
- if strings.Contains(k, key) {
- ks = append(ks, k)
- }
- }
-
- for _, v := range ks {
- delete(c.rs, v)
- }
-
- return nil
-}
-
-func TestARecordTranslation(t *testing.T) {
- expectedTarget1 := "1.2.3.4"
- expectedTarget2 := "2.3.4.5"
- expectedTargets := []string{expectedTarget1, expectedTarget2}
- expectedDNSName := "p1xaf1.lb.rancher.cloud"
- expectedRecordType := endpoint.RecordTypeA
-
- client := fakeEtcdv3Client{
- map[string]RDNSRecord{
- "/rdnsv3/cloud/rancher/lb/p1xaf1/1_2_3_4": {Host: expectedTarget1},
- "/rdnsv3/cloud/rancher/lb/p1xaf1/2_3_4_5": {Host: expectedTarget2},
- },
- }
-
- provider := RDNSProvider{
- client: client,
- rootDomain: "lb.rancher.cloud",
- }
-
- endpoints, err := provider.Records(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- if len(endpoints) != 1 {
- t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
- }
-
- ep := endpoints[0]
- if ep.DNSName != expectedDNSName {
- t.Errorf("got unexpected DNS name: %s != %s", ep.DNSName, expectedDNSName)
- }
- assert.Contains(t, expectedTargets, ep.Targets[0])
- assert.Contains(t, expectedTargets, ep.Targets[1])
- if ep.RecordType != expectedRecordType {
- t.Errorf("got unexpected DNS record type: %s != %s", ep.RecordType, expectedRecordType)
- }
-}
-
-func TestTXTRecordTranslation(t *testing.T) {
- expectedTarget := "string"
- expectedDNSName := "p1xaf1.lb.rancher.cloud"
- expectedRecordType := endpoint.RecordTypeTXT
-
- client := fakeEtcdv3Client{
- map[string]RDNSRecord{
- "/rdnsv3/cloud/rancher/lb/p1xaf1": {Text: expectedTarget},
- },
- }
-
- provider := RDNSProvider{
- client: client,
- rootDomain: "lb.rancher.cloud",
- }
-
- endpoints, err := provider.Records(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- if len(endpoints) != 1 {
- t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
- }
- if endpoints[0].DNSName != expectedDNSName {
- t.Errorf("got unexpected DNS name: %s != %s", endpoints[0].DNSName, expectedDNSName)
- }
- if endpoints[0].Targets[0] != expectedTarget {
- t.Errorf("got unexpected DNS target: %s != %s", endpoints[0].Targets[0], expectedTarget)
- }
- if endpoints[0].RecordType != expectedRecordType {
- t.Errorf("got unexpected DNS record type: %s != %s", endpoints[0].RecordType, expectedRecordType)
- }
-}
-
-func TestAWithTXTRecordTranslation(t *testing.T) {
- expectedTargets := map[string]string{
- endpoint.RecordTypeA: "1.2.3.4",
- endpoint.RecordTypeTXT: "string",
- }
- expectedDNSName := "p1xaf1.lb.rancher.cloud"
-
- client := fakeEtcdv3Client{
- map[string]RDNSRecord{
- "/rdnsv3/cloud/rancher/lb/p1xaf1": {Host: "1.2.3.4", Text: "string"},
- },
- }
-
- provider := RDNSProvider{
- client: client,
- rootDomain: "lb.rancher.cloud",
- }
-
- endpoints, err := provider.Records(context.Background())
- if err != nil {
- t.Fatal(err)
- }
-
- if len(endpoints) != len(expectedTargets) {
- t.Fatalf("got unexpected number of endpoints: %d", len(endpoints))
- }
-
- for _, ep := range endpoints {
- expectedTarget := expectedTargets[ep.RecordType]
- if expectedTarget == "" {
- t.Errorf("got unexpected DNS record type: %s", ep.RecordType)
- continue
- }
- delete(expectedTargets, ep.RecordType)
-
- if ep.DNSName != expectedDNSName {
- t.Errorf("got unexpected DNS name: %s != %s", ep.DNSName, expectedDNSName)
- }
-
- if ep.Targets[0] != expectedTarget {
- t.Errorf("got unexpected DNS target: %s != %s", ep.Targets[0], expectedTarget)
- }
- }
-}
-
-func TestRDNSApplyChanges(t *testing.T) {
- client := fakeEtcdv3Client{
- map[string]RDNSRecord{},
- }
-
- provider := RDNSProvider{
- client: client,
- rootDomain: "lb.rancher.cloud",
- }
-
- changes1 := &plan.Changes{
- Create: []*endpoint.Endpoint{
- endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "5.5.5.5", "6.6.6.6"),
- endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeTXT, "string1"),
- },
- }
-
- if err := provider.ApplyChanges(context.Background(), changes1); err != nil {
- t.Error(err)
- }
-
- expectedRecords1 := map[string]RDNSRecord{
- "/rdnsv3/cloud/rancher/lb/p1xaf1/5_5_5_5": {Host: "5.5.5.5", Text: "string1"},
- "/rdnsv3/cloud/rancher/lb/p1xaf1/6_6_6_6": {Host: "6.6.6.6", Text: "string1"},
- }
-
- client.validateRecords(client.rs, expectedRecords1, t)
-
- changes2 := &plan.Changes{
- Create: []*endpoint.Endpoint{
- endpoint.NewEndpoint("abx1v1.lb.rancher.cloud", endpoint.RecordTypeA, "7.7.7.7"),
- },
- UpdateNew: []*endpoint.Endpoint{
- endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "8.8.8.8", "9.9.9.9"),
- },
- }
-
- records, _ := provider.Records(context.Background())
- for _, ep := range records {
- if ep.DNSName == "p1xaf1.lb.rancher.cloud" {
- changes2.UpdateOld = append(changes2.UpdateOld, ep)
- }
- }
-
- if err := provider.ApplyChanges(context.Background(), changes2); err != nil {
- t.Error(err)
- }
-
- expectedRecords2 := map[string]RDNSRecord{
- "/rdnsv3/cloud/rancher/lb/p1xaf1/8_8_8_8": {Host: "8.8.8.8"},
- "/rdnsv3/cloud/rancher/lb/p1xaf1/9_9_9_9": {Host: "9.9.9.9"},
- "/rdnsv3/cloud/rancher/lb/abx1v1/7_7_7_7": {Host: "7.7.7.7"},
- }
-
- client.validateRecords(client.rs, expectedRecords2, t)
-
- changes3 := &plan.Changes{
- Delete: []*endpoint.Endpoint{
- endpoint.NewEndpoint("p1xaf1.lb.rancher.cloud", endpoint.RecordTypeA, "8.8.8.8", "9.9.9.9"),
- },
- }
-
- if err := provider.ApplyChanges(context.Background(), changes3); err != nil {
- t.Error(err)
- }
-
- expectedRecords3 := map[string]RDNSRecord{
- "/rdnsv3/cloud/rancher/lb/abx1v1/7_7_7_7": {Host: "7.7.7.7"},
- }
-
- client.validateRecords(client.rs, expectedRecords3, t)
-}
-
-func (c fakeEtcdv3Client) aggregationRecords(result *clientv3.GetResponse) ([]RDNSRecord, error) {
- var rs []RDNSRecord
- bx := make(map[RDNSRecordType]RDNSRecord)
-
- for _, n := range result.Kvs {
- r := new(RDNSRecord)
- if err := json.Unmarshal(n.Value, r); err != nil {
- return nil, fmt.Errorf("%s: %s", n.Key, err.Error())
- }
-
- r.Key = string(n.Key)
-
- if r.Host == "" && r.Text == "" {
- continue
- }
-
- if r.Host != "" {
- c := RDNSRecord{
- AggregationHosts: r.AggregationHosts,
- Host: r.Host,
- Text: r.Text,
- TTL: r.TTL,
- Key: r.Key,
- }
- n, isContinue := appendRecords(c, endpoint.RecordTypeA, bx, rs)
- if isContinue {
- continue
- }
- rs = n
- }
-
- if r.Text != "" && r.Host == "" {
- c := RDNSRecord{
- AggregationHosts: []string{},
- Host: r.Host,
- Text: r.Text,
- TTL: r.TTL,
- Key: r.Key,
- }
- n, isContinue := appendRecords(c, endpoint.RecordTypeTXT, bx, rs)
- if isContinue {
- continue
- }
- rs = n
- }
- }
-
- return rs, nil
-}
-
-func (c fakeEtcdv3Client) validateRecords(rs, expectedRs map[string]RDNSRecord, t *testing.T) {
- if len(rs) != len(expectedRs) {
- t.Errorf("wrong number of records: %d != %d", len(rs), len(expectedRs))
- }
- for key, value := range rs {
- if _, ok := expectedRs[key]; !ok {
- t.Errorf("unexpected record %s", key)
- continue
- }
- expected := expectedRs[key]
- delete(expectedRs, key)
- if value.Host != expected.Host {
- t.Errorf("wrong host for record %s: %s != %s", key, value.Host, expected.Host)
- }
- if value.Text != expected.Text {
- t.Errorf("wrong text for record %s: %s != %s", key, value.Text, expected.Text)
- }
- }
-}
diff --git a/registry/txt.go b/registry/txt.go
index 12445bcb1d..e594a40f6d 100644
--- a/registry/txt.go
+++ b/registry/txt.go
@@ -406,6 +406,9 @@ func (pr affixNameMapper) toEndpointName(txtDNSName string) (endpointName string
domainWithSuffix := strings.Join(DNSName[:1+dc], ".")
r, rType := pr.dropAffixExtractType(domainWithSuffix)
+ if !strings.Contains(lowerDNSName, ".") {
+ return r, rType
+ }
return r + "." + DNSName[1+dc], rType
}
return "", ""
diff --git a/registry/txt_test.go b/registry/txt_test.go
index 515163cfa8..77ef84262f 100644
--- a/registry/txt_test.go
+++ b/registry/txt_test.go
@@ -1300,6 +1300,13 @@ func TestToEndpointNameNewTXT(t *testing.T) {
recordType: "A",
txtDomain: "fooa-example.com",
},
+ {
+ name: "suffix",
+ mapper: newaffixNameMapper("", "foo", ""),
+ domain: "example",
+ recordType: "AAAA",
+ txtDomain: "aaaa-examplefoo",
+ },
{
name: "suffix",
mapper: newaffixNameMapper("", "foo", ""),
diff --git a/scripts/install-ko.sh b/scripts/install-ko.sh
index 47307f4d42..91cf30392c 100755
--- a/scripts/install-ko.sh
+++ b/scripts/install-ko.sh
@@ -20,5 +20,5 @@ set -o pipefail
if ! command -v ko &> /dev/null; then
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1
- go install github.com/google/ko@v0.14.1
+ go install github.com/google/ko@v0.17.1
fi
diff --git a/source/node.go b/source/node.go
index 81e40755dc..c35b3883e2 100644
--- a/source/node.go
+++ b/source/node.go
@@ -102,6 +102,11 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro
continue
}
+ if node.Spec.Unschedulable {
+ log.Debugf("Skipping node %s because it is unschedulable", node.Name)
+ continue
+ }
+
log.Debugf("creating endpoint for node %s", node.Name)
ttl := getTTLFromAnnotations(node.Annotations, fmt.Sprintf("node/%s", node.Name))
diff --git a/source/node_test.go b/source/node_test.go
index 9ce4591dfb..bf047bae84 100644
--- a/source/node_test.go
+++ b/source/node_test.go
@@ -101,6 +101,7 @@ func testNodeSourceEndpoints(t *testing.T) {
nodeAddresses []v1.NodeAddress
labels map[string]string
annotations map[string]string
+ unschedulable bool // default to false
expected []*endpoint.Endpoint
expectError bool
}{
@@ -321,6 +322,13 @@ func testNodeSourceEndpoints(t *testing.T) {
{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}, RecordTTL: endpoint.TTL(10)},
},
},
+ {
+ title: "unschedulable node return nothing",
+ nodeName: "node1",
+ nodeAddresses: []v1.NodeAddress{{Type: v1.NodeExternalIP, Address: "1.2.3.4"}},
+ unschedulable: true,
+ expected: []*endpoint.Endpoint{},
+ },
} {
tc := tc
t.Run(tc.title, func(t *testing.T) {
@@ -342,6 +350,9 @@ func testNodeSourceEndpoints(t *testing.T) {
Labels: tc.labels,
Annotations: tc.annotations,
},
+ Spec: v1.NodeSpec{
+ Unschedulable: tc.unschedulable,
+ },
Status: v1.NodeStatus{
Addresses: tc.nodeAddresses,
},
diff --git a/source/source.go b/source/source.go
index 132f40dcd9..012cf82739 100644
--- a/source/source.go
+++ b/source/source.go
@@ -21,7 +21,7 @@ import (
"context"
"fmt"
"math"
- "net"
+ "net/netip"
"reflect"
"strconv"
"strings"
@@ -255,11 +255,12 @@ func getTargetsFromTargetAnnotation(annotations map[string]string) endpoint.Targ
}
// suitableType returns the DNS resource record type suitable for the target.
-// In this case type A for IPs and type CNAME for everything else.
+// In this case type A/AAAA for IPs and type CNAME for everything else.
func suitableType(target string) string {
- if net.ParseIP(target) != nil && net.ParseIP(target).To4() != nil {
+ netIP, err := netip.ParseAddr(target)
+ if err == nil && netIP.Is4() {
return endpoint.RecordTypeA
- } else if net.ParseIP(target) != nil && net.ParseIP(target).To16() != nil {
+ } else if err == nil && netIP.Is6() {
return endpoint.RecordTypeAAAA
}
return endpoint.RecordTypeCNAME
@@ -276,14 +277,8 @@ func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoin
for _, t := range targets {
switch suitableType(t) {
case endpoint.RecordTypeA:
- if isIPv6String(t) {
- continue
- }
aTargets = append(aTargets, t)
case endpoint.RecordTypeAAAA:
- if !isIPv6String(t) {
- continue
- }
aaaaTargets = append(aaaaTargets, t)
default:
cnameTargets = append(cnameTargets, t)
@@ -387,9 +382,3 @@ func waitForDynamicCacheSync(ctx context.Context, factory dynamicInformerFactory
}
return nil
}
-
-// isIPv6String returns if ip is IPv6.
-func isIPv6String(ip string) bool {
- netIP := net.ParseIP(ip)
- return netIP != nil && netIP.To4() == nil
-}
diff --git a/source/source_test.go b/source/source_test.go
index 417a3c14e5..17a3c4a7fb 100644
--- a/source/source_test.go
+++ b/source/source_test.go
@@ -85,6 +85,7 @@ func TestSuitableType(t *testing.T) {
}{
{"8.8.8.8", "", "A"},
{"2001:db8::1", "", "AAAA"},
+ {"::ffff:c0a8:101", "", "AAAA"},
{"foo.example.org", "", "CNAME"},
{"bar.eu-central-1.elb.amazonaws.com", "", "CNAME"},
} {