chore(source): code cleanup

This commit is contained in:
ivan katliarchuk 2025-05-12 14:17:29 +01:00
commit fe83c0d2d0
No known key found for this signature in database
GPG Key ID: 90C9B4748A999097
128 changed files with 3434 additions and 1849 deletions

View File

@ -17,7 +17,11 @@ jobs:
checks: write # to create a new check based on the results (shogo82148/actions-goveralls) checks: write # to create a new check based on the results (shogo82148/actions-goveralls)
name: Build name: Build
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy:
matrix:
# tests for target OS
os: [ubuntu-latest, macos-latest]
steps: steps:
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
@ -27,6 +31,7 @@ jobs:
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true
id: go id: go
- name: Install CI - name: Install CI
@ -37,7 +42,7 @@ jobs:
run: | run: |
apt update apt update
apt install -y make gcc libc-dev git apt install -y make gcc libc-dev git
if: github.actor == 'nektos/act' if: github.actor == 'nektos/act' && matrix.os == 'ubuntu-latest'
- name: Test - name: Test
run: make test run: make test
@ -46,5 +51,5 @@ jobs:
uses: shogo82148/actions-goveralls@v1 uses: shogo82148/actions-goveralls@v1
with: with:
path-to-profile: profile.cov path-to-profile: profile.cov
if: github.actor != 'nektos/act' if: github.actor != 'nektos/act' && matrix.os == 'ubuntu-latest'
continue-on-error: true continue-on-error: true

View File

@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@v4.2.2 uses: actions/checkout@v4.2.2
# https://github.com/renovatebot/github-action # https://github.com/renovatebot/github-action
- name: self-hosted renovate - name: self-hosted renovate
uses: renovatebot/github-action@v41.0.18 uses: renovatebot/github-action@v41.0.22
with: with:
# https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication # https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -19,7 +19,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with: with:
python-version: "3.12" python-version: "3.12"
cache: "pip" cache: "pip"

View File

@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: json-yaml-validate - name: json-yaml-validate
uses: GrantBirki/json-yaml-validate@v3.2.1 uses: GrantBirki/json-yaml-validate@v3.3.0
with: with:
comment: "true" # enable comment mode comment: "true" # enable comment mode
yaml_exclude_regex: "(charts/external-dns/templates.*|mkdocs.yml)" yaml_exclude_regex: "(charts/external-dns/templates.*|mkdocs.yml)"

View File

@ -78,7 +78,7 @@ jobs:
run: ah lint --kind helm || exit 1 run: ah lint --kind helm || exit 1
- name: Install Python - name: Install Python
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with: with:
token: ${{ github.token }} token: ${{ github.token }}
python-version: "3.x" python-version: "3.x"

View File

@ -26,7 +26,7 @@ jobs:
files: '.' files: '.'
config_file: ".markdownlint.json" config_file: ".markdownlint.json"
- name: Set up Go 1.x - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version-file: go.mod
@ -44,11 +44,11 @@ jobs:
# https://github.com/golangci/golangci-lint-action?tab=readme-ov-file#verify # https://github.com/golangci/golangci-lint-action?tab=readme-ov-file#verify
- name: Verify linter configuration and Lint go code - name: Verify linter configuration and Lint go code
uses: golangci/golangci-lint-action@v7 uses: golangci/golangci-lint-action@v8
with: with:
verify: true verify: true
args: --timeout=30m args: --timeout=30m
version: v2.0 version: v2.1
# Run Spectral # Run Spectral
- name: Lint OpenAPI spec - name: Lint OpenAPI spec

View File

@ -1,7 +1,8 @@
# https://golangci-lint.run/usage/configuration/
version: "2" version: "2"
linters: linters:
default: none default: none
enable: enable: # golangci-lint help linters
- dogsled - dogsled
- goprintffuncname - goprintffuncname
- govet - govet
@ -13,6 +14,9 @@ linters:
- unconvert - unconvert
- unused - unused
- whitespace - whitespace
- predeclared # Find code that shadows one of Go's predeclared identifiers
- sloglint # Ensure consistent code style when using log/slog
- asciicheck # Checks that all code identifiers does not have non-ASCII symbols in the name
settings: settings:
exhaustive: exhaustive:
default-signifies-exhaustive: false default-signifies-exhaustive: false

View File

@ -12,6 +12,8 @@ We have full documentation on how to get started contributing here:
- [Kubernetes Contributor Guide](https://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](https://git.k8s.io/community/contributors/guide#contributing) - [Kubernetes Contributor Guide](https://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](https://git.k8s.io/community/contributors/guide#contributing)
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet) - Common resources for existing developers - [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet) - Common resources for existing developers
This project follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification on PR title. The explicit commit history is used, among other things, to provide a readable changelog in release notes.
## Mentorship ## Mentorship
- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! - [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!

View File

@ -32,10 +32,6 @@ else
CONTROLLER_GEN=$(shell which controller-gen) CONTROLLER_GEN=$(shell which controller-gen)
endif endif
#? controller-gen-install: download controller-gen if necessary
controller-gen-install:
@scripts/install-tools.sh --generator
#? golangci-lint-install: Install golangci-lint tool #? golangci-lint-install: Install golangci-lint tool
golangci-lint-install: golangci-lint-install:
@scripts/install-tools.sh --golangci @scripts/install-tools.sh --golangci
@ -67,10 +63,11 @@ oas-lint:
.PHONY: lint .PHONY: lint
lint: licensecheck go-lint oas-lint lint: licensecheck go-lint oas-lint
#? crd: Generates CRD using controller-gen #? crd: Generates CRD using controller-gen and copy it into chart
.PHONY: crd .PHONY: crd
crd: controller-gen-install crd: controller-gen-install
${CONTROLLER_GEN} crd:crdVersions=v1 paths="./endpoint/..." output:crd:stdout > docs/contributing/crd-source/crd-manifest.yaml ${CONTROLLER_GEN} crd:crdVersions=v1 paths="./endpoint/..." output:crd:stdout > config/crd/standard/dnsendpoint.yaml
cp -f config/crd/standard/dnsendpoint.yaml charts/external-dns/crds/dnsendpoint.yaml
#? test: The verify target runs tasks similar to the CI tasks, but without code coverage #? test: The verify target runs tasks similar to the CI tasks, but without code coverage
.PHONY: test .PHONY: test
@ -201,3 +198,7 @@ helm-template:
helm-lint: helm-lint:
scripts/helm-tools.sh --schema scripts/helm-tools.sh --schema
scripts/helm-tools.sh --docs scripts/helm-tools.sh --docs
.PHONY: go-dependency
go-dependency: ## Dependency maintanance
go mod tidy

View File

@ -18,6 +18,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [UNRELEASED] ## [UNRELEASED]
### Changed
- Allow extraArgs to also be a map enabling overrides of individual values ([#5293](https://github.com/kubernetes-sigs/external-dns/pull/5293)) _@frittentheke
### Fixed
- Fixed wrong type definitions for webhook probes. ([#5297](https://github.com/kubernetes-sigs/external-dns/pull/5297)) _@semnell_
## [v1.16.1] - 2025-04-10 ## [v1.16.1] - 2025-04-10
### Changed ### Changed

View File

@ -104,7 +104,7 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
| enabled | bool | `nil` | No effect - reserved for use in sub-charting. | | enabled | bool | `nil` | No effect - reserved for use in sub-charting. |
| env | list | `[]` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `external-dns` container. | | env | list | `[]` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the `external-dns` container. |
| excludeDomains | list | `[]` | Intentionally exclude domains from being managed. | | excludeDomains | list | `[]` | Intentionally exclude domains from being managed. |
| extraArgs | list | `[]` | Extra arguments to provide to _ExternalDNS_. | | extraArgs | object | `{}` | Extra arguments to provide to _ExternalDNS_. An array or map can be used, with maps allowing for value overrides; maps also support slice values to use the same arg multiple times. |
| extraContainers | object | `{}` | Extra containers to add to the `Deployment`. | | 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. | | 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`. | | extraVolumes | list | `[]` | Extra [volumes](https://kubernetes.io/docs/concepts/storage/volumes/) for the `Pod`. |
@ -180,7 +180,7 @@ If `namespaced` is set to `true`, please ensure that `sources` my only contains
| tolerations | list | `[]` | Node taints which will be tolerated for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). | | tolerations | list | `[]` | Node taints which will be tolerated for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). |
| topologySpreadConstraints | list | `[]` | Topology spread constraints for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). If an explicit label selector is not provided one will be created from the pod selector labels. | | topologySpreadConstraints | list | `[]` | Topology spread constraints for `Pod` [scheduling](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). If an explicit label selector is not provided one will be created from the pod selector labels. |
| triggerLoopOnEvent | bool | `false` | If `true`, triggers run loop on create/update/delete events in addition of regular interval. | | triggerLoopOnEvent | bool | `false` | If `true`, triggers run loop on create/update/delete events in addition of regular interval. |
| txtOwnerId | string | `nil` | Specify an identifier for this instance of _ExternalDNS_ wWhen using a registry other than `noop`. | | txtOwnerId | string | `nil` | Specify an identifier for this instance of _ExternalDNS_ when using a registry other than `noop`. |
| txtPrefix | string | `nil` | Specify a prefix for the domain names of TXT records created for the `txt` registry. Mutually exclusive with `txtSuffix`. | | txtPrefix | string | `nil` | Specify a prefix for the domain names of TXT records created for the `txt` registry. Mutually exclusive with `txtSuffix`. |
| txtSuffix | string | `nil` | Specify a suffix for the domain names of TXT records created for the `txt` registry. Mutually exclusive with `txtPrefix`. | | txtSuffix | string | `nil` | Specify a suffix for the domain names of TXT records created for the `txt` registry. Mutually exclusive with `txtPrefix`. |

View File

@ -1,9 +1,11 @@
---
apiVersion: apiextensions.k8s.io/v1 apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition kind: CustomResourceDefinition
metadata: metadata:
name: dnsendpoints.externaldns.k8s.io
annotations: annotations:
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/external-dns/pull/2007 api-approved.kubernetes.io: https://github.com/kubernetes-sigs/external-dns/pull/2007
controller-gen.kubebuilder.io/version: v0.17.2
name: dnsendpoints.externaldns.k8s.io
spec: spec:
group: externaldns.k8s.io group: externaldns.k8s.io
names: names:
@ -13,90 +15,86 @@ spec:
singular: dnsendpoint singular: dnsendpoint
scope: Namespaced scope: Namespaced
versions: versions:
- name: v1alpha1 - name: v1alpha1
schema: schema:
openAPIV3Schema: openAPIV3Schema:
properties: properties:
apiVersion: apiVersion:
description: |- description: |-
APIVersion defines the versioned schema of this representation of an object. APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values. may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string type: string
kind: kind:
description: |- description: |-
Kind is a string value representing the REST resource this object represents. Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to. Servers may infer this from the endpoint the client submits requests to.
Cannot be updated. Cannot be updated.
In CamelCase. In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string type: string
metadata: metadata:
type: object type: object
spec: spec:
description: DNSEndpointSpec defines the desired state of DNSEndpoint description: DNSEndpointSpec defines the desired state of DNSEndpoint
properties: properties:
endpoints: endpoints:
items: items:
description: description: Endpoint is a high-level way of a connection between
Endpoint is a high-level way of a connection between a service and an IP
a service and an IP properties:
properties: dnsName:
dnsName: description: The hostname of the DNS record
description: The hostname of the DNS record type: string
labels:
additionalProperties:
type: string type: string
labels: description: Labels stores labels defined for the Endpoint
additionalProperties: type: object
type: string providerSpecific:
description: Labels stores labels defined for the Endpoint description: ProviderSpecific stores provider specific config
items:
description: ProviderSpecificProperty holds the name and value
of a configuration which is specific to individual DNS providers
properties:
name:
type: string
value:
type: string
type: object type: object
providerSpecific: type: array
description: ProviderSpecific stores provider specific config recordTTL:
items: description: TTL for the record
description: format: int64
ProviderSpecificProperty holds the name and value type: integer
of a configuration which is specific to individual DNS providers recordType:
properties: description: RecordType type of record, e.g. CNAME, A, AAAA,
name: SRV, TXT etc
type: string type: string
value: setIdentifier:
type: string description: Identifier to distinguish multiple records with
type: object the same name and type (e.g. Route53 records with routing
type: array policies other than 'simple')
recordTTL: type: string
description: TTL for the record targets:
format: int64 description: The targets the DNS record points to
type: integer items:
recordType:
description:
RecordType type of record, e.g. CNAME, A, AAAA,
SRV, TXT etc
type: string type: string
setIdentifier: type: array
description: type: object
Identifier to distinguish multiple records with type: array
the same name and type (e.g. Route53 records with routing type: object
policies other than 'simple') status:
type: string description: DNSEndpointStatus defines the observed state of DNSEndpoint
targets: properties:
description: The targets the DNS record points to observedGeneration:
items: description: The generation observed by the external-dns controller.
type: string format: int64
type: array type: integer
type: object type: object
type: array type: object
type: object served: true
status: storage: true
description: DNSEndpointStatus defines the observed state of DNSEndpoint subresources:
properties: status: {}
observedGeneration:
description: The generation observed by the external-dns controller.
format: int64
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@ -124,9 +124,26 @@ spec:
- --managed-record-types={{ . }} - --managed-record-types={{ . }}
{{- end }} {{- end }}
- --provider={{ $providerName }} - --provider={{ $providerName }}
{{- range .Values.extraArgs }} {{- if kindIs "map" .Values.extraArgs }}
{{- range $key, $value := .Values.extraArgs }}
{{- if not (kindIs "invalid" $value) }}
{{- if kindIs "slice" $value }}
{{- range $value }}
- --{{ $key }}={{ tpl (. | toString) $ }}
{{- end }}
{{- else }}
- --{{ $key }}={{ tpl ($value | toString) $ }}
{{- end }}
{{- else }}
- --{{ $key }}
{{- end }}
{{- end }}
{{- end }}
{{- if kindIs "slice" .Values.extraArgs }}
{{- range .Values.extraArgs }}
- {{ tpl . $ }} - {{ tpl . $ }}
{{- end }} {{- end }}
{{- end }}
ports: ports:
- name: http - name: http
protocol: TCP protocol: TCP

View File

@ -103,6 +103,60 @@ tests:
- --zone-id-filter=/hostedzone/Z00004 - --zone-id-filter=/hostedzone/Z00004
- --zone-id-filter=/hostedzone/Z00005 - --zone-id-filter=/hostedzone/Z00005
- it: should allow 'extraArgs' to be a slice
set:
extraArgs:
- --extraArgA=valueA
- --extraArgB=valueB
- --extraArgC=valueC-1
- --extraArgC=valueC-2
asserts:
- equal:
path: spec.template.spec.containers[?(@.name == "external-dns")].args
value:
- --log-level=info
- --log-format=text
- --interval=1m
- --source=service
- --source=ingress
- --policy=upsert-only
- --registry=txt
- --provider=aws
- --extraArgA=valueA
- --extraArgB=valueB
- --extraArgC=valueC-1
- --extraArgC=valueC-2
- it: should allow 'extraArgs' to be a map with its entries potentially being slices (lists) themselves
set:
extraArgs:
extraArgA: valueA
extraArgB: valueB
extraArgC:
- valueC-1
- valueC-2
asserts:
- equal:
path: spec.template.spec.containers[?(@.name == "external-dns")].args
value:
- --log-level=info
- --log-format=text
- --interval=1m
- --source=service
- --source=ingress
- --policy=upsert-only
- --registry=txt
- --provider=aws
- --extraArgA=valueA
- --extraArgB=valueB
- --extraArgC=valueC-1
- --extraArgC=valueC-2
- it: should throw error when txtPrefix and txtSuffix are set - it: should throw error when txtPrefix and txtSuffix are set
set: set:
txtPrefix: "test-prefix" txtPrefix: "test-prefix"

View File

@ -50,3 +50,15 @@ tests:
labelFilter: "mydomain.io/enable-dns-record in (true)" labelFilter: "mydomain.io/enable-dns-record in (true)"
asserts: asserts:
- notFailedTemplate: {} - notFailedTemplate: {}
- it: should not fail when livenessProbe is null
set:
livenessProbe: null
asserts:
- notFailedTemplate: {}
- it: should not fail when readinessProbe is null
set:
readinessProbe: null
asserts:
- notFailedTemplate: {}

View File

@ -64,9 +64,11 @@
"items": { "items": {
"type": "string" "type": "string"
}, },
"properties": {},
"type": [ "type": [
"array", "array",
"null" "null",
"object"
], ],
"uniqueItems": true "uniqueItems": true
}, },
@ -308,30 +310,51 @@
"livenessProbe": { "livenessProbe": {
"properties": { "properties": {
"failureThreshold": { "failureThreshold": {
"type": "integer" "type": [
"integer",
"null"
]
}, },
"httpGet": { "httpGet": {
"properties": { "properties": {
"path": { "path": {
"type": "string" "type": [
"string",
"null"
]
}, },
"port": { "port": {
"type": "string" "type": [
"integer",
"string"
]
} }
}, },
"type": "object" "type": "object"
}, },
"initialDelaySeconds": { "initialDelaySeconds": {
"type": "integer" "type": [
"integer",
"null"
]
}, },
"periodSeconds": { "periodSeconds": {
"type": "integer" "type": [
"integer",
"null"
]
}, },
"successThreshold": { "successThreshold": {
"type": "integer" "type": [
"integer",
"null"
]
}, },
"timeoutSeconds": { "timeoutSeconds": {
"type": "integer" "type": [
"integer",
"null"
]
} }
}, },
"type": "object" "type": "object"
@ -339,30 +362,51 @@
"readinessProbe": { "readinessProbe": {
"properties": { "properties": {
"failureThreshold": { "failureThreshold": {
"type": "integer" "type": [
"integer",
"null"
]
}, },
"httpGet": { "httpGet": {
"properties": { "properties": {
"path": { "path": {
"type": "string" "type": [
"string",
"null"
]
}, },
"port": { "port": {
"type": "string" "type": [
"integer",
"string"
]
} }
}, },
"type": "object" "type": "object"
}, },
"initialDelaySeconds": { "initialDelaySeconds": {
"type": "integer" "type": [
"integer",
"null"
]
}, },
"periodSeconds": { "periodSeconds": {
"type": "integer" "type": [
"integer",
"null"
]
}, },
"successThreshold": { "successThreshold": {
"type": "integer" "type": [
"integer",
"null"
]
}, },
"timeoutSeconds": { "timeoutSeconds": {
"type": "integer" "type": [
"integer",
"null"
]
} }
}, },
"type": "object" "type": "object"

View File

@ -216,7 +216,7 @@ policy: upsert-only # @schema enum:[sync, upsert-only]; type:string; default: "
# -- Specify the registry for storing ownership and labels. # -- Specify the registry for storing ownership and labels.
# Valid values are `txt`, `aws-sd`, `dynamodb` & `noop`. # Valid values are `txt`, `aws-sd`, `dynamodb` & `noop`.
registry: txt # @schema enum:[txt, aws-sd, dynamodb, noop]; default: "txt" registry: txt # @schema enum:[txt, aws-sd, dynamodb, noop]; default: "txt"
# -- (string) Specify an identifier for this instance of _ExternalDNS_ wWhen using a registry other than `noop`. # -- (string) Specify an identifier for this instance of _ExternalDNS_ when using a registry other than `noop`.
txtOwnerId: # @schema type:[string, null]; default: null txtOwnerId: # @schema type:[string, null]; default: null
# -- (string) Specify a prefix for the domain names of TXT records created for the `txt` registry. # -- (string) Specify a prefix for the domain names of TXT records created for the `txt` registry.
# Mutually exclusive with `txtSuffix`. # Mutually exclusive with `txtSuffix`.
@ -263,24 +263,24 @@ provider: # @schema type: [object, string];
# @default -- See _values.yaml_ # @default -- See _values.yaml_
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /healthz path: /healthz # @schema type:[string, null]; default: null
port: http-webhook port: http-webhook # @schema type:[integer,string]; default: string
initialDelaySeconds: 10 initialDelaySeconds: 10 # @schema type:[integer, null]; default: null
periodSeconds: 10 periodSeconds: 10 # @schema type:[integer, null]; default: null
timeoutSeconds: 5 timeoutSeconds: 5 # @schema type:[integer, null]; default: null
failureThreshold: 2 failureThreshold: 2 # @schema type:[integer, null]; default: null
successThreshold: 1 successThreshold: 1 # @schema type:[integer, null]; default: null
# -- [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `webhook` container. # -- [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) configuration for the `webhook` container.
# @default -- See _values.yaml_ # @default -- See _values.yaml_
readinessProbe: readinessProbe:
httpGet: httpGet:
path: /healthz path: /healthz # @schema type:[string, null]; default: null
port: http-webhook port: http-webhook # @schema type:[integer,string]; default: string
initialDelaySeconds: 5 initialDelaySeconds: 5 # @schema type:[integer, null]; default: null
periodSeconds: 10 periodSeconds: 10 # @schema type:[integer, null]; default: null
timeoutSeconds: 5 timeoutSeconds: 5 # @schema type:[integer, null]; default: null
failureThreshold: 6 failureThreshold: 6 # @schema type:[integer, null]; default: null
successThreshold: 1 successThreshold: 1 # @schema type:[integer, null]; default: null
service: service:
# -- Webhook exposed HTTP port for the service. # -- Webhook exposed HTTP port for the service.
port: 8080 port: 8080
@ -296,7 +296,8 @@ provider: # @schema type: [object, string];
relabelings: [] relabelings: []
# -- Extra arguments to provide to _ExternalDNS_. # -- Extra arguments to provide to _ExternalDNS_.
extraArgs: [] # @schema type: [array, null]; item: string; uniqueItems: true; # An array or map can be used, with maps allowing for value overrides; maps also support slice values to use the same arg multiple times.
extraArgs: {} # @schema type: [array, null, object]; item: string; uniqueItems: true;
secretConfiguration: secretConfiguration:
# -- If `true`, create a `Secret` to store sensitive provider configuration (**DEPRECATED**). # -- If `true`, create a `Secret` to store sensitive provider configuration (**DEPRECATED**).

View File

@ -4,7 +4,7 @@ kind: CustomResourceDefinition
metadata: metadata:
annotations: annotations:
api-approved.kubernetes.io: https://github.com/kubernetes-sigs/external-dns/pull/2007 api-approved.kubernetes.io: https://github.com/kubernetes-sigs/external-dns/pull/2007
controller-gen.kubebuilder.io/version: v0.15.0 controller-gen.kubebuilder.io/version: v0.17.2
name: dnsendpoints.externaldns.k8s.io name: dnsendpoints.externaldns.k8s.io
spec: spec:
group: externaldns.k8s.io group: externaldns.k8s.io

View File

@ -18,6 +18,7 @@ package controller
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
@ -29,10 +30,9 @@ import (
"testing" "testing"
"time" "time"
"context"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/pkg/apis/externaldns" "sigs.k8s.io/external-dns/pkg/apis/externaldns"
"sigs.k8s.io/external-dns/plan" "sigs.k8s.io/external-dns/plan"
@ -219,18 +219,45 @@ func TestHandleSigterm(t *testing.T) {
} }
} }
func TestServeMetrics(t *testing.T) { func getRandomPort() (int, error) {
l, _ := net.Listen("tcp", ":0") addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
_ = l.Close() if err != nil {
_, port, _ := net.SplitHostPort(l.Addr().String()) return 0, err
}
go serveMetrics(fmt.Sprintf(":%s", port)) l, err := net.ListenTCP("tcp", addr)
resp, err := http.Get(fmt.Sprintf("http://localhost:%s", port) + "/healthz") if err != nil {
assert.NoError(t, err) return 0, err
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port, nil
}
func TestServeMetrics(t *testing.T) {
t.Parallel()
port, err := getRandomPort()
require.NoError(t, err)
addresse := fmt.Sprintf("localhost:%d", port)
go serveMetrics(fmt.Sprintf(":%d", port))
// Wait for the TCP socket to be ready
require.Eventually(t, func() bool {
conn, err := net.Dial("tcp", addresse)
if err != nil {
return false
}
_ = conn.Close()
return true
}, 1*time.Second, 5*time.Millisecond, "server not ready with port open in time")
resp, err := http.Get(fmt.Sprintf("http://%s/healthz", addresse))
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = http.Get(fmt.Sprintf("http://localhost:%s", port) + "/metrics") resp, err = http.Get(fmt.Sprintf("http://%s/metrics", addresse))
assert.NoError(t, err) require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, http.StatusOK, resp.StatusCode)
} }
@ -308,7 +335,6 @@ func (m *MockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error
} }
func (p *MockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { func (p *MockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
return nil return nil
} }

View File

@ -0,0 +1,306 @@
# FQDN Templating Guide
## What is FQDN Templating?
**FQDN templating** is a feature that allows to dynamically construct Fully Qualified Domain Names (FQDNs) using a Go templating engine.
Instead of relying solely on annotations or static names, you can use metadata from Kubernetes objects—such as service names, namespaces, and labels—to generate DNS records programmatically and dynamically.
This is useful for:
- Creating consistent naming conventions across environments.
- Reducing boilerplate annotations.
- Supporting multi-tenant or dynamic environments.
- Migrating from one DNS scheme to another
- Supporting multiple variants, such as a regional one and then one that doesn't or similar.
## How It Works
ExternalDNS has a flag: `--fqdn-template`, which defines a Go template for rendering the desired DNS names.
The template uses the following data from the source object (e.g., a `Service` or `Ingress`):
| Field | Description |
|:--------------|:------------------------------------------------------------------|
| `Name` | Name of the object (e.g., service) |
| `Namespace` | Namespace of the object |
| `Labels` | Map of labels applied to the object |
| `Annotations` | Map of annotations |
| `TargetName` | For `Service`, it's the service name; for `Ingress`, the hostname |
| `Endpoint` | Contains more contextual endpoint info, such as IP/target |
| `Controller` | Controller type (optional) |
## Supported Sources
<!-- TODO: generate from code -->
| Source | Description | FQDN Supported |
|:-----------------------|:----------------------------------------------------------------|:--------------:|
| `ambassador-host` | Queries Ambassador Host resources for endpoints. | ❌ |
| `cloudfoundry` | Queries Cloud Foundry resources for endpoints. | ❌ |
| `connector` | Queries a custom connector source for endpoints. | ❌ |
| `contour-httpproxy` | Queries Contour HTTPProxy resources for endpoints. | ✅ |
| `crd` | Queries Custom Resource Definitions (CRDs) for endpoints. | ❌ |
| `empty` | Uses an empty source, typically for testing or no-op scenarios. | ❌ |
| `f5-transportserver` | Queries F5 TransportServer resources for endpoints. | ❌ |
| `f5-virtualserver` | Queries F5 VirtualServer resources for endpoints. | ❌ |
| `fake` | Uses a fake source for testing purposes. | ❌ |
| `gateway-grpcroute` | Queries GRPCRoute resources from the Gateway API. | ✅ |
| `gateway-httproute` | Queries HTTPRoute resources from the Gateway API. | ✅ |
| `gateway-tcproute` | Queries TCPRoute resources from the Gateway API. | ✅ |
| `gateway-tlsroute` | Queries TLSRoute resources from the Gateway API. | ❌ |
| `gateway-udproute` | Queries UDPRoute resources from the Gateway API. | ❌ |
| `gloo-proxy` | Queries Gloo Proxy resources for endpoints. | ❌ |
| `ingress` | Queries Kubernetes Ingress resources for endpoints. | ✅ |
| `istio-gateway` | Queries Istio Gateway resources for endpoints. | ✅ |
| `istio-virtualservice` | Queries Istio VirtualService resources for endpoints. | ✅ |
| `kong-tcpingress` | Queries Kong TCPIngress resources for endpoints. | ❌ |
| `node` | Queries Kubernetes Node resources for endpoints. | ✅ |
| `openshift-route` | Queries OpenShift Route resources for endpoints. | ✅ |
| `pod` | Queries Kubernetes Pod resources for endpoints. | ❌ |
| `service` | Queries Kubernetes Service resources for endpoints. | ✅ |
| `skipper-routegroup` | Queries Skipper RouteGroup resources for endpoints. | ✅ |
| `traefik-proxy` | Queries Traefik Proxy resources for endpoints. | ❌ |
## Custom Functions
<!-- TODO: generate from code -->
| Function | Description |
|:-------------|:-----------------------------------------------------------------------------------------|
| `trimPrefix` | Function from the `strings` package. Returns `string` without the provided leading prefix. |
---
## Example Usage
> These examples should provide a solid foundation for implementing FQDN templating in your ExternalDNS setup.
> If you have specific requirements or encounter issues, feel free to explore the issues or update this guide.
### Basic Usage
```yml
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: my-namespace
```
```sh
external-dns \
--provider=aws \
--source=service \
--fqdn-template="{{ .Name }}.example.com,{{ .Name }}.{{ .Namespace }}.example.tld"
# This will result in DNS entries like
>route53> my-service.example.com
>route53> my-service.my-namespace.example.tld
```
### With Namespace
```yml
---
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
---
apiVersion: v1
kind: Service
metadata:
name: other-service
namespace: kube-system
```
```yml
args:
--fqdn-template="{{.Name}}.{{.Namespace}}.example.com"
# This will result in DNS entries like
# route53> my-service.default.example.com
# route53> other-service.kube-system.example.com
```
### Using Labels in Templates
You can also utilize labels in your FQDN templates to create more dynamic DNS entries. Assuming your service has:
```yml
apiVersion: v1
kind: Service
metadata:
name: my-service
labels:
environment: staging
```
```yml
args:
--fqdn-template="{{ .Labels.environment }}.{{ .Name }}.example.com"
# This will result in DNS entries like
# route53> staging.my-service.example.com
```
### Multiple FQDN Templates
ExternalDNS allows specifying multiple FQDN templates, which can be useful when you want to create multiple DNS entries for a single service or ingress.
> Be cautious, as this will create multiple DNS records per resource, potentially increasing the number of API calls to your DNS provider.
```yml
args:
--fqdn-template={{.Name}}.example.com,{{.Name}}.svc.example.com
```
### Conditional Templating combined with Annotations processing
In scenarios where you want to conditionally generate FQDNs based on annotations, you can use Go template functions like or to provide defaults.
```yml
args:
- --combine-fqdn-annotation # this is required to combine FQDN templating and annotation processing
- --fqdn-template={{ or .Annotations.dns "invalid" }}.example.com
- --exclude-domains=invalid.example.com
```
### Using Annotations for FQDN Templating
This example demonstrates how to use annotations in Kubernetes objects to dynamically generate Fully Qualified Domain Names (FQDNs) using the --fqdn-template flag in ExternalDNS.
The Service object includes an annotation dns.company.com/label with the value my-org-tld-v2. This annotation is used as part of the FQDN template to construct the DNS name.
```yml
apiVersion: v1
kind: Service
metadata:
name: nginx-v2
namespace: my-namespace
annotations:
dns.company.com/label: my-org-tld-v2
spec:
type: ClusterIP
clusterIP: None
```
The --fqdn-template flag is configured to use the annotation value (dns.company.com/label) and append the namespace and a custom domain (company.local) to generate the FQDN.
```yml
args:
--source=service
--fqdn-template='{{ index .ObjectMeta.Annotations "dns.company.com/label" }}.{{ .Namespace }}.company.local'
# For the given Service object, the resulting FQDN will be:
# route53> my-org-tld-v2.my-namespace.company.local
```
### DNS Scheme Migration
If you're transitioning from one naming convention to another (e.g., from svc.cluster.local to svc.example.com), --fqdn-template allows you to generate the new records alongside or in place of the old ones — without requiring changes to your Kubernetes manifests.
```yml
args:
- --fqdn-template='{{.Name}}.new-dns.example.com'
```
This helps automate DNS record migration while maintaining service continuity.
### Multi-Variant Domain Support
You can also support regional variants or multi-tenant architectures, where the same service is deployed to different regions or environments:
```yaml
--fqdn-template='{{ .Name }}.{{ .Labels.env }}.{{ .Labels.region }}.example.com, {{ if eq .Labels.env "prod" }}{{ .Name }}.my-company.tld{{ end }}'
```
With additional context (e.g., annotations), this can produce FQDNs like:
```yml
api.prod.us-east-1.example.com
api.my-company.tld
```
This is helpful in scenarios such as:
- Blue/green deployments across domains
- Staging vs. production resolution
- Multi-cloud or multi-region failover strategies
## Tips
- If `--fqdn-template` is specified, ExternalDNS ignores any `external-dns.alpha.kubernetes.io/hostname` annotations.
- You must still ensure the resulting FQDN is valid and unique.
- Since Go templates can be error-prone, test your template with simple examples before deploying. Mismatched field names or nil values (e.g., missing labels) will result in errors or skipped entries.
## FaQ
### Can I specify multiple global FQDN templates?
Yes, you can. Pass in a comma separated list to --fqdn-template. Beware this will double (triple, etc) the amount of DNS entries based on how many services, ingresses and so on you have and will get you faster towards the API request limit of your DNS provider.
### Where to find template syntax
- [Go template syntax](https://pkg.go.dev/text/template)
- [Go func builtins](https://github.com/golang/go/blob/master/src/text/template/funcs.go#L39-L63)
### FQDN Templating, Helm and improper templating syntax
The user encountered errors due to improper templating syntax:
```yml
extraArgs:
- --fqdn-template={{name}}.uat.example.com
```
The correct syntax should include a dot prefix: `{{ .Name }}`.
Additionally, when using Helm's `tpl` function, it's necessary to escape the braces to prevent premature evaluation:
```yml
extraArgs:
- --fqdn-template={{ `{{ .Name }}.uat.example.com` }}
```
### Handling Subdomain-Only Hostnames
In [Issue #1872](https://github.com/kubernetes-sigs/external-dns/issues/1872), it was observed that ExternalDNS ignores the `--fqdn-template` when the ingress host field is set to a subdomain (e.g., foo) without a full domain.
The expectation was that the template would still apply, generating entries like `foo.bar.example.com.`
This highlights a limitation to be aware of when designing FQDN templates.
> :warning: This is currently not supported ! User would expect external-dns to generate a dns record according to the fqdnTemplate
> e.g. if the ingress name: foo and host: foo is created while fqdnTemplate={{.Name}}.bar.example.com then a dns record foo.bar.example.com should be created
```yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: foo
spec:
rules:
- host: foo
http:
paths:
- backend:
serviceName: foo
servicePort: 80
path: /
```
### Combining FQDN Template with Annotations
In [Issue #3318](https://github.com/kubernetes-sigs/external-dns/issues/3318), a question was raised about the interaction between --fqdn-template and --combine-fqdn-annotation.
The discussion clarified that when both flags are used, ExternalDNS combines the FQDN generated from the template with the annotation value, providing flexibility in DNS name construction.
### Using Annotations for Dynamic FQDNs
In [Issue #2627](https://github.com/kubernetes-sigs/external-dns/issues/2627), a user aimed to generate DNS entries based on ingress annotations:
```yml
args:
- --fqdn-template={{.Annotations.hostname}}.example.com
- --combine-fqdn-annotation
- --domain-filter=example.com
```
By setting the hostname annotation in the ingress resource, ExternalDNS constructs the FQDN accordingly. This approach allows for dynamic DNS entries without hardcoding hostnames.

View File

@ -48,6 +48,36 @@ make generate-metrics-documentation
We require all changes to be covered by acceptance tests and/or unit tests, depending on the situation. We require all changes to be covered by acceptance tests and/or unit tests, depending on the situation.
In the context of the `external-dns`, acceptance tests are tests of interactions with providers, such as creating, reading information about, and destroying DNS resources. In contrast, unit tests test functionality wholly within the codebase itself, such as function tests. In the context of the `external-dns`, acceptance tests are tests of interactions with providers, such as creating, reading information about, and destroying DNS resources. In contrast, unit tests test functionality wholly within the codebase itself, such as function tests.
### Log Unit Testing
Testing log messages within codebase provides significant advantages, especially when it comes to debugging, monitoring, and gaining a deeper understanding of system behavior. Log library [build-in testing functionality](https://github.com/sirupsen/logrus?tab=readme-ov-file#testing)
This practice enables:
- Early detection of logging issues
- Verification of Important Information
- Ensuring Correct Severity Levels
- Improving Observability and Monitoring
- Driving Better Logging Practices
To illustrate how to unit test log output within functions, consider the following example:
```go
import (
"testing"
"sigs.k8s.io/external-dns/internal/testutils"
)
func TestMe(t *testing.T) {
hook := testutils.LogsUnderTestWithLogLeve(log.WarnLevel, t)
... function under tests ...
testutils.TestHelperLogContains("example warning message", hook, t)
// provide negative assertion
testuitls.TestHelperLogNotContains("this message should not be shown", hook, t)
}
```
### Continuous Integration ### Continuous Integration
When submitting a pull request, you'll notice that we run several automated processes on your proposed change. Some of these processes are tests to ensure your contribution aligns with our standards. While we strive for accuracy, some users may find these tests confusing. When submitting a pull request, you'll notice that we run several automated processes on your proposed change. Some of these processes are tests to ensure your contribution aligns with our standards. While we strive for accuracy, some users may find these tests confusing.

View File

@ -50,10 +50,6 @@ There are three sources of information for ExternalDNS to decide on DNS name. Ex
3. If `--fqdn-template` flag is specified, e.g. `--fqdn-template={{.Name}}.my-org.com`, ExternalDNS will use service/ingress specifications for the provided template to generate DNS name. 3. If `--fqdn-template` flag is specified, e.g. `--fqdn-template={{.Name}}.my-org.com`, ExternalDNS will use service/ingress specifications for the provided template to generate DNS name.
## Can I specify multiple global FQDN templates?
Yes, you can. Pass in a comma separated list to `--fqdn-template`. Beaware this will double (triple, etc) the amount of DNS entries based on how many services, ingresses and so on you have and will get you faster towards the API request limit of your DNS provider.
## Which Service and Ingress controllers are supported? ## Which Service and Ingress controllers are supported?
Regarding Services, we'll support the OSI Layer 4 load balancers that Kubernetes creates on AWS and Google Kubernetes Engine, and possibly other clusters running on Google Compute Engine. Regarding Services, we'll support the OSI Layer 4 load balancers that Kubernetes creates on AWS and Google Kubernetes Engine, and possibly other clusters running on Google Compute Engine.

View File

@ -17,40 +17,40 @@
| `--cf-password=""` | The password 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) | | `--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 | | `--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, f5-transportserver, 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-non-host-network-pods` | Ignore pods not running on host network when using pod source (default: true) |
| `--[no-]ignore-ingress-tls-spec` | Ignore the spec.tls section in Ingress resources (default: false) |
| `--gateway-name=GATEWAY-NAME` | Limit Gateways of Route endpoints to a specific name (default: all names) |
| `--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) |
| `--pod-source-domain=""` | Domain to use for pods records (optional) |
| `--[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) | | `--[no-]always-publish-not-ready-addresses` | Always publish also not ready addresses for headless services (optional) |
| `--annotation-filter=""` | Filter resources queried for endpoints by annotation, using label selector semantics |
| `--[no-]combine-fqdn-annotation` | Combine FQDN template and Annotations instead of overwriting |
| `--compatibility=` | Process annotation semantics from legacy implementations (optional, options: mate, molecule, kops-dns-controller) |
| `--connector-source-server="localhost:8080"` | The server to connect for connector source, valid only when using connector source | | `--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-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 | | `--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) | | `--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-record-types=EXCLUDE-RECORD-TYPES` | Record types to exclude from management; specify multiple times to exclude many; (optional) |
| `--exclude-target-net=EXCLUDE-TARGET-NET` | Exclude target 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) |
| `--[no-]exclude-unschedulable` | Exclude nodes that are considered unschedulable (default: true) | | `--[no-]exclude-unschedulable` | Exclude nodes that are considered unschedulable (default: true) |
| `--[no-]expose-internal-ipv6` | When using the node source, expose internal IPv6 addresses (optional). Default is true. | | `--[no-]expose-internal-ipv6` | When using the node source, expose internal IPv6 addresses (optional). Default is true. |
| `--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. |
| `--gateway-label-filter=GATEWAY-LABEL-FILTER` | Filter Gateways of Route endpoints via label selector (default: all gateways) |
| `--gateway-name=GATEWAY-NAME` | Limit Gateways of Route endpoints to a specific name (default: all names) |
| `--gateway-namespace=GATEWAY-NAMESPACE` | Limit Gateways of Route endpoints to a specific namespace (default: all namespaces) |
| `--[no-]ignore-hostname-annotation` | Ignore hostname annotation when generating DNS names, valid only when --fqdn-template is set (default: false) |
| `--[no-]ignore-ingress-rules-spec` | Ignore the spec.rules section in Ingress resources (default: false) |
| `--[no-]ignore-ingress-tls-spec` | Ignore the spec.tls section in Ingress resources (default: false) |
| `--[no-]ignore-non-host-network-pods` | Ignore pods not running on host network when using pod source (default: true) |
| `--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) |
| `--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 |
| `--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) |
| `--namespace=""` | Limit resources queried for endpoints to a specific namespace (default: all namespaces) |
| `--nat64-networks=NAT64-NETWORKS` | Adding an A record for each AAAA record in NAT64-enabled networks; specify multiple times for multiple possible nets (optional) |
| `--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. |
| `--pod-source-domain=""` | Domain to use for pods records (optional) |
| `--[no-]publish-host-ip` | Allow external-dns to publish host-ip for headless services (optional) |
| `--[no-]publish-internal-services` | Allow external-dns to publish DNS records for ClusterIP services (optional) |
| `--service-type-filter=SERVICE-TYPE-FILTER` | The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName) |
| `--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, f5-transportserver, traefik-proxy) |
| `--target-net-filter=TARGET-NET-FILTER` | Limit possible targets by a net filter; specify multiple times for multiple possible 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 |
| `--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, digitalocean, dnsimple, exoscale, gandi, godaddy, google, ibmcloud, inmemory, linode, ns1, oci, ovh, pdns, pihole, plural, rfc2136, scaleway, skydns, tencentcloud, transip, ultradns, webhook) | | `--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, 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. | | `--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) | | `--domain-filter=` | Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional) |
@ -133,7 +133,7 @@
| `--exoscale-apisecret=""` | Provide your API Secret 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 (optionally specify multiple times when when using --rfc2136-load-balancing-strategy) | | `--rfc2136-host=` | When using the RFC2136 provider, specify the host of the DNS server (optionally specify multiple times when when using --rfc2136-load-balancing-strategy) |
| `--rfc2136-port=0` | When using the RFC2136 provider, specify the port 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 | | `--rfc2136-zone=RFC2136-ZONE` | When using the RFC2136 provider, specify zone entry of the DNS server to use (can be specified multiple times) |
| `--[no-]rfc2136-create-ptr` | When using the RFC2136 provider, enable PTR management | | `--[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) | | `--[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-keyname=""` | When using the RFC2136 provider, specify the TSIG key to attached to DNS messages (required when --rfc2136-insecure=false) |

35
docs/providers.md Normal file
View File

@ -0,0 +1,35 @@
# Providers
Provider supported configurations
| Provider Name | Zone Cache | Dry Run | Default TTL (seconds) |
|:--------------|:-----------|:--------|:----------------------|
| Akamai | n/a | yes | 600 |
| AlibabaCloud | n/a | yes | 600 |
| AWS | yes | yes | 300 |
| AWSSD | n/a | yes | 300 |
| Azure | yes | yes | 300 |
| Civo | n/a | yes | n/a |
| Cloudflare | n/a | yes | 1 |
| CoreDNS | n/a | yes | n/a |
| DigitalOcean | n/a | yes | 300 |
| DNSSimple | n/a | yes | 3600 |
| Exoscale | n/a | yes | n/a |
| Gandi | n/a | no | 600 |
| GoDaddy | n/a | yes | 600 |
| Google GCP | n/a | yes | 300 |
| IBMCloud | n/a | yes | 1 |
| InMemory | n/a | n/a | n/a |
| Linode | n/a | n/a | n/a |
| NS1 | n/a | yes | 10 |
| OCI | yes | yes | 300 |
| OVH | n/a | yes | 0 |
| PDNS | n/a | yes | 300 |
| PiHole | n/a | yes | n/a |
| Plural | n/a | n/a | n/a |
| RFC2136 | n/a | yes | n/a |
| Scaleway | n/a | n/a | 300 |
| TencentCloud | n/a | n/a | n/a |
| Transip | n/a | yes | 60 |
| Ultradns | n/a | yes | n/a |
| Webhook | n/a | n/a | n/a |

View File

@ -81,11 +81,11 @@ Create the objects of CRD type by filling in the fields of CRD and DNS record wo
### Example ### Example
Here is an example [CRD manifest](crd/crd-manifest.yaml) generated by kubebuilder. Here is an example [CRD manifest](https://github.com/kubernetes-sigs/external-dns/blob/HEAD/charts/external-dns/crds/dnsendpoint.yaml) generated by kubebuilder.
Apply this to register the CRD Apply this to register the CRD
```sh ```sh
$ kubectl apply --validate=false -f docs/sources/crd/crd-manifest.yaml $ kubectl apply --server-side=true -f "https://raw.githubusercontent.com/kubernetes-sigs/external-dns/master/config/crd/standard/dnsendpoint.yaml"
customresourcedefinition.apiextensions.k8s.io "dnsendpoints.externaldns.k8s.io" created customresourcedefinition.apiextensions.k8s.io "dnsendpoints.externaldns.k8s.io" created
``` ```

View File

@ -0,0 +1,256 @@
# IONOS Cloud
This tutorial describes how to set up ExternalDNS for use within a Kubernetes cluster using IONOS Cloud DNS.
For more details, visit the [IONOS external-dns webhook repository](https://github.com/ionos-cloud/external-dns-ionos-webhook).
You can also find the [external-dns-ionos-webhook container image](https://github.com/ionos-cloud/external-dns-ionos-webhook/pkgs/container/external-dns-ionos-webhook) required for this setup.
## Creating a DNS Zone with IONOS Cloud DNS
If you are new to IONOS Cloud DNS, we recommend you first read the following instructions for creating a DNS zone:
- [Manage DNS Zones in Data Centre Designer](https://docs.ionos.com/cloud/network-services/cloud-dns/dcd-how-tos/manage-dns-zone)
- [Creating a DNS Zone using the IONOS Cloud DNS API](https://docs.ionos.com/cloud/network-services/cloud-dns/api-how-tos/create-dns-zone)
### Steps to Create a DNS Zone
1. Log in to the [IONOS Cloud Data Center Designer](https://dcd.ionos.com/).
2. Navigate to the **Network Services** section and select **Cloud DNS**.
3. Click on **Create Zone** and provide the following details:
- **Zone Name**: Enter the domain name (e.g., `example.com`).
- **Description**: It is optional to provide a description of your zone.
4. Save the zone configuration.
For more advanced configurations, such as adding records or managing subdomains, refer to the [IONOS Cloud DNS Documentation](https://docs.ionos.com/cloud/network-services/cloud-dns/).
## Creating an IONOS API Token
To use ExternalDNS with IONOS Cloud DNS, you need an API token with sufficient privileges to manage DNS zones and records. Follow these steps to create an API token:
1. Log in to the [IONOS Cloud Data Center Designer](https://dcd.ionos.com/).
2. Navigate to the **Management** section in the top right corner and select **Token Manager**.
3. Select the Time To Live(TTL) of the token and click on **Create Token**.
4. Copy the generated token and store it securely. You will use this token to authenticate ExternalDNS.
## Deploy ExternalDNS
### Step 1: Create a Kubernetes Secret for the IONOS API Token
Store your IONOS API token securely in a Kubernetes secret:
```bash
kubectl create secret generic ionos-credentials --from-literal=api-key='<IONOS_API_TOKEN>'
```
Replace `<IONOS_API_TOKEN>` with your actual IONOS API token.
### Step 2: Configure ExternalDNS
Create a Helm values file for the ExternalDNS Helm chart that includes the webhook configuration. In this example, the values file is called `external-dns-ionos-values.yaml` .
```yaml
logLevel: debug # ExternalDNS Log level, reduce in production
namespaced: false # if true, ExternalDNS will run in a namespaced scope (Role and Rolebinding will be namespaced too).
triggerLoopOnEvent: true # if true, ExternalDNS will trigger a loop on every event (create/update/delete) on the resources it watches.
logLevel: debug
sources:
- ingress
- service
provider:
name: webhook
webhook:
image:
repository: ghcr.io/ionos-cloud/external-dns-ionos-webhook
tag: latest
pullPolicy: IfNotPresent
env:
- name: IONOS_API_KEY
valueFrom:
secretKeyRef:
name: ionos-credentials
key: api-key
- name: SERVER_PORT
value: "8888"
- name: METRICS_PORT
value: "8080"
- name: DRY_RUN
value: "false"
```
### Step 3: Install ExternalDNS Using Helm
Install ExternalDNS with the IONOS webhook provider:
```bash
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm upgrade --install external-dns external-dns/external-dns -f external-dns-ionos-values.yaml
```
## Deploying an Example Application
### Step 1: Create a Deployment
In this step we will create `echoserver` application manifest with the following content:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: echoserver
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: echoserver
template:
metadata:
labels:
app: echoserver
spec:
containers:
- name: echoserver
image: ealen/echo-server:latest
ports:
- containerPort: 80
```
Deployment manifest can be saved in `echoserver-deployment.yaml` file.
Next, we will apply the deployment:
```bash
kubectl apply -f echoserver-deployment.yaml
```
### Step 2: Create a Service
In this step, we will create a `Service` manifest to expose the `echoserver` application within the cluster. The service will also include an annotation for ExternalDNS to create a DNS record for the specified hostname.
Save the following content in a file named `echoserver-service.yaml`:
```yaml
apiVersion: v1
kind: Service
metadata:
name: echoserver
annotations:
external-dns.alpha.kubernetes.io/hostname: app.example.com
spec:
ports:
- port: 80
targetPort: 80
selector:
app: echoserver
```
**Note:** Replace `app.example.com` with a subdomain of your DNS zone configured in IONOS Cloud DNS. For example, if your DNS zone is `example.com`, you can use a subdomain like `app.example.com`.
Next, apply the service:
```bash
kubectl apply -f echoserver-service.yaml
```
This service will expose the echoserver application on port 80 and instruct ExternalDNS to create a DNS record for `app.example.com`.
### Step 3: Create an Ingress
In this step, we will create an `Ingress` resource to expose the `echoserver` application externally. The ingress will route HTTP traffic to the `echoserver` service and include a hostname that ExternalDNS will use to create the corresponding DNS record.
Save the following content in a file named `echoserver-ingress.yaml` :
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echoserver
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: echoserver
port:
number: 80
```
**Note:** Replace `app.example.com` with a subdomain of your DNS zone configured in IONOS Cloud DNS. For example, if your DNS zone is `example.com`, you can use a subdomain like `app.example.com`.
Next, apply the ingress manifest:
```bash
kubectl apply -f echoserver-ingress.yaml
```
This ingress will expose the `echoserver` application at `http://app.example.com` and instruct ExternalDNS to create a DNS record for the specified hostname.
## Accessing the Application
Once the `Ingress` resource has been applied and the DNS records have been created, you can access the application using the hostname specified in the ingress (`app.example.com`).
### Verify Application Access
Use the following `curl` command to verify that the application is accessible:
```bash
curl -I http://app.example.com
```
Replace app.example.com with the subdomain you configured in your DNS zone.
**Note:** Ensure that your DNS changes have propagated and that the hostname resolves to the correct IP address before running the command.
### Expected result
You should see an HTTP response header indicating that the application is running, such as:
```bash
HTTP/1.1 200 OK
```
> **Troubleshooting:**
>
>If you encounter any issues, verify the following:
>
> - The DNS record for `app.example.com` (replace with your own subdomain configured in IONOS Cloud DNS) has been created in IONOS Cloud DNS.
> - The ingress controller is running and properly configured in your Kubernetes cluster.
> - The `echoserver` application is running and accessible within the cluster.
## Verifying IONOS Cloud DNS Records
Use the IONOS Cloud Console or API to verify that the A and TXT records for your domain have been created. For example, you can use the following API call:
```bash
curl --location --request GET 'https://dns.de-fra.ionos.com/records?filter.name=app' \
--header 'Authorization: Bearer <IONOS_API_TOKEN>'
```
Replace `<IONOS_API_TOKEN>` with your actual API token.
The API response should include the `A` and `TXT` records for the subdomain you configured.
> **Note:** DNS changes may take a few minutes to propagate. If the records are not visible immediately, wait and try again.
## Cleanup
> **Optional:** Perform the cleanup step only if you no longer need the deployed resources.
Once you have verified the setup, you can clean up the resources created during this tutorial:
```bash
kubectl delete -f echoserver-deployment.yaml
kubectl delete -f echoserver-service.yaml
kubectl delete -f echoserver-ingress.yaml
```
## Summary
In this tutorial, you successfully deployed ExternalDNS webhook with IONOS Cloud DNS as the provider.
You created a Kubernetes deployment, service, and ingress, and verified that DNS records were created and the application was accessible.
You also learned how to clean up the resources when they are no longer needed.

View File

@ -497,6 +497,35 @@ external-dns \
--rfc2136-insecure --rfc2136-insecure
``` ```
### Helm
```yaml
extraArgs:
- --rfc2136-host="dns-host-1.yourdomain.com"
- --rfc2136-port=53
- --rfc2136-zone=example.com
- --rfc2136-tsig-secret-alg=hmac-sha256
- --rfc2136-tsig-axfr
env:
- name: "EXTERNAL_DNS_RFC2136_TSIG_SECRET"
valueFrom:
secretKeyRef:
name: rfc2136-keys
key: rfc2136-tsig-secret
- name: "EXTERNAL_DNS_RFC2136_TSIG_KEYNAME"
valueFrom:
secretKeyRef:
name: rfc2136-keys
key: rfc2136-tsig-keyname
```
#### Secret creation
```shell
kubectl create secret generic rfc2136-keys --from-literal=rfc2136-tsig-secret='xxx' --from-literal=rfc2136-tsig-keyname='k8s-external-dns-key' -n external-dns
```
### Benefits ### Benefits
- Distributes the load of DNS updates across multiple data centers, preventing any single DC from becoming a bottleneck. - Distributes the load of DNS updates across multiple data centers, preventing any single DC from becoming a bottleneck.

View File

@ -204,6 +204,7 @@ type EndpointKey struct {
} }
// Endpoint is a high-level way of a connection between a service and an IP // Endpoint is a high-level way of a connection between a service and an IP
// +kubebuilder:object:generate=true
type Endpoint struct { type Endpoint struct {
// The hostname of the DNS record // The hostname of the DNS record
DNSName string `json:"dnsName,omitempty"` DNSName string `json:"dnsName,omitempty"`
@ -337,6 +338,7 @@ func FilterEndpointsByOwnerID(ownerID string, eps []*Endpoint) []*Endpoint {
} }
// DNSEndpointSpec defines the desired state of DNSEndpoint // DNSEndpointSpec defines the desired state of DNSEndpoint
// +kubebuilder:object:generate=true
type DNSEndpointSpec struct { type DNSEndpointSpec struct {
Endpoints []*Endpoint `json:"endpoints,omitempty"` Endpoints []*Endpoint `json:"endpoints,omitempty"`
} }

View File

@ -1,28 +1,11 @@
//go:build !ignore_autogenerated //go:build !ignore_autogenerated
// +build !ignore_autogenerated
/* // Code generated by controller-gen. DO NOT EDIT.
Copyright 2018 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package endpoint package endpoint
import ( import (
"k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@ -32,7 +15,6 @@ func (in *DNSEndpoint) DeepCopyInto(out *DNSEndpoint) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec) in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status out.Status = in.Status
return
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpoint. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpoint.
@ -57,7 +39,7 @@ func (in *DNSEndpoint) DeepCopyObject() runtime.Object {
func (in *DNSEndpointList) DeepCopyInto(out *DNSEndpointList) { func (in *DNSEndpointList) DeepCopyInto(out *DNSEndpointList) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil { if in.Items != nil {
in, out := &in.Items, &out.Items in, out := &in.Items, &out.Items
*out = make([]DNSEndpoint, len(*in)) *out = make([]DNSEndpoint, len(*in))
@ -65,7 +47,6 @@ func (in *DNSEndpointList) DeepCopyInto(out *DNSEndpointList) {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
return
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointList. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointList.
@ -93,15 +74,13 @@ func (in *DNSEndpointSpec) DeepCopyInto(out *DNSEndpointSpec) {
in, out := &in.Endpoints, &out.Endpoints in, out := &in.Endpoints, &out.Endpoints
*out = make([]*Endpoint, len(*in)) *out = make([]*Endpoint, len(*in))
for i := range *in { for i := range *in {
if (*in)[i] == nil { if (*in)[i] != nil {
(*out)[i] = nil in, out := &(*in)[i], &(*out)[i]
} else { *out = new(Endpoint)
(*out)[i] = new(Endpoint) (*in).DeepCopyInto(*out)
(*in)[i].DeepCopyInto((*out)[i])
} }
} }
} }
return
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointSpec. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointSpec.
@ -114,22 +93,6 @@ func (in *DNSEndpointSpec) DeepCopy() *DNSEndpointSpec {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DNSEndpointStatus) DeepCopyInto(out *DNSEndpointStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointStatus.
func (in *DNSEndpointStatus) DeepCopy() *DNSEndpointStatus {
if in == nil {
return nil
}
out := new(DNSEndpointStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Endpoint) DeepCopyInto(out *Endpoint) { func (in *Endpoint) DeepCopyInto(out *Endpoint) {
*out = *in *out = *in
@ -148,11 +111,8 @@ func (in *Endpoint) DeepCopyInto(out *Endpoint) {
if in.ProviderSpecific != nil { if in.ProviderSpecific != nil {
in, out := &in.ProviderSpecific, &out.ProviderSpecific in, out := &in.ProviderSpecific, &out.ProviderSpecific
*out = make(ProviderSpecific, len(*in)) *out = make(ProviderSpecific, len(*in))
for key, val := range *in { copy(*out, *in)
(*out)[key] = val
}
} }
return
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Endpoint. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Endpoint.
@ -164,67 +124,3 @@ func (in *Endpoint) DeepCopy() *Endpoint {
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Labels) DeepCopyInto(out *Labels) {
{
in := &in
*out = make(Labels, len(*in))
for key, val := range *in {
(*out)[key] = val
}
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Labels.
func (in Labels) DeepCopy() Labels {
if in == nil {
return nil
}
out := new(Labels)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in ProviderSpecific) DeepCopyInto(out *ProviderSpecific) {
{
in := &in
*out = make(ProviderSpecific, len(*in))
for key, val := range *in {
(*out)[key] = val
}
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderSpecific.
func (in ProviderSpecific) DeepCopy() ProviderSpecific {
if in == nil {
return nil
}
out := new(ProviderSpecific)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Targets) DeepCopyInto(out *Targets) {
{
in := &in
*out = make(Targets, len(*in))
copy(*out, *in)
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Targets.
func (in Targets) DeepCopy() Targets {
if in == nil {
return nil
}
out := new(Targets)
in.DeepCopyInto(out)
return *out
}

105
go.mod
View File

@ -1,37 +1,37 @@
module sigs.k8s.io/external-dns module sigs.k8s.io/external-dns
go 1.24.0 go 1.24.2
require ( require (
cloud.google.com/go/compute/metadata v0.6.0 cloud.google.com/go/compute/metadata v0.6.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.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.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1 github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.0 github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2
github.com/IBM/go-sdk-core/v5 v5.19.0 github.com/IBM/go-sdk-core/v5 v5.19.1
github.com/IBM/networking-go-sdk v0.51.3 github.com/IBM/networking-go-sdk v0.51.4
github.com/Yamashou/gqlgenc v0.31.0 github.com/Yamashou/gqlgenc v0.32.1
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2
github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/kingpin/v2 v2.4.0
github.com/aliyun/alibaba-cloud-sdk-go v1.63.104 github.com/aliyun/alibaba-cloud-sdk-go v1.63.107
github.com/aws/aws-sdk-go-v2 v1.36.3 github.com/aws/aws-sdk-go-v2 v1.36.3
github.com/aws/aws-sdk-go-v2/config v1.29.13 github.com/aws/aws-sdk-go-v2/config v1.29.14
github.com/aws/aws-sdk-go-v2/credentials v1.17.66 github.com/aws/aws-sdk-go-v2/credentials v1.17.67
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.9 github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.19.0
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.42.1 github.com/aws/aws-sdk-go-v2/service/dynamodb v1.43.1
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.0 github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.2 github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.4
github.com/aws/aws-sdk-go-v2/service/sts v1.33.18 github.com/aws/aws-sdk-go-v2/service/sts v1.33.19
github.com/bodgit/tsig v1.2.2 github.com/bodgit/tsig v1.2.2
github.com/cenkalti/backoff/v4 v4.3.0 github.com/cenkalti/backoff/v5 v5.0.2
github.com/civo/civogo v0.3.96 github.com/civo/civogo v0.4.1
github.com/cloudflare/cloudflare-go v0.115.0 github.com/cloudflare/cloudflare-go v0.115.0
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
github.com/datawire/ambassador v1.12.4 github.com/datawire/ambassador v1.12.4
github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace
github.com/digitalocean/godo v1.142.0 github.com/digitalocean/godo v1.145.0
github.com/dnsimple/dnsimple-go v1.7.0 github.com/dnsimple/dnsimple-go v1.7.0
github.com/exoscale/egoscale v0.102.3 github.com/exoscale/egoscale v0.102.3
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99 github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
@ -47,19 +47,18 @@ require (
github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo v1.16.5
github.com/openshift/api v0.0.0-20230607130528-611114dca681 github.com/openshift/api v0.0.0-20230607130528-611114dca681
github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3 github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3
github.com/oracle/oci-go-sdk/v65 v65.88.1 github.com/oracle/oci-go-sdk/v65 v65.89.3
github.com/ovh/go-ovh v1.7.0 github.com/ovh/go-ovh v1.7.0
github.com/patrickmn/go-cache v2.1.0+incompatible 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/pluralsh/gqlclient v1.12.2
github.com/projectcontour/contour v1.30.3 github.com/projectcontour/contour v1.30.3
github.com/prometheus/client_golang v1.21.1 github.com/prometheus/client_golang v1.22.0
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1140 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1158
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1132 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145
github.com/transip/gotransip/v6 v6.26.0 github.com/transip/gotransip/v6 v6.26.0
github.com/ultradns/ultradns-sdk-go v1.3.7 github.com/ultradns/ultradns-sdk-go v1.3.7
go.etcd.io/etcd/client/v3 v3.5.21 go.etcd.io/etcd/client/v3 v3.5.21
@ -69,25 +68,25 @@ require (
golang.org/x/sync v0.13.0 golang.org/x/sync v0.13.0
golang.org/x/text v0.24.0 golang.org/x/text v0.24.0
golang.org/x/time v0.11.0 golang.org/x/time v0.11.0
google.golang.org/api v0.228.0 google.golang.org/api v0.231.0
gopkg.in/ns1/ns1-go.v2 v2.14.2 gopkg.in/ns1/ns1-go.v2 v2.14.2
istio.io/api v1.25.1 istio.io/api v1.25.2
istio.io/client-go v1.25.1 istio.io/client-go v1.25.2
k8s.io/api v0.32.3 k8s.io/api v0.33.0
k8s.io/apimachinery v0.32.3 k8s.io/apimachinery v0.33.0
k8s.io/client-go v0.32.3 k8s.io/client-go v0.33.0
k8s.io/klog/v2 v2.130.1 k8s.io/klog/v2 v2.130.1
sigs.k8s.io/gateway-api v1.2.1 sigs.k8s.io/gateway-api v1.3.0
) )
require ( require (
cloud.google.com/go/auth v0.15.0 // indirect cloud.google.com/go/auth v0.16.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
github.com/99designs/gqlgen v0.17.61 // indirect github.com/99designs/gqlgen v0.17.71 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/Masterminds/semver v1.4.2 // indirect github.com/Masterminds/semver v1.5.0 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
@ -95,7 +94,7 @@ require (
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.25.2 // indirect github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.25.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
@ -123,7 +122,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.24.0 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/go-resty/resty/v2 v2.16.5 // indirect github.com/go-resty/resty/v2 v2.16.5 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/flock v0.8.1 // indirect
@ -131,9 +130,8 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/s2a-go v0.1.9 // indirect github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
@ -154,7 +152,6 @@ require (
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
@ -171,6 +168,7 @@ require (
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/peterhellberg/link v1.1.0 // indirect github.com/peterhellberg/link v1.1.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/common v0.62.0 // indirect
@ -180,10 +178,10 @@ require (
github.com/shopspring/decimal v1.3.1 // indirect github.com/shopspring/decimal v1.3.1 // indirect
github.com/sony/gobreaker v0.5.0 // indirect github.com/sony/gobreaker v0.5.0 // indirect
github.com/sosodev/duration v1.3.1 // indirect github.com/sosodev/duration v1.3.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/objx v0.5.2 // indirect
github.com/terra-farm/udnssdk v1.3.5 // indirect github.com/terra-farm/udnssdk v1.3.5 // indirect
github.com/vektah/gqlparser/v2 v2.5.20 // indirect github.com/vektah/gqlparser/v2 v2.5.25 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
@ -191,21 +189,21 @@ require (
go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect
go.mongodb.org/mongo-driver v1.17.2 // indirect go.mongodb.org/mongo-driver v1.17.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.uber.org/atomic v1.10.0 // indirect go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.37.0 // indirect golang.org/x/crypto v0.37.0 // indirect
golang.org/x/mod v0.23.0 // indirect golang.org/x/mod v0.24.0 // indirect
golang.org/x/sys v0.32.0 // indirect golang.org/x/sys v0.32.0 // indirect
golang.org/x/term v0.31.0 // indirect golang.org/x/term v0.31.0 // indirect
golang.org/x/tools v0.30.0 // indirect golang.org/x/tools v0.32.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 // indirect
google.golang.org/grpc v1.71.0 // indirect google.golang.org/grpc v1.72.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
@ -213,11 +211,12 @@ require (
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
moul.io/http2curl v1.0.0 // indirect moul.io/http2curl v1.0.0 // indirect
sigs.k8s.io/controller-runtime v0.18.7 // indirect sigs.k8s.io/controller-runtime v0.20.4 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect
) )

241
go.sum
View File

@ -2,8 +2,8 @@ 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.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.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 v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
@ -12,18 +12,18 @@ code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f h1:UrKzEwTg
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f/go.mod h1:sk5LnIjB/nIEU7yP5sDQExVm62wu0pBh3yrElngUisI= 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= 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= git.lukeshu.com/go/libsystemd v0.5.3/go.mod h1:FfDoP0i92r4p5Vn4NCLxvjkd7rCOe6otPa4L6hZg9WM=
github.com/99designs/gqlgen v0.17.61 h1:vE7xLRC066n9wehgjeplILOWtwz75zbzcV2/Iv9i3pw= github.com/99designs/gqlgen v0.17.71 h1:6JdwweHlSMWGY+6VWY5ey0tO+sF8LckbUV0NmdOQi04=
github.com/99designs/gqlgen v0.17.61/go.mod h1:rFU1T3lhv/tPeAlww/DJ4ol2YxT/pPpue+xxPbkd3r4= github.com/99designs/gqlgen v0.17.71/go.mod h1:3yz6ekwCAjC90zaFvPoy+mEjaKiyYJjhtCnwn1seoxE=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbxukD1WuWXOiE9fRdu32f2I= 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 v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.0 h1:Bg8m3nq/X1DeePkAbCfb6ml6F3F0IunEhE8TMh+lY48= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.0/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
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 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/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0=
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 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw=
@ -51,17 +51,18 @@ github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1 h1:NHWjSBeXbL8mlx+0QyCl4OrUvytCZ3nkEIRqX7t97wQ= github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1 h1:NHWjSBeXbL8mlx+0QyCl4OrUvytCZ3nkEIRqX7t97wQ=
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1/go.mod h1:JwdtGjHFTmUM1zjzvvCotCCyP55S146IuVPOJZ7D/Jw= github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1/go.mod h1:JwdtGjHFTmUM1zjzvvCotCCyP55S146IuVPOJZ7D/Jw=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.0 h1:VMMr9Yu2QkIdXtQfi3v/+qoqlnzUuFx7vfU+GDfuhh0= github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2 h1:eW5o8NpblAyqPjwOlZ+XISdhlYynjf7B7dsCmsvfC/s=
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.0/go.mod h1:+fXHt9ZUw/AlSAHvQXkTcEMKXLVwSBvA7uReQXqDwmU= github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2/go.mod h1:HulyrJLLc9FSZlwKQ9vu5Jq83thNlUfg1afonOdhrRA=
github.com/IBM/go-sdk-core/v5 v5.19.0 h1:YN2S5JUvq/EwYulmcNFwgyYBxZhVWl9nkY22H7Hpghw= github.com/IBM/go-sdk-core/v5 v5.19.1 h1:sleVks1O4XjgF4YEGvyDh6PZbP6iZhlTPeDkQc8nWDs=
github.com/IBM/go-sdk-core/v5 v5.19.0/go.mod h1:deZO1J5TSlU69bCnl/YV7nPxFZA2UEaup7cq/7ZTOgw= github.com/IBM/go-sdk-core/v5 v5.19.1/go.mod h1:Q3BYO6iDA2zweQPDGbNTtqft5tDcEpm6RTuqMlPcvbw=
github.com/IBM/networking-go-sdk v0.51.3 h1:GW2VLP7XVcAHin6eWnCNA9l3gsv5tnyjf5UgGwMA7hk= github.com/IBM/networking-go-sdk v0.51.4 h1:rkbR+gUkksLKjNYL5YEWEAMv3qddR0mUkxObDMa4l/s=
github.com/IBM/networking-go-sdk v0.51.3/go.mod h1:T27XI2gtPjT7tW9nkHgrpBUNbmAc9OR41Z78Y493PoA= github.com/IBM/networking-go-sdk v0.51.4/go.mod h1:gjCFEp+UVP7FUlcq2C1RaoZAXFcD39CQdlUk7uVKko4=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig v2.17.1+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig v2.17.1+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA=
@ -81,8 +82,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/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/Yamashou/gqlgenc v0.31.0 h1:t54Gi6Zx9Ub1oa/RvJ+DkOC1c2wdK1R4n7wyRBuQkyc= github.com/Yamashou/gqlgenc v0.32.1 h1:EHs9//xQxXlyltkSFXM+fhO2rTXcWNw6FPKRJ6t+iQQ=
github.com/Yamashou/gqlgenc v0.31.0/go.mod h1:0VNZtzXhyDofkNNZXXPw8LSLvi3vyG6LXo7XvcWv2X4= github.com/Yamashou/gqlgenc v0.32.1/go.mod h1:o5SxKt9d3+oUZ2i0V3CW8lHFyunfLR+KcKHubS4zf5E=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
@ -98,8 +99,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/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 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro= github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.104 h1:/6jF6rhmKZzvZZhzFXWqATazSKL5OSv7p+8PfoT6MG0= github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.104/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/alibaba-cloud-sdk-go v1.63.107/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 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= 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= github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U=
@ -123,12 +124,12 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.29.13 h1:RgdPqWoE8nPpIekpVpDJsBckbqT4Liiaq9f35pbTh1Y= github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM=
github.com/aws/aws-sdk-go-v2/config v1.29.13/go.mod h1:NI28qs/IOUIRhsR7GQ/JdexoqRN9tDxkIrYZq0SOF44= github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
github.com/aws/aws-sdk-go-v2/credentials v1.17.66 h1:aKpEKaTy6n4CEJeYI1MNj97oSDLi4xro3UzQfwf5RWE= github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
github.com/aws/aws-sdk-go-v2/credentials v1.17.66/go.mod h1:xQ5SusDmHb/fy55wU0QqTy0yNfLqxzec59YcsRZB+rI= github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.9 h1:EU6VkY8G4N+IFl0D2Cd9LcUeJHyNdLJAbHfMD9v5GHQ= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.19.0 h1:F3W0YqWZrpCcelbvXMP9LWSTOI620aAq1+8fZ/71TBg=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.9/go.mod h1:JlH7zEPanxEEBLAAnKBRNZz+nrxTTMVKO40P5+umoUQ= github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.19.0/go.mod h1:34X+UzFJwsQfyk5U1hYiCO/gv9ZVL+Hh8w+bJQ6+HbU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
@ -137,26 +138,26 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0io
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.42.1 h1:67oYHlAdIoWS65kdTKatf9o1eDNkR2wan6TlBdP3oe4= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.43.1 h1:YYjNTAyPL0425ECmq6Xm48NSXdT6hDVQmLOJZxyhNTM=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.42.1/go.mod h1:yYaWRnVSPyAmexW5t7G3TcuYoalYfT+xQwzWsvtUQ7M= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.43.1/go.mod h1:yYaWRnVSPyAmexW5t7G3TcuYoalYfT+xQwzWsvtUQ7M=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.25.2 h1:D1Af/NlGfG2/8S3EY/hCUlvPcfu2UrX4+XaGeiFzJQM= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.25.3 h1:GHC1WTF3ZBZy+gvz2qtYB6ttALVx35hlwc4IzOIUY7g=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.25.2/go.mod h1:lUqWdw5/esjPTkITXhN4C66o1ltwDq2qQ12j3SOzhVg= github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.25.3/go.mod h1:lUqWdw5/esjPTkITXhN4C66o1ltwDq2qQ12j3SOzhVg=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15 h1:M1R1rud7HzDrfCdlBQ7NjnRsDNEhXO/vGhuD189Ggmk= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15 h1:M1R1rud7HzDrfCdlBQ7NjnRsDNEhXO/vGhuD189Ggmk=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15/go.mod h1:uvFKBSq9yMPV4LGAi7N4awn4tLY+hKE35f8THes2mzQ= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15/go.mod h1:uvFKBSq9yMPV4LGAi7N4awn4tLY+hKE35f8THes2mzQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.0 h1:pK3YJIgOzYqctprqQ67kGSjeL+77r9Ue/4/gBonsGNc= github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 h1:41HrH51fydStW2Tah74zkqZlJfyx4gXeuGOdsIFuckY=
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.0/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y= github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.2 h1:KDXGFjFqMc31WyGljYA1Jb6yMH+YS22iC32NcYR8mZ8= github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.4 h1:zZvziql5vgfDs2hTfF8fRF4pySG7A28/qNJQihJvpwc=
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.2/go.mod h1:IbC8X3WZvsN+w48OrHBDUKcVnhhzO1YpXkCkFlr0qs8= github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.4/go.mod h1:IbC8X3WZvsN+w48OrHBDUKcVnhhzO1YpXkCkFlr0qs8=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.18 h1:xz7WvTMfSStb9Y8NpCT82FXLNC3QasqBfuAFHY4Pk5g= github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.18/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
@ -179,16 +180,16 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 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/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/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/civo/civogo v0.3.96 h1:9R3yZS3B8B0oAqIlNDnMvsONk0mqZUkHREd0EH6HRIc= github.com/civo/civogo v0.4.1 h1:C+lwZ7hBqKy6eKy6qgviuselF0V5Z/um0x7X/eLEQ64=
github.com/civo/civogo v0.3.96/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc= github.com/civo/civogo v0.4.1/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM= github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
@ -257,8 +258,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 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-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/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/digitalocean/godo v1.142.0 h1:OZwDccTSL7yrgkGIiPMcJ4DXD8tCpu3xfbEVHAIB8mw= github.com/digitalocean/godo v1.145.0 h1:xBhWr+vCBy7GsexCUsWC+dKhPAWBMRLazavvXwyPBp8=
github.com/digitalocean/godo v1.142.0/go.mod h1:tYeiWY5ZXVpU48YaFv0M5irUFHXGorZpDNm7zzdWMzM= github.com/digitalocean/godo v1.145.0/go.mod h1:tYeiWY5ZXVpU48YaFv0M5irUFHXGorZpDNm7zzdWMzM=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY= 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= github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8=
@ -303,8 +304,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/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/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.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@ -418,15 +419,15 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 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-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= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
@ -492,8 +493,8 @@ github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkY
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -509,8 +510,6 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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/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-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
@ -650,16 +649,16 @@ 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/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/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/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.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -724,8 +723,8 @@ github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIG
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@ -810,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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 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 v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ= github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 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.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.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.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.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.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 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-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@ -848,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.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.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/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.88.1 h1:Y9Y5jlQX8oVDe3UN+O4IcQnLN/aQmi4jR1/2RsXMN3M= github.com/oracle/oci-go-sdk/v65 v65.89.3 h1:KSUykb5Ou54jF4SeJNjBwcDg+umbAwcvT+xhrvNDog0=
github.com/oracle/oci-go-sdk/v65 v65.88.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= github.com/oracle/oci-go-sdk/v65 v65.89.3/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw= github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw=
github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
@ -893,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.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.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.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 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-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@ -925,8 +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/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/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/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@ -991,8 +990,9 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
@ -1018,14 +1018,14 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 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/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/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1132/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1140 h1:F/vuesfLQmJhBZTHYOktz8IVWTs1nHwZYh74/H64uEs= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1145/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1140/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1158 h1:N+C8Tz6JKGwnDFDfd3g5CkTsiKTa6/Uia0uAL0OhimE=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1158/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 h1:kMIdSU5IvpOROh27ToVQ3hlm6ym3lCRs9tnGCOBoZqk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 h1:kMIdSU5IvpOROh27ToVQ3hlm6ym3lCRs9tnGCOBoZqk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136/go.mod h1:FpyIz3mymKaExVs6Fz27kxDBS42jqZn7vbACtxdeEH4= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136/go.mod h1:FpyIz3mymKaExVs6Fz27kxDBS42jqZn7vbACtxdeEH4=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1132 h1:YjVToKgm2V8aUxiDahVEFOvXYgswXiROUhrfSc3jamE= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145 h1:K5N0Uxqm9kM7KU6DFBekCTKbldlXq6UD1ekOyXn4zEc=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1132/go.mod h1:2xDMNe2ohV2hRBldVgv++eByAhj3vSMnh0xl9jRFiEU= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145/go.mod h1:mgwxarWzLxIylWtHmkoMg0dX8aqhO5lwfrHkK4IGLKE=
github.com/terra-farm/udnssdk v1.3.5 h1:MNR3adfuuEK/l04+jzo8WW/0fnorY+nW515qb3vEr6I= 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/terra-farm/udnssdk v1.3.5/go.mod h1:8RnM56yZTR7mYyUIvrDgXzdRaEyFIzqdEi7+um26Sv8=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@ -1052,8 +1052,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/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/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 v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/vektah/gqlparser/v2 v2.5.20 h1:kPaWbhBntxoZPaNdBaIPT1Kh0i1b/onb5kXgEdP5JCo= github.com/vektah/gqlparser/v2 v2.5.25 h1:FmWtFEa+invTIzWlWK6Vk7BVEZU/97QBzeI8Z1JjGt8=
github.com/vektah/gqlparser/v2 v2.5.20/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM= github.com/vektah/gqlparser/v2 v2.5.25/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 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/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -1098,24 +1098,26 @@ 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.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@ -1127,8 +1129,8 @@ go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJh
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -1182,8 +1184,8 @@ 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.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.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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1366,8 +1368,8 @@ golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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.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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1380,8 +1382,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.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.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.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.228.0 h1:X2DJ/uoWGnY5obVjewbp8icSL5U4FzuCfy9OjbLSnLs= google.golang.org/api v0.231.0 h1:LbUD5FUl0C4qwia2bjXhCMH65yz1MLPzA/0OYEsYY7Q=
google.golang.org/api v0.228.0/go.mod h1:wNvRS1Pbe8r4+IfBIniV8fwCpGwTrYa+kMUDiC5z5a4= google.golang.org/api v0.231.0/go.mod h1:H52180fPI/QQlUc0F4xWfGZILdv09GCWKt2bcsn164A=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 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.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -1395,10 +1397,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-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-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-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 h1:29cjnHVylHwTzH66WfFZqgSQgnxzvWE+jvBwpZCLRxY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 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.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@ -1412,8 +1414,8 @@ 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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.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.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 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-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 v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -1480,23 +1482,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-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.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
istio.io/api v1.25.1 h1:l3JS6Rh4bH+6ox84hRvtsJsr7gMJLsoJG2BQZGbXRyc= istio.io/api v1.25.2 h1:FCRQy7iaTreKJdLemlQ1vRJEsf1soCHoTAuSf68w5I8=
istio.io/api v1.25.1/go.mod h1:QFzEXv/IT582T0FHZVp1QoolvE4ws0zz/vVO55blmlE= istio.io/api v1.25.2/go.mod h1:QFzEXv/IT582T0FHZVp1QoolvE4ws0zz/vVO55blmlE=
istio.io/client-go v1.25.1 h1:1Asibz5v2NBA6w4Sqa85NS7TkpEolZcPKAT5oUAXWi8= istio.io/client-go v1.25.2 h1:faupTqeMD0PkuNTZdFlpxxT35jBSQapK5k+MNAkXvqA=
istio.io/client-go v1.25.1/go.mod h1:Vap9OyHJMvvDegYoZczcNybS4wbPaTk+4bZcWMb8+vE= istio.io/client-go v1.25.2/go.mod h1:E2LTxTcCVe4cqpKy4/9Y4VmwSoLiH6ff9MEG7EhfSDo=
k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= 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.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4=
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= 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.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= 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.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.2/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.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= 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.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8=
@ -1505,8 +1507,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.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8=
k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= 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.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g=
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= 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.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
@ -1524,8 +1526,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 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-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-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU=
k8s.io/kubectl v0.18.4/go.mod h1:EzB+nfeUWk6fm6giXQ8P4Fayw3dsN+M7Wjy23mTRtB0= k8s.io/kubectl v0.18.4/go.mod h1:EzB+nfeUWk6fm6giXQ8P4Fayw3dsN+M7Wjy23mTRtB0=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
@ -1541,18 +1543,21 @@ rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 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/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.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A=
sigs.k8s.io/controller-runtime v0.18.7 h1:WDnx8LTRY8Fn1j/7B+S/R9MeDjWNAzpDBoaSvMSrQME= sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=
sigs.k8s.io/controller-runtime v0.18.7/go.mod h1:L9r3fUZhID7Q9eK9mseNskpaTg2n11f/tlb8odyzJ4Y= sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
sigs.k8s.io/controller-tools v0.3.1-0.20200517180335-820a4a27ea84/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI= sigs.k8s.io/controller-tools v0.3.1-0.20200517180335-820a4a27ea84/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
sigs.k8s.io/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM= sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M=
sigs.k8s.io/gateway-api v1.2.1/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= 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/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/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
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-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/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 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.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=

View File

@ -17,33 +17,100 @@ limitations under the License.
package testutils package testutils
import ( import (
"bytes" "strings"
"flag"
"testing" "testing"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"k8s.io/klog/v2" "github.com/stretchr/testify/assert"
"github.com/sirupsen/logrus/hooks/test"
) )
// LogsToBuffer redirects log(s) output to a buffer for testing purposes // LogsUnderTestWithLogLevel redirects log(s) output to a buffer for testing purposes
// //
// Usage: LogsToBuffer(t) // Usage: LogsUnderTestWithLogLevel(t)
// Example: // Example:
// //
// buf := LogsToBuffer(log.DebugLevel, t) // hook := testutils.LogsUnderTestWithLogLevel(log.DebugLevel, t)
// ... do something that logs ... // ... do something that logs ...
// assert.Contains(t, buf.String(), "expected debug log message") //
func LogsToBuffer(level log.Level, t *testing.T) *bytes.Buffer { // testutils.TestHelperLogContains("expected debug log message", hook, t)
func LogsUnderTestWithLogLevel(level log.Level, t *testing.T) *test.Hook {
t.Helper() t.Helper()
buf := new(bytes.Buffer) logger, hook := test.NewNullLogger()
log.SetOutput(buf)
log.AddHook(hook)
log.SetOutput(logger.Out)
log.SetLevel(level) log.SetLevel(level)
klog.SetOutput(buf) return hook
flags := &flag.FlagSet{} }
klog.InitFlags(flags)
// make sure klog doesn't write to stderr by default in tests // TestHelperLogContains verifies that a specific log message is present
_ = flags.Set("logtostderr", "false") // in the captured log entries. It asserts that the provided message `msg`
_ = flags.Set("alsologtostderr", "false") // appears in at least one of the log entries recorded by the `hook`.
_ = flags.Set("stderrthreshold", "4") //
return buf // Parameters:
// - msg: The log message that should be found.
// - hook: The test hook capturing log entries.
// - t: The testing object used for assertions.
//
// Usage:
// This helper is used in tests to ensure that certain log messages are
// logged during the execution of the code under test.
func TestHelperLogContains(msg string, hook *test.Hook, t *testing.T) {
t.Helper()
isContains := false
for _, entry := range hook.AllEntries() {
if strings.Contains(entry.Message, msg) {
isContains = true
}
}
assert.True(t, isContains, "Expected log message not found: %s", msg)
}
// TestHelperLogNotContains verifies that a specific log message is not present
// in the captured log entries. It asserts that the provided message `msg`
// does not appear in any of the log entries recorded by the `hook`.
//
// Parameters:
// - msg: The log message that should not be found.
// - hook: The test hook capturing log entries.
// - t: The testing object used for assertions.
//
// Usage:
// This helper is used in tests to ensure that certain log messages are not
// logged during the execution of the code under test.
func TestHelperLogNotContains(msg string, hook *test.Hook, t *testing.T) {
t.Helper()
isContains := false
for _, entry := range hook.AllEntries() {
if strings.Contains(entry.Message, msg) {
isContains = true
}
}
assert.False(t, isContains, "Expected log message found when should not: %s", msg)
}
// TestHelperLogContainsWithLogLevel verifies that a specific log message with a given log level
// is present in the captured log entries. It asserts that the provided message `msg`
// appears in at least one of the log entries recorded by the `hook` with the specified log level.
//
// Parameters:
// - msg: The log message that should be found.
// - level: The log level that the message should have.
// - hook: The test hook capturing log entries.
// - t: The testing object used for assertions.
//
// Usage:
// This helper is used in tests to ensure that certain log messages with a specific log level
// are logged during the execution of the code under test.
func TestHelperLogContainsWithLogLevel(msg string, level log.Level, hook *test.Hook, t *testing.T) {
t.Helper()
isContains := false
for _, entry := range hook.AllEntries() {
if strings.Contains(entry.Message, msg) && entry.Level == level {
isContains = true
}
}
assert.True(t, isContains, "Expected log message not found: %s with level %s", msg, level)
} }

View File

@ -29,9 +29,10 @@ nav:
- Leader Election: docs/proposal/001-leader-election.md - Leader Election: docs/proposal/001-leader-election.md
- Monitoring: docs/monitoring/* - Monitoring: docs/monitoring/*
- MultiTarget: docs/proposal/multi-target.md - MultiTarget: docs/proposal/multi-target.md
- NAT64: docs/nat64.md - NAT64: docs/advanced/nat64.md
- Rate Limits: docs/rate-limits.md - Rate Limits: docs/advanced/rate-limits.md
- TTL: docs/ttl.md - TTL: docs/advanced/ttl.md
- FQDN Templating: docs/advanced/fqdn-templating.md
- Contributing: - Contributing:
- Kubernetes Contributions: CONTRIBUTING.md - Kubernetes Contributions: CONTRIBUTING.md
- Release: docs/release. - Release: docs/release.

View File

@ -217,167 +217,168 @@ type Config struct {
} }
var defaultConfig = &Config{ var defaultConfig = &Config{
APIServerURL: "", AkamaiAccessToken: "",
KubeConfig: "", AkamaiClientSecret: "",
RequestTimeout: time.Second * 30, AkamaiClientToken: "",
DefaultTargets: []string{}, AkamaiEdgercPath: "",
GlooNamespaces: []string{"gloo-system"}, AkamaiEdgercSection: "",
SkipperRouteGroupVersion: "zalando.org/v1", AkamaiServiceConsumerDomain: "",
Sources: nil, AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
Namespace: "", AnnotationFilter: "",
AnnotationFilter: "", APIServerURL: "",
LabelFilter: labels.Everything().String(), AWSAPIRetries: 3,
IngressClassNames: nil, AWSAssumeRole: "",
FQDNTemplate: "", AWSAssumeRoleExternalID: "",
CombineFQDNAndAnnotation: false, AWSBatchChangeInterval: time.Second,
IgnoreHostnameAnnotation: false, AWSBatchChangeSize: 1000,
IgnoreIngressTLSSpec: false, AWSBatchChangeSizeBytes: 32000,
IgnoreIngressRulesSpec: false, AWSBatchChangeSizeValues: 1000,
GatewayName: "", AWSDynamoDBRegion: "",
GatewayNamespace: "", AWSDynamoDBTable: "external-dns",
GatewayLabelFilter: "", AWSEvaluateTargetHealth: true,
Compatibility: "", AWSPreferCNAME: false,
PublishInternal: false, AWSSDCreateTag: map[string]string{},
PublishHostIP: false, AWSSDServiceCleanup: false,
ExposeInternalIPV6: true, AWSZoneCacheDuration: 0 * time.Second,
ConnectorSourceServer: "localhost:8080", AWSZoneMatchParent: false,
Provider: "", AWSZoneTagFilter: []string{},
ProviderCacheTime: 0, AWSZoneType: "",
GoogleProject: "", AzureConfigFile: "/etc/kubernetes/azure.json",
GoogleBatchChangeSize: 1000, AzureResourceGroup: "",
GoogleBatchChangeInterval: time.Second, AzureSubscriptionID: "",
GoogleZoneVisibility: "", AzureZonesCacheDuration: 0 * time.Second,
DomainFilter: []string{}, CFAPIEndpoint: "",
ZoneIDFilter: []string{}, CFPassword: "",
ExcludeDomains: []string{}, CFUsername: "",
RegexDomainFilter: regexp.MustCompile(""),
RegexDomainExclusion: regexp.MustCompile(""),
TargetNetFilter: []string{},
ExcludeTargetNets: []string{},
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
AWSZoneType: "",
AWSZoneTagFilter: []string{},
AWSZoneMatchParent: false,
AWSAssumeRole: "",
AWSAssumeRoleExternalID: "",
AWSBatchChangeSize: 1000,
AWSBatchChangeSizeBytes: 32000,
AWSBatchChangeSizeValues: 1000,
AWSBatchChangeInterval: time.Second,
AWSEvaluateTargetHealth: true,
AWSAPIRetries: 3,
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,
CloudflareCustomHostnames: false,
CloudflareCustomHostnamesMinTLSVersion: "1.0",
CloudflareCustomHostnamesCertificateAuthority: "google", CloudflareCustomHostnamesCertificateAuthority: "google",
CloudflareCustomHostnames: false,
CloudflareCustomHostnamesMinTLSVersion: "1.0",
CloudflareDNSRecordsPerPage: 100, CloudflareDNSRecordsPerPage: 100,
CloudflareProxied: false,
CloudflareRegionKey: "earth", CloudflareRegionKey: "earth",
CoreDNSPrefix: "/skydns/",
AkamaiServiceConsumerDomain: "", CombineFQDNAndAnnotation: false,
AkamaiClientToken: "", Compatibility: "",
AkamaiClientSecret: "", ConnectorSourceServer: "localhost:8080",
AkamaiAccessToken: "", CoreDNSPrefix: "/skydns/",
AkamaiEdgercSection: "", CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1",
AkamaiEdgercPath: "", CRDSourceKind: "DNSEndpoint",
OCIConfigFile: "/etc/kubernetes/oci.yaml", DefaultTargets: []string{},
OCIZoneScope: "GLOBAL", DigitalOceanAPIPageSize: 50,
OCIZoneCacheDuration: 0 * time.Second, DomainFilter: []string{},
InMemoryZones: []string{}, DryRun: false,
OVHEndpoint: "ovh-eu", ExcludeDNSRecordTypes: []string{},
OVHApiRateLimit: 20, ExcludeDomains: []string{},
OVHEnableCNAMERelative: false, ExcludeTargetNets: []string{},
PDNSServer: "http://localhost:8081", ExcludeUnschedulable: true,
PDNSServerID: "localhost", ExoscaleAPIEnvironment: "api",
PDNSAPIKey: "", ExoscaleAPIKey: "",
PDNSSkipTLSVerify: false, ExoscaleAPISecret: "",
PodSourceDomain: "", ExoscaleAPIZone: "ch-gva-2",
TLSCA: "", ExposeInternalIPV6: true,
TLSClientCert: "", FQDNTemplate: "",
TLSClientCertKey: "", GatewayLabelFilter: "",
Policy: "sync", GatewayName: "",
Registry: "txt", GatewayNamespace: "",
TXTOwnerID: "default", GlooNamespaces: []string{"gloo-system"},
TXTPrefix: "", GoDaddyAPIKey: "",
TXTSuffix: "", GoDaddyOTE: false,
TXTCacheInterval: 0, GoDaddySecretKey: "",
TXTWildcardReplacement: "", GoDaddyTTL: 600,
MinEventSyncInterval: 5 * time.Second, GoogleBatchChangeInterval: time.Second,
TXTEncryptEnabled: false, GoogleBatchChangeSize: 1000,
TXTEncryptAESKey: "", GoogleProject: "",
TXTNewFormatOnly: false, GoogleZoneVisibility: "",
Interval: time.Minute, IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json",
Once: false, IBMCloudProxied: false,
DryRun: false, IgnoreHostnameAnnotation: false,
UpdateEvents: false, IgnoreIngressRulesSpec: false,
LogFormat: "text", IgnoreIngressTLSSpec: false,
MetricsAddress: ":7979", IngressClassNames: nil,
LogLevel: logrus.InfoLevel.String(), InMemoryZones: []string{},
ExoscaleAPIEnvironment: "api", Interval: time.Minute,
ExoscaleAPIZone: "ch-gva-2", KubeConfig: "",
ExoscaleAPIKey: "", LabelFilter: labels.Everything().String(),
ExoscaleAPISecret: "", LogFormat: "text",
CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1", LogLevel: logrus.InfoLevel.String(),
CRDSourceKind: "DNSEndpoint", ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
ServiceTypeFilter: []string{}, MetricsAddress: ":7979",
CFAPIEndpoint: "", MinEventSyncInterval: 5 * time.Second,
CFUsername: "", Namespace: "",
CFPassword: "", NAT64Networks: []string{},
RFC2136Host: []string{""}, NS1Endpoint: "",
RFC2136Port: 0, NS1IgnoreSSL: false,
RFC2136Zone: []string{}, OCIConfigFile: "/etc/kubernetes/oci.yaml",
RFC2136Insecure: false, OCIZoneCacheDuration: 0 * time.Second,
RFC2136GSSTSIG: false, OCIZoneScope: "GLOBAL",
RFC2136KerberosRealm: "", Once: false,
RFC2136KerberosUsername: "", OVHApiRateLimit: 20,
RFC2136KerberosPassword: "", OVHEnableCNAMERelative: false,
RFC2136TSIGKeyName: "", OVHEndpoint: "ovh-eu",
RFC2136TSIGSecret: "", PDNSAPIKey: "",
RFC2136TSIGSecretAlg: "", PDNSServer: "http://localhost:8081",
RFC2136TAXFR: true, PDNSServerID: "localhost",
RFC2136MinTTL: 0, PDNSSkipTLSVerify: false,
RFC2136BatchChangeSize: 50, PiholeApiVersion: "5",
RFC2136UseTLS: false, PiholePassword: "",
RFC2136LoadBalancingStrategy: "disabled", PiholeServer: "",
RFC2136SkipTLSVerify: false, PiholeTLSInsecureSkipVerify: false,
NS1Endpoint: "", PluralCluster: "",
NS1IgnoreSSL: false, PluralProvider: "",
TransIPAccountName: "", PodSourceDomain: "",
TransIPPrivateKeyFile: "", Policy: "sync",
DigitalOceanAPIPageSize: 50, Provider: "",
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, ProviderCacheTime: 0,
ExcludeDNSRecordTypes: []string{}, PublishHostIP: false,
GoDaddyAPIKey: "", PublishInternal: false,
GoDaddySecretKey: "", RegexDomainExclusion: regexp.MustCompile(""),
GoDaddyTTL: 600, RegexDomainFilter: regexp.MustCompile(""),
GoDaddyOTE: false, Registry: "txt",
IBMCloudProxied: false, RequestTimeout: time.Second * 30,
IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json", RFC2136BatchChangeSize: 50,
TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json", RFC2136GSSTSIG: false,
TencentCloudZoneType: "", RFC2136Host: []string{""},
PiholeServer: "", RFC2136Insecure: false,
PiholePassword: "", RFC2136KerberosPassword: "",
PiholeTLSInsecureSkipVerify: false, RFC2136KerberosRealm: "",
PiholeApiVersion: "5", RFC2136KerberosUsername: "",
PluralCluster: "", RFC2136LoadBalancingStrategy: "disabled",
PluralProvider: "", RFC2136MinTTL: 0,
WebhookProviderURL: "http://localhost:8888", RFC2136Port: 0,
WebhookProviderReadTimeout: 5 * time.Second, RFC2136SkipTLSVerify: false,
WebhookProviderWriteTimeout: 10 * time.Second, RFC2136TAXFR: true,
WebhookServer: false, RFC2136TSIGKeyName: "",
TraefikDisableLegacy: false, RFC2136TSIGSecret: "",
TraefikDisableNew: false, RFC2136TSIGSecretAlg: "",
NAT64Networks: []string{}, RFC2136UseTLS: false,
ExcludeUnschedulable: true, RFC2136Zone: []string{},
ServiceTypeFilter: []string{},
SkipperRouteGroupVersion: "zalando.org/v1",
Sources: nil,
TargetNetFilter: []string{},
TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json",
TencentCloudZoneType: "",
TLSCA: "",
TLSClientCert: "",
TLSClientCertKey: "",
TraefikDisableLegacy: false,
TraefikDisableNew: false,
TransIPAccountName: "",
TransIPPrivateKeyFile: "",
TXTCacheInterval: 0,
TXTEncryptAESKey: "",
TXTEncryptEnabled: false,
TXTNewFormatOnly: false,
TXTOwnerID: "default",
TXTPrefix: "",
TXTSuffix: "",
TXTWildcardReplacement: "",
UpdateEvents: false,
WebhookProviderReadTimeout: 5 * time.Second,
WebhookProviderURL: "http://localhost:8888",
WebhookProviderWriteTimeout: 10 * time.Second,
WebhookServer: false,
ZoneIDFilter: []string{},
} }
// NewConfig returns new Config object // NewConfig returns new Config object
@ -453,40 +454,41 @@ func App(cfg *Config) *kingpin.Application {
app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(defaultConfig.SkipperRouteGroupVersion).StringVar(&cfg.SkipperRouteGroupVersion) app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(defaultConfig.SkipperRouteGroupVersion).StringVar(&cfg.SkipperRouteGroupVersion)
// Flags related to processing source // Flags related to processing source
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, 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, f5-transportserver, traefik-proxy)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "gateway-httproute", "gateway-grpcroute", "gateway-tlsroute", "gateway-tcproute", "gateway-udproute", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress", "f5-virtualserver", "f5-transportserver", "traefik-proxy")
app.Flag("openshift-router-name", "if source is openshift-route then you can pass the ingress controller name. Based on this name external-dns will select the respective router from the route status and map that routerCanonicalHostname to the route host while creating a CNAME record.").StringVar(&cfg.OCPRouterName)
app.Flag("namespace", "Limit resources queried for endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
app.Flag("annotation-filter", "Filter resources queried for endpoints by annotation, using label selector semantics").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter)
app.Flag("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").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
app.Flag("ingress-class", "Require an Ingress to have this class name (defaults to any class; specify multiple times to allow more than one class)").StringsVar(&cfg.IngressClassNames)
app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN.").Default(defaultConfig.FQDNTemplate).StringVar(&cfg.FQDNTemplate)
app.Flag("combine-fqdn-annotation", "Combine FQDN template and Annotations instead of overwriting").BoolVar(&cfg.CombineFQDNAndAnnotation)
app.Flag("ignore-hostname-annotation", "Ignore hostname annotation when generating DNS names, valid only when --fqdn-template is set (default: false)").BoolVar(&cfg.IgnoreHostnameAnnotation)
app.Flag("ignore-non-host-network-pods", "Ignore pods not running on host network when using pod source (default: true)").BoolVar(&cfg.IgnoreNonHostNetworkPods)
app.Flag("ignore-ingress-tls-spec", "Ignore the spec.tls section in Ingress resources (default: false)").BoolVar(&cfg.IgnoreIngressTLSSpec)
app.Flag("gateway-name", "Limit Gateways of Route endpoints to a specific name (default: all names)").StringVar(&cfg.GatewayName)
app.Flag("gateway-namespace", "Limit Gateways of Route endpoints to a specific namespace (default: all namespaces)").StringVar(&cfg.GatewayNamespace)
app.Flag("gateway-label-filter", "Filter Gateways of Route endpoints via label selector (default: all gateways)").StringVar(&cfg.GatewayLabelFilter)
app.Flag("compatibility", "Process annotation semantics from legacy implementations (optional, options: mate, molecule, kops-dns-controller)").Default(defaultConfig.Compatibility).EnumVar(&cfg.Compatibility, "", "mate", "molecule", "kops-dns-controller")
app.Flag("ignore-ingress-rules-spec", "Ignore the spec.rules section in Ingress resources (default: false)").BoolVar(&cfg.IgnoreIngressRulesSpec)
app.Flag("pod-source-domain", "Domain to use for pods records (optional)").Default(defaultConfig.PodSourceDomain).StringVar(&cfg.PodSourceDomain)
app.Flag("publish-internal-services", "Allow external-dns to publish DNS records for ClusterIP services (optional)").BoolVar(&cfg.PublishInternal)
app.Flag("publish-host-ip", "Allow external-dns to publish host-ip for headless services (optional)").BoolVar(&cfg.PublishHostIP)
app.Flag("always-publish-not-ready-addresses", "Always publish also not ready addresses for headless services (optional)").BoolVar(&cfg.AlwaysPublishNotReadyAddresses) app.Flag("always-publish-not-ready-addresses", "Always publish also not ready addresses for headless services (optional)").BoolVar(&cfg.AlwaysPublishNotReadyAddresses)
app.Flag("annotation-filter", "Filter resources queried for endpoints by annotation, using label selector semantics").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter)
app.Flag("combine-fqdn-annotation", "Combine FQDN template and Annotations instead of overwriting").BoolVar(&cfg.CombineFQDNAndAnnotation)
app.Flag("compatibility", "Process annotation semantics from legacy implementations (optional, options: mate, molecule, kops-dns-controller)").Default(defaultConfig.Compatibility).EnumVar(&cfg.Compatibility, "", "mate", "molecule", "kops-dns-controller")
app.Flag("connector-source-server", "The server to connect for connector source, valid only when using connector source").Default(defaultConfig.ConnectorSourceServer).StringVar(&cfg.ConnectorSourceServer) app.Flag("connector-source-server", "The server to connect for connector source, valid only when using connector source").Default(defaultConfig.ConnectorSourceServer).StringVar(&cfg.ConnectorSourceServer)
app.Flag("crd-source-apiversion", "API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source").Default(defaultConfig.CRDSourceAPIVersion).StringVar(&cfg.CRDSourceAPIVersion) app.Flag("crd-source-apiversion", "API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source").Default(defaultConfig.CRDSourceAPIVersion).StringVar(&cfg.CRDSourceAPIVersion)
app.Flag("crd-source-kind", "Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion").Default(defaultConfig.CRDSourceKind).StringVar(&cfg.CRDSourceKind) app.Flag("crd-source-kind", "Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion").Default(defaultConfig.CRDSourceKind).StringVar(&cfg.CRDSourceKind)
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)
app.Flag("managed-record-types", "Record types to manage; specify multiple times to include many; (default: A, AAAA, CNAME) (supported records: A, AAAA, CNAME, NS, SRV, TXT)").Default("A", "AAAA", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes)
app.Flag("exclude-record-types", "Record types to exclude from management; specify multiple times to exclude many; (optional)").Default().StringsVar(&cfg.ExcludeDNSRecordTypes)
app.Flag("default-targets", "Set globally default host/IP that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets) app.Flag("default-targets", "Set globally default host/IP that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional)").StringsVar(&cfg.DefaultTargets)
app.Flag("target-net-filter", "Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.TargetNetFilter) app.Flag("exclude-record-types", "Record types to exclude from management; specify multiple times to exclude many; (optional)").Default().StringsVar(&cfg.ExcludeDNSRecordTypes)
app.Flag("exclude-target-net", "Exclude target nets (optional)").StringsVar(&cfg.ExcludeTargetNets) app.Flag("exclude-target-net", "Exclude target nets (optional)").StringsVar(&cfg.ExcludeTargetNets)
app.Flag("traefik-disable-legacy", "Disable listeners on Resources under the traefik.containo.us API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableLegacy)).BoolVar(&cfg.TraefikDisableLegacy)
app.Flag("traefik-disable-new", "Disable listeners on Resources under the traefik.io API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableNew)).BoolVar(&cfg.TraefikDisableNew)
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)
app.Flag("exclude-unschedulable", "Exclude nodes that are considered unschedulable (default: true)").Default(strconv.FormatBool(defaultConfig.ExcludeUnschedulable)).BoolVar(&cfg.ExcludeUnschedulable) app.Flag("exclude-unschedulable", "Exclude nodes that are considered unschedulable (default: true)").Default(strconv.FormatBool(defaultConfig.ExcludeUnschedulable)).BoolVar(&cfg.ExcludeUnschedulable)
app.Flag("expose-internal-ipv6", "When using the node source, expose internal IPv6 addresses (optional). Default is true.").BoolVar(&cfg.ExposeInternalIPV6) app.Flag("expose-internal-ipv6", "When using the node source, expose internal IPv6 addresses (optional). Default is true.").BoolVar(&cfg.ExposeInternalIPV6)
app.Flag("fqdn-template", "A templated string that's used to generate DNS names from sources that don't define a hostname themselves, or to add a hostname suffix when paired with the fake source (optional). Accepts comma separated list for multiple global FQDN.").Default(defaultConfig.FQDNTemplate).StringVar(&cfg.FQDNTemplate)
app.Flag("gateway-label-filter", "Filter Gateways of Route endpoints via label selector (default: all gateways)").StringVar(&cfg.GatewayLabelFilter)
app.Flag("gateway-name", "Limit Gateways of Route endpoints to a specific name (default: all names)").StringVar(&cfg.GatewayName)
app.Flag("gateway-namespace", "Limit Gateways of Route endpoints to a specific namespace (default: all namespaces)").StringVar(&cfg.GatewayNamespace)
app.Flag("ignore-hostname-annotation", "Ignore hostname annotation when generating DNS names, valid only when --fqdn-template is set (default: false)").BoolVar(&cfg.IgnoreHostnameAnnotation)
app.Flag("ignore-ingress-rules-spec", "Ignore the spec.rules section in Ingress resources (default: false)").BoolVar(&cfg.IgnoreIngressRulesSpec)
app.Flag("ignore-ingress-tls-spec", "Ignore the spec.tls section in Ingress resources (default: false)").BoolVar(&cfg.IgnoreIngressTLSSpec)
app.Flag("ignore-non-host-network-pods", "Ignore pods not running on host network when using pod source (default: true)").BoolVar(&cfg.IgnoreNonHostNetworkPods)
app.Flag("ingress-class", "Require an Ingress to have this class name (defaults to any class; specify multiple times to allow more than one class)").StringsVar(&cfg.IngressClassNames)
app.Flag("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").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
managedRecordTypesHelp := fmt.Sprintf("Record types to manage; specify multiple times to include many; (default: %s) (supported records: A, AAAA, CNAME, NS, SRV, TXT)", strings.Join(defaultConfig.ManagedDNSRecordTypes, ","))
app.Flag("managed-record-types", managedRecordTypesHelp).Default(defaultConfig.ManagedDNSRecordTypes...).StringsVar(&cfg.ManagedDNSRecordTypes)
app.Flag("namespace", "Limit resources queried for endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
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)
app.Flag("openshift-router-name", "if source is openshift-route then you can pass the ingress controller name. Based on this name external-dns will select the respective router from the route status and map that routerCanonicalHostname to the route host while creating a CNAME record.").StringVar(&cfg.OCPRouterName)
app.Flag("pod-source-domain", "Domain to use for pods records (optional)").Default(defaultConfig.PodSourceDomain).StringVar(&cfg.PodSourceDomain)
app.Flag("publish-host-ip", "Allow external-dns to publish host-ip for headless services (optional)").BoolVar(&cfg.PublishHostIP)
app.Flag("publish-internal-services", "Allow external-dns to publish DNS records for ClusterIP services (optional)").BoolVar(&cfg.PublishInternal)
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)
app.Flag("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, f5-transportserver, traefik-proxy)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "gateway-httproute", "gateway-grpcroute", "gateway-tlsroute", "gateway-tcproute", "gateway-udproute", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress", "f5-virtualserver", "f5-transportserver", "traefik-proxy")
app.Flag("target-net-filter", "Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional)").StringsVar(&cfg.TargetNetFilter)
app.Flag("traefik-disable-legacy", "Disable listeners on Resources under the traefik.containo.us API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableLegacy)).BoolVar(&cfg.TraefikDisableLegacy)
app.Flag("traefik-disable-new", "Disable listeners on Resources under the traefik.io API Group").Default(strconv.FormatBool(defaultConfig.TraefikDisableNew)).BoolVar(&cfg.TraefikDisableNew)
// Flags related to providers // Flags related to providers
providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "webhook"} providers := []string{"akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", "civo", "cloudflare", "coredns", "digitalocean", "dnsimple", "exoscale", "gandi", "godaddy", "google", "ibmcloud", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", "plural", "rfc2136", "scaleway", "skydns", "tencentcloud", "transip", "ultradns", "webhook"}
@ -580,7 +582,7 @@ func App(cfg *Config) *kingpin.Application {
// Flags related to RFC2136 provider // Flags related to RFC2136 provider
app.Flag("rfc2136-host", "When using the RFC2136 provider, specify the host of the DNS server (optionally specify multiple times when when using --rfc2136-load-balancing-strategy)").Default(defaultConfig.RFC2136Host[0]).StringsVar(&cfg.RFC2136Host) app.Flag("rfc2136-host", "When using the RFC2136 provider, specify the host of the DNS server (optionally specify multiple times when when using --rfc2136-load-balancing-strategy)").Default(defaultConfig.RFC2136Host[0]).StringsVar(&cfg.RFC2136Host)
app.Flag("rfc2136-port", "When using the RFC2136 provider, specify the port of the DNS server").Default(strconv.Itoa(defaultConfig.RFC2136Port)).IntVar(&cfg.RFC2136Port) app.Flag("rfc2136-port", "When using the RFC2136 provider, specify the port of the DNS server").Default(strconv.Itoa(defaultConfig.RFC2136Port)).IntVar(&cfg.RFC2136Port)
app.Flag("rfc2136-zone", "When using the RFC2136 provider, specify zone entries of the DNS server to use").StringsVar(&cfg.RFC2136Zone) app.Flag("rfc2136-zone", "When using the RFC2136 provider, specify zone entry of the DNS server to use (can be specified multiple times)").StringsVar(&cfg.RFC2136Zone)
app.Flag("rfc2136-create-ptr", "When using the RFC2136 provider, enable PTR management").Default(strconv.FormatBool(defaultConfig.RFC2136CreatePTR)).BoolVar(&cfg.RFC2136CreatePTR) app.Flag("rfc2136-create-ptr", "When using the RFC2136 provider, enable PTR management").Default(strconv.FormatBool(defaultConfig.RFC2136CreatePTR)).BoolVar(&cfg.RFC2136CreatePTR)
app.Flag("rfc2136-insecure", "When using the RFC2136 provider, specify whether to attach TSIG or not (default: false, requires --rfc2136-tsig-keyname and rfc2136-tsig-secret)").Default(strconv.FormatBool(defaultConfig.RFC2136Insecure)).BoolVar(&cfg.RFC2136Insecure) app.Flag("rfc2136-insecure", "When using the RFC2136 provider, specify whether to attach TSIG or not (default: false, requires --rfc2136-tsig-keyname and rfc2136-tsig-secret)").Default(strconv.FormatBool(defaultConfig.RFC2136Insecure)).BoolVar(&cfg.RFC2136Insecure)
app.Flag("rfc2136-tsig-keyname", "When using the RFC2136 provider, specify the TSIG key to attached to DNS messages (required when --rfc2136-insecure=false)").Default(defaultConfig.RFC2136TSIGKeyName).StringVar(&cfg.RFC2136TSIGKeyName) app.Flag("rfc2136-tsig-keyname", "When using the RFC2136 provider, specify the TSIG key to attached to DNS messages (required when --rfc2136-insecure=false)").Default(defaultConfig.RFC2136TSIGKeyName).StringVar(&cfg.RFC2136TSIGKeyName)

View File

@ -84,13 +84,13 @@ func TestMustRegister(t *testing.T) {
} }
func TestUnsupportedMetricWarning(t *testing.T) { func TestUnsupportedMetricWarning(t *testing.T) {
buf := testutils.LogsToBuffer(log.WarnLevel, t) hook := testutils.LogsUnderTestWithLogLevel(log.WarnLevel, t)
registry := NewMetricsRegister() registry := NewMetricsRegister()
mockUnsupported := &MockMetric{FQDN: "unsupported_metric"} mockUnsupported := &MockMetric{FQDN: "unsupported_metric"}
registry.MustRegister(mockUnsupported) registry.MustRegister(mockUnsupported)
assert.NotContains(t, registry.mName, "unsupported_metric") assert.NotContains(t, registry.mName, "unsupported_metric")
assert.Contains(t, buf.String(), "Unsupported metric type: *metrics.MockMetric") testutils.TestHelperLogContains("Unsupported metric type: *metrics.MockMetric", hook, t)
} }
func TestNewMetricsRegister(t *testing.T) { func TestNewMetricsRegister(t *testing.T) {

View File

@ -0,0 +1,147 @@
/*
Copyright 2023 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 tlsutils
import (
"crypto/tls"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/internal/gen/docs/utils"
)
var rsaCertPEM = `-----BEGIN CERTIFICATE-----
MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ
hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa
rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv
zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW
r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
-----END CERTIFICATE-----
`
var rsaKeyPEM = testingKey(`-----BEGIN RSA TESTING KEY-----
MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
-----END RSA TESTING KEY-----
`)
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }
func TestCreateTLSConfig(t *testing.T) {
tests := []struct {
title string
prefix string
caFile string
certFile string
keyFile string
isInsecureStr string
serverName string
assertions func(actual *tls.Config, err error)
}{
{
"Provide only CA returns error",
"prefix",
"",
rsaCertPEM,
"",
"",
"",
func(actual *tls.Config, err error) {
assert.Contains(t, err.Error(), "either both cert and key or none must be provided")
},
},
{
"Invalid cert and key returns error",
"prefix",
"",
"invalid-cert",
"invalid-key",
"",
"",
func(actual *tls.Config, err error) {
assert.Contains(t, err.Error(), "could not load TLS cert")
},
},
{
"Valid cert and key return a valid tls.Config with a certificate",
"prefix",
"",
rsaCertPEM,
rsaKeyPEM,
"",
"server-name",
func(actual *tls.Config, err error) {
assert.Nil(t, err)
assert.Equal(t, actual.ServerName, "server-name")
assert.NotNil(t, actual.Certificates[0])
assert.Equal(t, actual.InsecureSkipVerify, false)
assert.Equal(t, actual.MinVersion, uint16(defaultMinVersion))
},
},
}
for _, tc := range tests {
t.Run(tc.title, func(t *testing.T) {
// setup
dir := t.TempDir()
if tc.caFile != "" {
path := fmt.Sprintf("%s/caFile", dir)
utils.WriteToFile(path, tc.caFile)
t.Setenv(fmt.Sprintf("%s_CA_FILE", tc.prefix), path)
}
if tc.certFile != "" {
path := fmt.Sprintf("%s/certFile", dir)
utils.WriteToFile(path, tc.certFile)
t.Setenv(fmt.Sprintf("%s_CERT_FILE", tc.prefix), path)
}
if tc.keyFile != "" {
path := fmt.Sprintf("%s/keyFile", dir)
utils.WriteToFile(path, tc.keyFile)
t.Setenv(fmt.Sprintf("%s_KEY_FILE", tc.prefix), path)
}
if tc.serverName != "" {
t.Setenv(fmt.Sprintf("%s_TLS_SERVER_NAME", tc.prefix), tc.serverName)
}
if tc.isInsecureStr != "" {
t.Setenv(fmt.Sprintf("%s_INSECURE", tc.prefix), tc.isInsecureStr)
}
// test
actual, err := CreateTLSConfig(tc.prefix)
tc.assertions(actual, err)
})
}
}

View File

@ -25,7 +25,7 @@ import (
) )
// ConflictResolver is used to make a decision in case of two or more different kubernetes resources // ConflictResolver is used to make a decision in case of two or more different kubernetes resources
// are trying to acquire same DNS name // are trying to acquire the same DNS name
type ConflictResolver interface { type ConflictResolver interface {
ResolveCreate(candidates []*endpoint.Endpoint) *endpoint.Endpoint ResolveCreate(candidates []*endpoint.Endpoint) *endpoint.Endpoint
ResolveUpdate(current *endpoint.Endpoint, candidates []*endpoint.Endpoint) *endpoint.Endpoint ResolveUpdate(current *endpoint.Endpoint, candidates []*endpoint.Endpoint) *endpoint.Endpoint
@ -38,13 +38,13 @@ type PerResource struct{}
// ResolveCreate is invoked when dns name is not owned by any resource // ResolveCreate is invoked when dns name is not owned by any resource
// ResolveCreate takes "minimal" (string comparison of Target) endpoint to acquire the DNS record // ResolveCreate takes "minimal" (string comparison of Target) endpoint to acquire the DNS record
func (s PerResource) ResolveCreate(candidates []*endpoint.Endpoint) *endpoint.Endpoint { func (s PerResource) ResolveCreate(candidates []*endpoint.Endpoint) *endpoint.Endpoint {
var min *endpoint.Endpoint var minE *endpoint.Endpoint
for _, ep := range candidates { for _, ep := range candidates {
if min == nil || s.less(ep, min) { if minE == nil || s.less(ep, minE) {
min = ep minE = ep
} }
} }
return min return minE
} }
// ResolveUpdate is invoked when dns name is already owned by "current" endpoint // ResolveUpdate is invoked when dns name is already owned by "current" endpoint

View File

@ -54,13 +54,13 @@ type Plan struct {
// Changes holds lists of actions to be executed by dns providers // Changes holds lists of actions to be executed by dns providers
type Changes struct { type Changes struct {
// Records that need to be created // Records that need to be created
Create []*endpoint.Endpoint Create []*endpoint.Endpoint `json:"create,omitempty"`
// Records that need to be updated (current data) // Records that need to be updated (current data)
UpdateOld []*endpoint.Endpoint UpdateOld []*endpoint.Endpoint `json:"updateOld,omitempty"`
// Records that need to be updated (desired data) // Records that need to be updated (desired data)
UpdateNew []*endpoint.Endpoint UpdateNew []*endpoint.Endpoint `json:"updateNew,omitempty"`
// Records that need to be deleted // Records that need to be deleted
Delete []*endpoint.Endpoint Delete []*endpoint.Endpoint `json:"delete,omitempty"`
} }
// planKey is a key for a row in `planTable`. // planKey is a key for a row in `planTable`.
@ -262,9 +262,11 @@ func (p *Plan) Calculate() *Plan {
} }
plan := &Plan{ plan := &Plan{
Current: p.Current, Current: p.Current,
Desired: p.Desired, Desired: p.Desired,
Changes: changes, Changes: changes,
// The default for ExternalDNS is to always only consider A/AAAA and CNAMEs.
// Everything else is an add on or something to be considered.
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME}, ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
} }

View File

@ -17,6 +17,9 @@ limitations under the License.
package plan package plan
import ( import (
"bytes"
"encoding/json"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -245,6 +248,48 @@ func (suite *PlanTestSuite) SetupTest() {
} }
} }
func TestPlan_ChangesJson_DecodeEncode(t *testing.T) {
ch := &Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "foo",
},
},
UpdateOld: []*endpoint.Endpoint{
{
DNSName: "bar",
},
},
UpdateNew: []*endpoint.Endpoint{
{
DNSName: "baz",
},
},
Delete: []*endpoint.Endpoint{
{
DNSName: "qux",
},
},
}
jsonBytes, err := json.Marshal(ch)
assert.NoError(t, err)
assert.Equal(t,
`{"create":[{"dnsName":"foo"}],"updateOld":[{"dnsName":"bar"}],"updateNew":[{"dnsName":"baz"}],"delete":[{"dnsName":"qux"}]}`,
string(jsonBytes))
var changes Changes
err = json.NewDecoder(bytes.NewBuffer(jsonBytes)).Decode(&changes)
assert.NoError(t, err)
assert.Equal(t, ch, &changes)
}
func TestPlan_ChangesJson_DecodeMixedCase(t *testing.T) {
input := `{"Create":[{"dnsName":"foo"}],"UpdateOld":[{"dnsName":"bar"}],"updateNew":[{"dnsName":"baz"}],"Delete":[{"dnsName":"qux"}]}`
var changes Changes
err := json.NewDecoder(strings.NewReader(input)).Decode(&changes)
assert.NoError(t, err)
assert.Len(t, changes.Create, 1)
}
func (suite *PlanTestSuite) TestSyncFirstRound() { func (suite *PlanTestSuite) TestSyncFirstRound() {
current := []*endpoint.Endpoint{} current := []*endpoint.Endpoint{}
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname, suite.bar127A} desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname, suite.bar127A}
@ -367,9 +412,22 @@ func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificNoChange() {
} }
changes := p.Calculate().Changes changes := p.Calculate().Changes
if changes.HasChanges() { suite.Assert().False(changes.HasChanges())
suite.T().Fatal("test should not have changes") }
func (suite *PlanTestSuite) TestHasChanges() {
current := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificTrue}
desired := []*endpoint.Endpoint{suite.bar127AWithProviderSpecificFalse}
p := &Plan{
Policies: []Policy{&SyncPolicy{}},
Current: current,
Desired: desired,
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
} }
changes := p.Calculate().Changes
suite.Assert().True(changes.HasChanges())
} }
func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificRemoval() { func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificRemoval() {

View File

@ -34,9 +34,9 @@ import (
const ( const (
// Default Record TTL // Default Record TTL
edgeDNSRecordTTL = 600 defaultTTL = 600
maxUint = ^uint(0) maxUint = ^uint(0)
maxInt = int(maxUint >> 1) maxInt = int(maxUint >> 1)
) )
// edgeDNSClient is a proxy interface of the Akamai edgegrid configdns-v2 package that can be stubbed for testing. // edgeDNSClient is a proxy interface of the Akamai edgegrid configdns-v2 package that can be stubbed for testing.
@ -90,14 +90,6 @@ type akamaiZone struct {
func NewAkamaiProvider(akamaiConfig AkamaiConfig, akaService AkamaiDNSService) (provider.Provider, error) { func NewAkamaiProvider(akamaiConfig AkamaiConfig, akaService AkamaiDNSService) (provider.Provider, error) {
var edgeGridConfig edgegrid.Config var edgeGridConfig edgegrid.Config
/*
log.Debugf("Host: %s", akamaiConfig.ServiceConsumerDomain)
log.Debugf("ClientToken: %s", akamaiConfig.ClientToken)
log.Debugf("ClientSecret: %s", akamaiConfig.ClientSecret)
log.Debugf("AccessToken: %s", akamaiConfig.AccessToken)
log.Debugf("EdgePath: %s", akamaiConfig.EdgercPath)
log.Debugf("EdgeSection: %s", akamaiConfig.EdgercSection)
*/
// environment overrides edgerc file but config needs to be complete // environment overrides edgerc file but config needs to be complete
if akamaiConfig.ServiceConsumerDomain == "" || akamaiConfig.ClientToken == "" || akamaiConfig.ClientSecret == "" || akamaiConfig.AccessToken == "" { if akamaiConfig.ServiceConsumerDomain == "" || akamaiConfig.ClientToken == "" || akamaiConfig.ClientSecret == "" || akamaiConfig.AccessToken == "" {
// Kubernetes config incomplete or non existent. Can't mix and match. // Kubernetes config incomplete or non existent. Can't mix and match.
@ -106,7 +98,7 @@ func NewAkamaiProvider(akamaiConfig AkamaiConfig, akaService AkamaiDNSService) (
edgeGridConfig, err = edgegrid.Init(akamaiConfig.EdgercPath, akamaiConfig.EdgercSection) // use default .edgerc location and section edgeGridConfig, err = edgegrid.Init(akamaiConfig.EdgercPath, akamaiConfig.EdgercSection) // use default .edgerc location and section
if err != nil { if err != nil {
log.Errorf("Edgegrid Init Failed") log.Errorf("Edgegrid Init Failed")
return &AkamaiProvider{}, err // return empty provider for backward compatibility return &AkamaiProvider{}, err // return an empty provider for backward compatibility
} }
edgeGridConfig.HeaderToSign = append(edgeGridConfig.HeaderToSign, "X-External-DNS") edgeGridConfig.HeaderToSign = append(edgeGridConfig.HeaderToSign, "X-External-DNS")
} else { } else {
@ -360,7 +352,7 @@ func trimTxtRdata(rdata []string, rtype string) []string {
func ttlAsInt(src endpoint.TTL) int { func ttlAsInt(src endpoint.TTL) int {
var temp interface{} = int64(src) var temp interface{} = int64(src)
temp64 := temp.(int64) temp64 := temp.(int64)
var ttl = edgeDNSRecordTTL var ttl = defaultTTL
if temp64 > 0 && temp64 <= int64(maxInt) { if temp64 > 0 && temp64 <= int64(maxInt) {
ttl = int(temp64) ttl = int(temp64)
} }

View File

@ -38,7 +38,7 @@ import (
) )
const ( const (
defaultAlibabaCloudRecordTTL = 600 defaultTTL = 600
defaultAlibabaCloudPrivateZoneRecordTTL = 60 defaultAlibabaCloudPrivateZoneRecordTTL = 60
defaultAlibabaCloudPageSize = 50 defaultAlibabaCloudPageSize = 50
nullHostAlibabaCloud = "@" nullHostAlibabaCloud = "@"
@ -606,12 +606,12 @@ func (p *AlibabaCloudProvider) deleteRecords(recordMap map[string][]alidns.Recor
func (p *AlibabaCloudProvider) equals(record alidns.Record, endpoint *endpoint.Endpoint) bool { func (p *AlibabaCloudProvider) equals(record alidns.Record, endpoint *endpoint.Endpoint) bool {
ttl1 := record.TTL ttl1 := record.TTL
if ttl1 == defaultAlibabaCloudRecordTTL { if ttl1 == defaultTTL {
ttl1 = 0 ttl1 = 0
} }
ttl2 := int64(endpoint.RecordTTL) ttl2 := int64(endpoint.RecordTTL)
if ttl2 == defaultAlibabaCloudRecordTTL { if ttl2 == defaultTTL {
ttl2 = 0 ttl2 = 0
} }

View File

@ -22,6 +22,7 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns"
"github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz" "github.com/aliyun/alibaba-cloud-sdk-go/services/pvtz"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan" "sigs.k8s.io/external-dns/plan"
@ -223,18 +224,7 @@ func newTestAlibabaCloudProvider(private bool) *AlibabaCloudProvider {
cfg := alibabaCloudConfig{ cfg := alibabaCloudConfig{
VPCID: "vpc-xxxxxx", VPCID: "vpc-xxxxxx",
} }
//
//dnsClient, _ := alidns.NewClientWithAccessKey(
// cfg.RegionID,
// cfg.AccessKeyID,
// cfg.AccessKeySecret,
//)
//
//pvtzClient, _ := pvtz.NewClientWithAccessKey(
// "cn-hangzhou",
// cfg.AccessKeyID,
// cfg.AccessKeySecret,
//)
domainFilterTest := endpoint.NewDomainFilter([]string{"container-service.top.", "example.org"}) domainFilterTest := endpoint.NewDomainFilter([]string{"container-service.top.", "example.org"})
return &AlibabaCloudProvider{ return &AlibabaCloudProvider{
@ -256,8 +246,8 @@ func TestAlibabaCloudPrivateProvider_Records(t *testing.T) {
if len(endpoints) != 2 { if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints)) t.Errorf("Incorrect number of records: %d", len(endpoints))
} }
for _, endpoint := range endpoints { for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *endpoint) t.Logf("Endpoint for %++v", *ep)
} }
} }
} }
@ -271,8 +261,8 @@ func TestAlibabaCloudProvider_Records(t *testing.T) {
if len(endpoints) != 2 { if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints)) t.Errorf("Incorrect number of records: %d", len(endpoints))
} }
for _, endpoint := range endpoints { for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *endpoint) t.Logf("Endpoint for %++v", *ep)
} }
} }
} }
@ -282,7 +272,7 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
defaultTtlPlan := &endpoint.Endpoint{ defaultTtlPlan := &endpoint.Endpoint{
DNSName: "ttl.container-service.top", DNSName: "ttl.container-service.top",
RecordType: "A", RecordType: "A",
RecordTTL: defaultAlibabaCloudRecordTTL, RecordTTL: defaultTTL,
Targets: endpoint.NewTargets("4.3.2.1"), Targets: endpoint.NewTargets("4.3.2.1"),
} }
changes := plan.Changes{ changes := plan.Changes{
@ -313,7 +303,8 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
}, },
} }
ctx := context.Background() ctx := context.Background()
p.ApplyChanges(ctx, &changes) err := p.ApplyChanges(ctx, &changes)
assert.NoError(t, err)
endpoints, err := p.Records(ctx) endpoints, err := p.Records(ctx)
if err != nil { if err != nil {
t.Errorf("Failed to get records: %v", err) t.Errorf("Failed to get records: %v", err)
@ -321,8 +312,8 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
if len(endpoints) != 3 { if len(endpoints) != 3 {
t.Errorf("Incorrect number of records: %d", len(endpoints)) t.Errorf("Incorrect number of records: %d", len(endpoints))
} }
for _, endpoint := range endpoints { for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *endpoint) t.Logf("Endpoint for %++v", *ep)
} }
} }
for _, ep := range endpoints { for _, ep := range endpoints {
@ -343,8 +334,8 @@ func TestAlibabaCloudProvider_Records_PrivateZone(t *testing.T) {
if len(endpoints) != 2 { if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints)) t.Errorf("Incorrect number of records: %d", len(endpoints))
} }
for _, endpoint := range endpoints { for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *endpoint) t.Logf("Endpoint for %++v", *ep)
} }
} }
} }
@ -378,7 +369,8 @@ func TestAlibabaCloudProvider_ApplyChanges_PrivateZone(t *testing.T) {
}, },
} }
ctx := context.Background() ctx := context.Background()
p.ApplyChanges(ctx, &changes) err := p.ApplyChanges(ctx, &changes)
assert.NoError(t, err)
endpoints, err := p.Records(ctx) endpoints, err := p.Records(ctx)
if err != nil { if err != nil {
t.Errorf("Failed to get records: %v", err) t.Errorf("Failed to get records: %v", err)
@ -386,8 +378,8 @@ func TestAlibabaCloudProvider_ApplyChanges_PrivateZone(t *testing.T) {
if len(endpoints) != 2 { if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints)) t.Errorf("Incorrect number of records: %d", len(endpoints))
} }
for _, endpoint := range endpoints { for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *endpoint) t.Logf("Endpoint for %++v", *ep)
} }
} }
} }

View File

@ -39,7 +39,7 @@ import (
const ( const (
defaultAWSProfile = "default" defaultAWSProfile = "default"
recordTTL = 300 defaultTTL = 300
// From the experiments, it seems that the default MaxItems applied is 100, // From the experiments, it seems that the default MaxItems applied is 100,
// and that, on the server side, there is a hard limit of 300 elements per page. // and that, on the server side, there is a hard limit of 300 elements per page.
// After a discussion with AWS representatives, clients should accept // After a discussion with AWS representatives, clients should accept
@ -232,7 +232,7 @@ type profiledZone struct {
} }
func (cs Route53Changes) Route53Changes() []route53types.Change { func (cs Route53Changes) Route53Changes() []route53types.Change {
ret := []route53types.Change{} var ret []route53types.Change
for _, c := range cs { for _, c := range cs {
ret = append(ret, c.Change) ret = append(ret, c.Change)
} }
@ -313,7 +313,7 @@ type AWSConfig struct {
// NewAWSProvider initializes a new AWS Route53 based Provider. // NewAWSProvider initializes a new AWS Route53 based Provider.
func NewAWSProvider(awsConfig AWSConfig, clients map[string]Route53API) (*AWSProvider, error) { func NewAWSProvider(awsConfig AWSConfig, clients map[string]Route53API) (*AWSProvider, error) {
provider := &AWSProvider{ pr := &AWSProvider{
clients: clients, clients: clients,
domainFilter: awsConfig.DomainFilter, domainFilter: awsConfig.DomainFilter,
zoneIDFilter: awsConfig.ZoneIDFilter, zoneIDFilter: awsConfig.ZoneIDFilter,
@ -331,7 +331,7 @@ func NewAWSProvider(awsConfig AWSConfig, clients map[string]Route53API) (*AWSPro
failedChangesQueue: make(map[string]Route53Changes), failedChangesQueue: make(map[string]Route53Changes),
} }
return provider, nil return pr, nil
} }
// Zones returns the list of hosted zones. // Zones returns the list of hosted zones.
@ -510,7 +510,7 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*profiledZon
if r.AliasTarget != nil { if r.AliasTarget != nil {
// Alias records don't have TTLs so provide the default to match the TXT generation // Alias records don't have TTLs so provide the default to match the TXT generation
if ttl == 0 { if ttl == 0 {
ttl = recordTTL ttl = defaultTTL
} }
ep := endpoint. ep := endpoint.
NewEndpointWithTTL(name, string(r.Type), ttl, *r.AliasTarget.DNSName). NewEndpointWithTTL(name, string(r.Type), ttl, *r.AliasTarget.DNSName).
@ -561,33 +561,33 @@ func (p *AWSProvider) records(ctx context.Context, zones map[string]*profiledZon
} }
// Identify if old and new endpoints require DELETE/CREATE instead of UPDATE. // Identify if old and new endpoints require DELETE/CREATE instead of UPDATE.
func (p *AWSProvider) requiresDeleteCreate(old *endpoint.Endpoint, new *endpoint.Endpoint) bool { func (p *AWSProvider) requiresDeleteCreate(old *endpoint.Endpoint, newE *endpoint.Endpoint) bool {
// a change of record type // a change of a record type
if old.RecordType != new.RecordType { if old.RecordType != newE.RecordType {
return true return true
} }
// an ALIAS record change to/from an A // an ALIAS record change to/from an A
if old.RecordType == endpoint.RecordTypeA { if old.RecordType == endpoint.RecordTypeA {
oldAlias, _ := old.GetProviderSpecificProperty(providerSpecificAlias) oldAlias, _ := old.GetProviderSpecificProperty(providerSpecificAlias)
newAlias, _ := new.GetProviderSpecificProperty(providerSpecificAlias) newAlias, _ := newE.GetProviderSpecificProperty(providerSpecificAlias)
if oldAlias != newAlias { if oldAlias != newAlias {
return true return true
} }
} }
// a set identifier change // a set identifier change
if old.SetIdentifier != new.SetIdentifier { if old.SetIdentifier != newE.SetIdentifier {
return true return true
} }
// a change of routing policy // a change of routing policy
// default to true for geolocation properties if any geolocation property exists in old/new but not the other // defaults to true for geolocation properties if any geolocation property exists in old/new but not the other
for _, propType := range [7]string{providerSpecificWeight, providerSpecificRegion, providerSpecificFailover, for _, propType := range [7]string{providerSpecificWeight, providerSpecificRegion, providerSpecificFailover,
providerSpecificFailover, providerSpecificGeolocationContinentCode, providerSpecificGeolocationCountryCode, providerSpecificFailover, providerSpecificGeolocationContinentCode, providerSpecificGeolocationCountryCode,
providerSpecificGeolocationSubdivisionCode} { providerSpecificGeolocationSubdivisionCode} {
_, oldPolicy := old.GetProviderSpecificProperty(propType) _, oldPolicy := old.GetProviderSpecificProperty(propType)
_, newPolicy := new.GetProviderSpecificProperty(propType) _, newPolicy := newE.GetProviderSpecificProperty(propType)
if oldPolicy != newPolicy { if oldPolicy != newPolicy {
return true return true
} }
@ -601,14 +601,14 @@ func (p *AWSProvider) createUpdateChanges(newEndpoints, oldEndpoints []*endpoint
var creates []*endpoint.Endpoint var creates []*endpoint.Endpoint
var updates []*endpoint.Endpoint var updates []*endpoint.Endpoint
for i, new := range newEndpoints { for i, newE := range newEndpoints {
old := oldEndpoints[i] oldE := oldEndpoints[i]
if p.requiresDeleteCreate(old, new) { if p.requiresDeleteCreate(oldE, newE) {
deletes = append(deletes, old) deletes = append(deletes, oldE)
creates = append(creates, new) creates = append(creates, newE)
} else { } else {
// Safe to perform an UPSERT. // Safe to perform an UPSERT.
updates = append(updates, new) updates = append(updates, newE)
} }
} }
@ -760,8 +760,8 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes Route53Changes,
func (p *AWSProvider) newChanges(action route53types.ChangeAction, endpoints []*endpoint.Endpoint) Route53Changes { func (p *AWSProvider) newChanges(action route53types.ChangeAction, endpoints []*endpoint.Endpoint) Route53Changes {
changes := make(Route53Changes, 0, len(endpoints)) changes := make(Route53Changes, 0, len(endpoints))
for _, endpoint := range endpoints { for _, ep := range endpoints {
change := p.newChange(action, endpoint) change := p.newChange(action, ep)
changes = append(changes, change) changes = append(changes, change)
} }
@ -804,8 +804,8 @@ func (p *AWSProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoi
if alias { if alias {
if ep.RecordTTL.IsConfigured() { if ep.RecordTTL.IsConfigured() {
log.Debugf("Modifying endpoint: %v, setting ttl=%v", ep, recordTTL) log.Debugf("Modifying endpoint: %v, setting ttl=%v", ep, defaultTTL)
ep.RecordTTL = recordTTL ep.RecordTTL = defaultTTL
} }
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificEvaluateTargetHealth); ok { if prop, ok := ep.GetProviderSpecificProperty(providerSpecificEvaluateTargetHealth); ok {
if prop != "true" && prop != "false" { if prop != "true" && prop != "false" {
@ -866,7 +866,7 @@ func (p *AWSProvider) newChange(action route53types.ChangeAction, ep *endpoint.E
change.sizeValues += 1 change.sizeValues += 1
} else { } else {
if !ep.RecordTTL.IsConfigured() { if !ep.RecordTTL.IsConfigured() {
change.ResourceRecordSet.TTL = aws.Int64(recordTTL) change.ResourceRecordSet.TTL = aws.Int64(defaultTTL)
} else { } else {
change.ResourceRecordSet.TTL = aws.Int64(int64(ep.RecordTTL)) change.ResourceRecordSet.TTL = aws.Int64(int64(ep.RecordTTL))
} }

View File

@ -101,7 +101,8 @@ func TestAWSZonesSecondRequestHitsTheCache(t *testing.T) {
ctx := context.Background() ctx := context.Background()
_, err := provider.Zones(ctx) _, err := provider.Zones(ctx)
assert.NoError(t, err) assert.NoError(t, err)
b := testutils.LogsToBuffer(log.DebugLevel, t) hook := testutils.LogsUnderTestWithLogLevel(log.DebugLevel, t)
_, _ = provider.Zones(ctx) _, _ = provider.Zones(ctx)
assert.Contains(t, b.String(), "level=debug msg=\"Using cached zones list\"")
testutils.TestHelperLogContainsWithLogLevel("Using cached zones list", log.DebugLevel, hook, t)
} }

View File

@ -400,37 +400,37 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("list-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("list-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
}, },
{ {
Name: aws.String("list-test.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("list-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
}, },
{ {
Name: aws.String(wildcardEscape("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do.")), Name: aws.String(wildcardEscape("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
}, },
{ {
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do.")), Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("example")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("example")}},
}, },
{ {
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes-a.zone-2.ext-dns-test-2.teapot.zalan.do.")), Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes-a.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
}, },
{ {
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do.")), Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
AliasTarget: &route53types.AliasTarget{ AliasTarget: &route53types.AliasTarget{
DNSName: aws.String("escape-codes.eu-central-1.elb.amazonaws.com."), DNSName: aws.String("escape-codes.eu-central-1.elb.amazonaws.com."),
EvaluateTargetHealth: false, EvaluateTargetHealth: false,
@ -440,7 +440,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do.")), Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
AliasTarget: &route53types.AliasTarget{ AliasTarget: &route53types.AliasTarget{
DNSName: aws.String("escape-codes.eu-central-1.elb.amazonaws.com."), DNSName: aws.String("escape-codes.eu-central-1.elb.amazonaws.com."),
EvaluateTargetHealth: false, EvaluateTargetHealth: false,
@ -504,19 +504,19 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("list-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("list-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeTxt, Type: route53types.RRTypeTxt,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("random")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("random")}},
}, },
{ {
Name: aws.String("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set-1"), SetIdentifier: aws.String("test-set-1"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -524,7 +524,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}},
SetIdentifier: aws.String("test-set-2"), SetIdentifier: aws.String("test-set-2"),
Weight: aws.Int64(20), Weight: aws.Int64(20),
@ -532,7 +532,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("latency-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("latency-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set"), SetIdentifier: aws.String("test-set"),
Region: route53types.ResourceRecordSetRegionUsEast1, Region: route53types.ResourceRecordSetRegionUsEast1,
@ -540,7 +540,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("failover-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("failover-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set"), SetIdentifier: aws.String("test-set"),
Failover: route53types.ResourceRecordSetFailoverPrimary, Failover: route53types.ResourceRecordSetFailoverPrimary,
@ -548,7 +548,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set"), SetIdentifier: aws.String("test-set"),
MultiValueAnswer: aws.Bool(true), MultiValueAnswer: aws.Bool(true),
@ -556,7 +556,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set-1"), SetIdentifier: aws.String("test-set-1"),
GeoLocation: &route53types.GeoLocation{ GeoLocation: &route53types.GeoLocation{
@ -566,7 +566,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}},
SetIdentifier: aws.String("test-set-2"), SetIdentifier: aws.String("test-set-2"),
GeoLocation: &route53types.GeoLocation{ GeoLocation: &route53types.GeoLocation{
@ -576,7 +576,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set-1"), SetIdentifier: aws.String("test-set-1"),
GeoLocation: &route53types.GeoLocation{ GeoLocation: &route53types.GeoLocation{
@ -586,7 +586,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("foo.example.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("foo.example.com")}},
SetIdentifier: aws.String("test-set-1"), SetIdentifier: aws.String("test-set-1"),
HealthCheckId: aws.String("foo-bar-healthcheck-id"), HealthCheckId: aws.String("foo-bar-healthcheck-id"),
@ -595,7 +595,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}},
SetIdentifier: aws.String("test-set-2"), SetIdentifier: aws.String("test-set-2"),
HealthCheckId: aws.String("abc-def-healthcheck-id"), HealthCheckId: aws.String("abc-def-healthcheck-id"),
@ -604,7 +604,7 @@ func TestAWSRecords(t *testing.T) {
{ {
Name: aws.String("mail.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("mail.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx, Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("10 mailhost1.example.com")}, {Value: aws.String("20 mailhost2.example.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("10 mailhost1.example.com")}, {Value: aws.String("20 mailhost2.example.com")}},
}, },
}) })
@ -613,32 +613,32 @@ func TestAWSRecords(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
validateEndpoints(t, provider, records, []*endpoint.Endpoint{ validateEndpoints(t, provider, records, []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), endpoint.NewEndpointWithTTL("list-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.2.3.4"),
endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), endpoint.NewEndpointWithTTL("list-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8"),
endpoint.NewEndpointWithTTL("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8"), endpoint.NewEndpointWithTTL("*.wildcard-test.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8"),
endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "example").WithProviderSpecific(providerSpecificAlias, "false"), endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(defaultTTL), "example").WithProviderSpecific(providerSpecificAlias, "false"),
endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes-a.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4"), endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes-a.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.2.3.4"),
endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "escape-codes.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "escape-codes.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "escape-codes.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("escape-%!s(<nil>)-codes-alias.zone-2.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, endpoint.TTL(defaultTTL), "escape-codes.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("list-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, endpoint.TTL(defaultTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("*.wildcard-test-alias.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, endpoint.TTL(defaultTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "false").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, endpoint.TTL(recordTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"), endpoint.NewEndpointWithTTL("list-test-alias-evaluate.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeAAAA, endpoint.TTL(defaultTTL), "foo.eu-central-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificEvaluateTargetHealth, "true").WithProviderSpecific(providerSpecificAlias, "true"),
endpoint.NewEndpointWithTTL("list-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8", "8.8.4.4"), endpoint.NewEndpointWithTTL("list-test-multiple.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8", "8.8.4.4"),
endpoint.NewEndpointWithTTL("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "random"), endpoint.NewEndpointWithTTL("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeTXT, endpoint.TTL(defaultTTL), "random"),
endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10"), endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10"),
endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20"), endpoint.NewEndpointWithTTL("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20"),
endpoint.NewEndpointWithTTL("latency-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificRegion, "us-east-1"), endpoint.NewEndpointWithTTL("latency-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificRegion, "us-east-1"),
endpoint.NewEndpointWithTTL("failover-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificFailover, "PRIMARY"), endpoint.NewEndpointWithTTL("failover-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificFailover, "PRIMARY"),
endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""), endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""),
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"), endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"),
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"), endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"),
endpoint.NewEndpointWithTTL("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, "NY"), endpoint.NewEndpointWithTTL("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, "NY"),
endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id").WithProviderSpecific(providerSpecificAlias, "false"), endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(defaultTTL), "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id").WithProviderSpecific(providerSpecificAlias, "false"),
endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"), endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"),
endpoint.NewEndpointWithTTL("mail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(recordTTL), "10 mailhost1.example.com", "20 mailhost2.example.com"), endpoint.NewEndpointWithTTL("mail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeMX, endpoint.TTL(defaultTTL), "10 mailhost1.example.com", "20 mailhost2.example.com"),
}) })
} }
@ -647,7 +647,7 @@ func TestAWSRecordsSoftError(t *testing.T) {
{ {
Name: aws.String("list-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("list-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
}, },
}) })
@ -710,55 +710,55 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
}, },
{ {
Name: aws.String("update-test-aaaa.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-aaaa.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}},
}, },
{ {
Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
}, },
{ {
Name: aws.String("delete-test-aaaa.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-aaaa.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}},
}, },
{ {
Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("update-test-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}},
}, },
{ {
Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("delete-test-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}},
}, },
{ {
Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.1.1.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.1.1.1")}},
}, },
{ {
@ -782,7 +782,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}},
}, },
{ {
@ -806,7 +806,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}},
}, },
{ {
@ -830,25 +830,25 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}},
}, },
{ {
Name: aws.String("delete-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}, {Value: aws.String("2606:4700:4700::1001")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}, {Value: aws.String("2606:4700:4700::1001")}},
}, },
{ {
Name: aws.String("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("weighted-to-simple"), SetIdentifier: aws.String("weighted-to-simple"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -856,13 +856,13 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("simple-to-weighted.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("simple-to-weighted.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
}, },
{ {
Name: aws.String("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("policy-change"), SetIdentifier: aws.String("policy-change"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -870,7 +870,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("before"), SetIdentifier: aws.String("before"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -878,7 +878,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"), SetIdentifier: aws.String("no-change"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -886,19 +886,19 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx, Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("10 mailhost2.bar.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("10 mailhost2.bar.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx, Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("30 mailhost1.foo.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("30 mailhost1.foo.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do.")), Name: aws.String(specialCharactersEscape("escape-%!s(<nil>)-codes.zone-2.ext-dns-test-2.teapot.zalan.do.")),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"), SetIdentifier: aws.String("no-change"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -993,25 +993,25 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("create-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
}, },
{ {
Name: aws.String("create-test-aaaa.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test-aaaa.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}},
}, },
{ {
Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
}, },
{ {
Name: aws.String("update-test-aaaa.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-aaaa.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}},
}, },
{ {
@ -1035,25 +1035,25 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("update-test-alias-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-alias-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("my-internal-host.example.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("my-internal-host.example.com")}},
}, },
{ {
Name: aws.String("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("foo.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("foo.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("baz.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("baz.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("foo.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("foo.elb.amazonaws.com")}},
}, },
{ {
@ -1077,13 +1077,13 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
}, },
{ {
Name: aws.String("simple-to-weighted.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("simple-to-weighted.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("simple-to-weighted"), SetIdentifier: aws.String("simple-to-weighted"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -1091,7 +1091,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("policy-change"), SetIdentifier: aws.String("policy-change"),
Region: route53types.ResourceRecordSetRegionUsEast1, Region: route53types.ResourceRecordSetRegionUsEast1,
@ -1099,7 +1099,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("set-identifier-change.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("after"), SetIdentifier: aws.String("after"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -1107,7 +1107,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("set-identifier-no-change.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"), SetIdentifier: aws.String("no-change"),
Weight: aws.Int64(20), Weight: aws.Int64(20),
@ -1115,7 +1115,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx, Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("10 mailhost1.foo.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("10 mailhost1.foo.elb.amazonaws.com")}},
}, },
}) })
@ -1123,7 +1123,7 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("escape-\\045\\041s\\050\\074nil\\076\\051-codes.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("escape-\\045\\041s\\050\\074nil\\076\\051-codes.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"), SetIdentifier: aws.String("no-change"),
Weight: aws.Int64(10), Weight: aws.Int64(10),
@ -1131,55 +1131,55 @@ func TestAWSApplyChanges(t *testing.T) {
{ {
Name: aws.String("create-test.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("create-test-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}},
}, },
{ {
Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}},
}, },
{ {
Name: aws.String("update-test-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}},
}, },
{ {
Name: aws.String("create-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("create-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("create-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}, {Value: aws.String("2606:4700:4700::1001")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}, {Value: aws.String("2606:4700:4700::1001")}},
}, },
{ {
Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}},
}, },
{ {
Name: aws.String("update-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeAaaa, Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}, {Value: aws.String("2606:4700:4700::1111")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}, {Value: aws.String("2606:4700:4700::1111")}},
}, },
{ {
Name: aws.String("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx, Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("20 mailhost3.foo.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("20 mailhost3.foo.elb.amazonaws.com")}},
}, },
}) })
@ -1191,79 +1191,79 @@ func TestAWSApplyChangesDryRun(t *testing.T) {
{ {
Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
}, },
{ {
Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
}, },
{ {
Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-a-to-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.1.1.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.1.1.1")}},
}, },
{ {
Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-cname.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("bar.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-cname-alias.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeCname, Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("qux.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}, {Value: aws.String("8.8.4.4")}},
}, },
{ {
Name: aws.String("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA, Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}, {Value: aws.String("4.3.2.1")}},
}, },
{ {
Name: aws.String("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx, Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("20 mail.foo.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("20 mail.foo.elb.amazonaws.com")}},
}, },
{ {
Name: aws.String("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."), Name: aws.String("delete-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx, Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL), TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("10 mail.bar.elb.amazonaws.com")}}, ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("10 mail.bar.elb.amazonaws.com")}},
}, },
} }
@ -1464,7 +1464,7 @@ func TestAWSsubmitChanges(t *testing.T) {
for j := 1; j < (hosts + 1); j++ { for j := 1; j < (hosts + 1); j++ {
hostname := fmt.Sprintf("subnet%dhost%d.zone-1.ext-dns-test-2.teapot.zalan.do", i, j) hostname := fmt.Sprintf("subnet%dhost%d.zone-1.ext-dns-test-2.teapot.zalan.do", i, j)
ip := fmt.Sprintf("1.1.%d.%d", i, j) ip := fmt.Sprintf("1.1.%d.%d", i, j)
ep := endpoint.NewEndpointWithTTL(hostname, endpoint.RecordTypeA, endpoint.TTL(recordTTL), ip) ep := endpoint.NewEndpointWithTTL(hostname, endpoint.RecordTypeA, endpoint.TTL(defaultTTL), ip)
endpoints = append(endpoints, ep) endpoints = append(endpoints, ep)
} }
} }
@ -1491,7 +1491,7 @@ func TestAWSsubmitChangesError(t *testing.T) {
zones, err := provider.zones(ctx) zones, err := provider.zones(ctx)
require.NoError(t, err) require.NoError(t, err)
ep := endpoint.NewEndpointWithTTL("fail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.0.0.1") ep := endpoint.NewEndpointWithTTL("fail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.0.0.1")
cs := provider.newChanges(route53types.ChangeActionCreate, []*endpoint.Endpoint{ep}) cs := provider.newChanges(route53types.ChangeActionCreate, []*endpoint.Endpoint{ep})
require.Error(t, provider.submitChanges(ctx, cs, zones)) require.Error(t, provider.submitChanges(ctx, cs, zones))
@ -1504,11 +1504,11 @@ func TestAWSsubmitChangesRetryOnError(t *testing.T) {
zones, err := provider.zones(ctx) zones, err := provider.zones(ctx)
require.NoError(t, err) require.NoError(t, err)
ep1 := endpoint.NewEndpointWithTTL("success.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.0.0.1") ep1 := endpoint.NewEndpointWithTTL("success.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.0.0.1")
ep2 := endpoint.NewEndpointWithTTL("fail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.0.0.2") ep2 := endpoint.NewEndpointWithTTL("fail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.0.0.2")
ep3 := endpoint.NewEndpointWithTTL("success2.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.0.0.3") ep3 := endpoint.NewEndpointWithTTL("success2.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.0.0.3")
ep2txt := endpoint.NewEndpointWithTTL("fail__edns_housekeeping.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "something") // "__edns_housekeeping" is the TXT suffix ep2txt := endpoint.NewEndpointWithTTL("fail__edns_housekeeping.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeTXT, endpoint.TTL(defaultTTL), "something") // "__edns_housekeeping" is the TXT suffix
ep2txt.Labels = map[string]string{ ep2txt.Labels = map[string]string{
endpoint.OwnedRecordLabelKey: "fail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.OwnedRecordLabelKey: "fail.zone-1.ext-dns-test-2.teapot.zalan.do",
} }
@ -2315,26 +2315,26 @@ func containsRecordWithDNSName(records []*endpoint.Endpoint, dnsName string) boo
func TestRequiresDeleteCreate(t *testing.T) { func TestRequiresDeleteCreate(t *testing.T) {
provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"foo.bar."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil) provider, _ := newAWSProvider(t, endpoint.NewDomainFilter([]string{"foo.bar."}), provider.NewZoneIDFilter([]string{}), provider.NewZoneTypeFilter(""), defaultEvaluateTargetHealth, false, nil)
oldRecordType := endpoint.NewEndpointWithTTL("recordType", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8") oldRecordType := endpoint.NewEndpointWithTTL("recordType", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8")
newRecordType := endpoint.NewEndpointWithTTL("recordType", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar").WithProviderSpecific(providerSpecificAlias, "false") newRecordType := endpoint.NewEndpointWithTTL("recordType", endpoint.RecordTypeCNAME, endpoint.TTL(defaultTTL), "bar").WithProviderSpecific(providerSpecificAlias, "false")
assert.False(t, provider.requiresDeleteCreate(oldRecordType, oldRecordType), "actual and expected endpoints don't match. %+v:%+v", oldRecordType, oldRecordType) assert.False(t, provider.requiresDeleteCreate(oldRecordType, oldRecordType), "actual and expected endpoints don't match. %+v:%+v", oldRecordType, oldRecordType)
assert.True(t, provider.requiresDeleteCreate(oldRecordType, newRecordType), "actual and expected endpoints don't match. %+v:%+v", oldRecordType, newRecordType) assert.True(t, provider.requiresDeleteCreate(oldRecordType, newRecordType), "actual and expected endpoints don't match. %+v:%+v", oldRecordType, newRecordType)
oldAtoAlias := endpoint.NewEndpointWithTTL("AtoAlias", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.1.1.1") oldAtoAlias := endpoint.NewEndpointWithTTL("AtoAlias", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.1.1.1")
newAtoAlias := endpoint.NewEndpointWithTTL("AtoAlias", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "bar.us-east-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificAlias, "true") newAtoAlias := endpoint.NewEndpointWithTTL("AtoAlias", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "bar.us-east-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificAlias, "true")
assert.False(t, provider.requiresDeleteCreate(oldAtoAlias, oldAtoAlias), "actual and expected endpoints don't match. %+v:%+v", oldAtoAlias, oldAtoAlias.DNSName) assert.False(t, provider.requiresDeleteCreate(oldAtoAlias, oldAtoAlias), "actual and expected endpoints don't match. %+v:%+v", oldAtoAlias, oldAtoAlias.DNSName)
assert.True(t, provider.requiresDeleteCreate(oldAtoAlias, newAtoAlias), "actual and expected endpoints don't match. %+v:%+v", oldAtoAlias, newAtoAlias) assert.True(t, provider.requiresDeleteCreate(oldAtoAlias, newAtoAlias), "actual and expected endpoints don't match. %+v:%+v", oldAtoAlias, newAtoAlias)
oldPolicy := endpoint.NewEndpointWithTTL("policy", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8").WithSetIdentifier("nochange").WithProviderSpecific(providerSpecificRegion, "us-east-1") oldPolicy := endpoint.NewEndpointWithTTL("policy", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8").WithSetIdentifier("nochange").WithProviderSpecific(providerSpecificRegion, "us-east-1")
newPolicy := endpoint.NewEndpointWithTTL("policy", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8").WithSetIdentifier("nochange").WithProviderSpecific(providerSpecificWeight, "10") newPolicy := endpoint.NewEndpointWithTTL("policy", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8").WithSetIdentifier("nochange").WithProviderSpecific(providerSpecificWeight, "10")
assert.False(t, provider.requiresDeleteCreate(oldPolicy, oldPolicy), "actual and expected endpoints don't match. %+v:%+v", oldPolicy, oldPolicy) assert.False(t, provider.requiresDeleteCreate(oldPolicy, oldPolicy), "actual and expected endpoints don't match. %+v:%+v", oldPolicy, oldPolicy)
assert.True(t, provider.requiresDeleteCreate(oldPolicy, newPolicy), "actual and expected endpoints don't match. %+v:%+v", oldPolicy, newPolicy) assert.True(t, provider.requiresDeleteCreate(oldPolicy, newPolicy), "actual and expected endpoints don't match. %+v:%+v", oldPolicy, newPolicy)
oldSetIdentifier := endpoint.NewEndpointWithTTL("setIdentifier", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8").WithSetIdentifier("old") oldSetIdentifier := endpoint.NewEndpointWithTTL("setIdentifier", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8").WithSetIdentifier("old")
newSetIdentifier := endpoint.NewEndpointWithTTL("setIdentifier", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8").WithSetIdentifier("new") newSetIdentifier := endpoint.NewEndpointWithTTL("setIdentifier", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8").WithSetIdentifier("new")
assert.False(t, provider.requiresDeleteCreate(oldSetIdentifier, oldSetIdentifier), "actual and expected endpoints don't match. %+v:%+v", oldSetIdentifier, oldSetIdentifier) assert.False(t, provider.requiresDeleteCreate(oldSetIdentifier, oldSetIdentifier), "actual and expected endpoints don't match. %+v:%+v", oldSetIdentifier, oldSetIdentifier)
assert.True(t, provider.requiresDeleteCreate(oldSetIdentifier, newSetIdentifier), "actual and expected endpoints don't match. %+v:%+v", oldSetIdentifier, newSetIdentifier) assert.True(t, provider.requiresDeleteCreate(oldSetIdentifier, newSetIdentifier), "actual and expected endpoints don't match. %+v:%+v", oldSetIdentifier, newSetIdentifier)

View File

@ -24,7 +24,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/route53" "github.com/aws/aws-sdk-go-v2/service/route53"
route53types "github.com/aws/aws-sdk-go-v2/service/route53/types" route53types "github.com/aws/aws-sdk-go-v2/service/route53/types"
yaml "github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/provider" "sigs.k8s.io/external-dns/provider"

View File

@ -35,7 +35,7 @@ import (
) )
const ( const (
sdDefaultRecordTTL = 300 defaultTTL = 300
sdNamespaceTypePublic = "public" sdNamespaceTypePublic = "public"
sdNamespaceTypePrivate = "private" sdNamespaceTypePrivate = "private"
@ -407,7 +407,7 @@ func (p *AWSSDProvider) CreateService(ctx context.Context, namespaceID *string,
srvType := p.serviceTypeFromEndpoint(ep) srvType := p.serviceTypeFromEndpoint(ep)
routingPolicy := p.routingPolicyFromEndpoint(ep) routingPolicy := p.routingPolicyFromEndpoint(ep)
ttl := int64(sdDefaultRecordTTL) ttl := int64(defaultTTL)
if ep.RecordTTL.IsConfigured() { if ep.RecordTTL.IsConfigured() {
ttl = int64(ep.RecordTTL) ttl = int64(ep.RecordTTL)
} }
@ -443,7 +443,7 @@ func (p *AWSSDProvider) UpdateService(ctx context.Context, service *sdtypes.Serv
srvType := p.serviceTypeFromEndpoint(ep) srvType := p.serviceTypeFromEndpoint(ep)
ttl := int64(sdDefaultRecordTTL) ttl := int64(defaultTTL)
if ep.RecordTTL.IsConfigured() { if ep.RecordTTL.IsConfigured() {
ttl = int64(ep.RecordTTL) ttl = int64(ep.RecordTTL)
} }

View File

@ -35,7 +35,7 @@ import (
) )
const ( const (
azureRecordTTL = 300 defaultTTL = 300
) )
// ZonesClient is an interface of dns.ZoneClient that can be stubbed for testing. // ZonesClient is an interface of dns.ZoneClient that can be stubbed for testing.
@ -337,7 +337,7 @@ func (p *AzureProvider) recordSetNameForZone(zone string, endpoint *endpoint.End
} }
func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet, error) { func (p *AzureProvider) newRecordSet(endpoint *endpoint.Endpoint) (dns.RecordSet, error) {
var ttl int64 = azureRecordTTL var ttl int64 = defaultTTL
if endpoint.RecordTTL.IsConfigured() { if endpoint.RecordTTL.IsConfigured() {
ttl = int64(endpoint.RecordTTL) ttl = int64(endpoint.RecordTTL)
} }

View File

@ -344,7 +344,7 @@ func (p *AzurePrivateDNSProvider) recordSetNameForZone(zone string, endpoint *en
} }
func (p *AzurePrivateDNSProvider) newRecordSet(endpoint *endpoint.Endpoint) (privatedns.RecordSet, error) { func (p *AzurePrivateDNSProvider) newRecordSet(endpoint *endpoint.Endpoint) (privatedns.RecordSet, error) {
var ttl int64 = azureRecordTTL var ttl int64 = defaultTTL
if endpoint.RecordTTL.IsConfigured() { if endpoint.RecordTTL.IsConfigured() {
ttl = int64(endpoint.RecordTTL) ttl = int64(endpoint.RecordTTL)
} }

View File

@ -50,7 +50,7 @@ func TestGetCloudConfiguration(t *testing.T) {
func TestOverrideConfiguration(t *testing.T) { func TestOverrideConfiguration(t *testing.T) {
_, filename, _, _ := runtime.Caller(0) _, filename, _, _ := runtime.Caller(0)
configFile := path.Join(path.Dir(filename), "config_test.json") configFile := path.Join(path.Dir(filename), "fixtures/config_test.json")
cfg, err := getConfig(configFile, "subscription-override", "rg-override", "", "aad-endpoint-override") cfg, err := getConfig(configFile, "subscription-override", "rg-override", "", "aad-endpoint-override")
if err != nil { if err != nil {
t.Errorf("got unexpected err %v", err) t.Errorf("got unexpected err %v", err)

View File

@ -1,7 +1,7 @@
{ {
"tenantId": "tenant", "tenantId": "tenant",
"subscriptionId": "subscription", "subscriptionId": "subscription",
"resourceGroup": "rg", "resourceGroup": "rg",
"aadClientId": "clientId", "aadClientId": "clientId",
"aadClientSecret": "clientSecret" "aadClientSecret": "clientSecret"
} }

View File

@ -44,20 +44,20 @@ const (
cloudFlareDelete = "DELETE" cloudFlareDelete = "DELETE"
// cloudFlareUpdate is a ChangeAction enum value // cloudFlareUpdate is a ChangeAction enum value
cloudFlareUpdate = "UPDATE" cloudFlareUpdate = "UPDATE"
// defaultCloudFlareRecordTTL 1 = automatic // defaultTTL 1 = automatic
defaultCloudFlareRecordTTL = 1 defaultTTL = 1
) )
// We have to use pointers to bools now, as the upstream cloudflare-go library requires them // We have to use pointers to bools now, as the upstream cloudflare-go library requires them
// see: https://github.com/cloudflare/cloudflare-go/pull/595 // see: https://github.com/cloudflare/cloudflare-go/pull/595
// proxyEnabled is a pointer to a bool true showing the record should be proxied through cloudflare var (
var proxyEnabled *bool = boolPtr(true) // proxyEnabled is a pointer to a bool true showing the record should be proxied through cloudflare
proxyEnabled *bool = boolPtr(true)
// proxyDisabled is a pointer to a bool false showing the record should not be proxied through cloudflare
proxyDisabled *bool = boolPtr(false)
)
// proxyDisabled is a pointer to a bool false showing the record should not be proxied through cloudflare
var proxyDisabled *bool = boolPtr(false)
// for faster getRecordID() lookup
type DNSRecordIndex struct { type DNSRecordIndex struct {
Name string Name string
Type string Type string
@ -279,8 +279,8 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to initialize cloudflare provider: %w", err) return nil, fmt.Errorf("failed to initialize cloudflare provider: %w", err)
} }
provider := &CloudFlareProvider{
// Client: config, return &CloudFlareProvider{
Client: zoneService{config}, Client: zoneService{config},
domainFilter: domainFilter, domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter, zoneIDFilter: zoneIDFilter,
@ -289,13 +289,12 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov
DryRun: dryRun, DryRun: dryRun,
DNSRecordsPerPage: dnsRecordsPerPage, DNSRecordsPerPage: dnsRecordsPerPage,
RegionKey: regionKey, RegionKey: regionKey,
} }, nil
return provider, nil
} }
// Zones returns the list of hosted zones. // Zones returns the list of hosted zones.
func (p *CloudFlareProvider) Zones(ctx context.Context) ([]cloudflare.Zone, error) { func (p *CloudFlareProvider) Zones(ctx context.Context) ([]cloudflare.Zone, error) {
result := []cloudflare.Zone{} var result []cloudflare.Zone
// if there is a zoneIDfilter configured // if there is a zoneIDfilter configured
// && if the filter isn't just a blank string (used in tests) // && if the filter isn't just a blank string (used in tests)
@ -349,7 +348,7 @@ func (p *CloudFlareProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
return nil, err return nil, err
} }
endpoints := []*endpoint.Endpoint{} var endpoints []*endpoint.Endpoint
for _, zone := range zones { for _, zone := range zones {
records, err := p.listDNSRecordsWithAutoPagination(ctx, zone.ID) records, err := p.listDNSRecordsWithAutoPagination(ctx, zone.ID)
if err != nil { if err != nil {
@ -373,7 +372,7 @@ func (p *CloudFlareProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
// ApplyChanges applies a given set of changes in a given zone. // ApplyChanges applies a given set of changes in a given zone.
func (p *CloudFlareProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { func (p *CloudFlareProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
cloudflareChanges := []*cloudFlareChange{} var cloudflareChanges []*cloudFlareChange
// if custom hostnames are enabled, deleting first allows to avoid conflicts with the new ones // if custom hostnames are enabled, deleting first allows to avoid conflicts with the new ones
if p.CustomHostnamesConfig.Enabled { if p.CustomHostnamesConfig.Enabled {
@ -425,7 +424,7 @@ func (p *CloudFlareProvider) submitCustomHostnameChanges(ctx context.Context, zo
failedChange := false failedChange := false
// return early if disabled // return early if disabled
if !p.CustomHostnamesConfig.Enabled { if !p.CustomHostnamesConfig.Enabled {
return !failedChange return true
} }
switch change.Action { switch change.Action {
@ -730,7 +729,7 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
// AdjustEndpoints modifies the endpoints as needed by the specific provider // AdjustEndpoints modifies the endpoints as needed by the specific provider
func (p *CloudFlareProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) { func (p *CloudFlareProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
adjustedEndpoints := []*endpoint.Endpoint{} var adjustedEndpoints []*endpoint.Endpoint
for _, e := range endpoints { for _, e := range endpoints {
proxied := shouldBeProxied(e, p.proxiedByDefault) proxied := shouldBeProxied(e, p.proxiedByDefault)
if proxied { if proxied {
@ -802,7 +801,7 @@ func (p *CloudFlareProvider) newCustomHostname(customHostname string, origin str
} }
func (p *CloudFlareProvider) newCloudFlareChange(action string, ep *endpoint.Endpoint, target string, current *endpoint.Endpoint) *cloudFlareChange { func (p *CloudFlareProvider) newCloudFlareChange(action string, ep *endpoint.Endpoint, target string, current *endpoint.Endpoint) *cloudFlareChange {
ttl := defaultCloudFlareRecordTTL ttl := defaultTTL
proxied := shouldBeProxied(ep, p.proxiedByDefault) proxied := shouldBeProxied(ep, p.proxiedByDefault)
if ep.RecordTTL.IsConfigured() { if ep.RecordTTL.IsConfigured() {
@ -969,7 +968,7 @@ func getEndpointCustomHostnames(ep *endpoint.Endpoint) []string {
} }
func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHostnamesMap) []*endpoint.Endpoint { func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHostnamesMap) []*endpoint.Endpoint {
endpoints := []*endpoint.Endpoint{} var endpoints []*endpoint.Endpoint
// group supported records by name and type // group supported records by name and type
groups := map[string][]cloudflare.DNSRecord{} groups := map[string][]cloudflare.DNSRecord{}
@ -994,7 +993,7 @@ func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHost
customHostnames[c.CustomOriginServer] = append(customHostnames[c.CustomOriginServer], c.Hostname) customHostnames[c.CustomOriginServer] = append(customHostnames[c.CustomOriginServer], c.Hostname)
} }
// create single endpoint with all the targets for each name/type // create a single endpoint with all the targets for each name/type
for _, records := range groups { for _, records := range groups {
if len(records) == 0 { if len(records) == 0 {
return endpoints return endpoints

View File

@ -51,6 +51,7 @@ type mockCloudFlareClient struct {
Records map[string]map[string]cloudflare.DNSRecord Records map[string]map[string]cloudflare.DNSRecord
Actions []MockAction Actions []MockAction
listZonesError error listZonesError error
zoneDetailsError error
listZonesContextError error listZonesContextError error
dnsRecordsError error dnsRecordsError error
customHostnames map[string][]cloudflare.CustomHostname customHostnames map[string][]cloudflare.CustomHostname
@ -410,6 +411,10 @@ func (m *mockCloudFlareClient) ListZonesContext(ctx context.Context, opts ...clo
} }
func (m *mockCloudFlareClient) ZoneDetails(ctx context.Context, zoneID string) (cloudflare.Zone, error) { func (m *mockCloudFlareClient) ZoneDetails(ctx context.Context, zoneID string) (cloudflare.Zone, error) {
if m.zoneDetailsError != nil {
return cloudflare.Zone{}, m.zoneDetailsError
}
for id, zoneName := range m.Zones { for id, zoneName := range m.Zones {
if zoneID == id { if zoneID == id {
return cloudflare.Zone{ return cloudflare.Zone{
@ -891,6 +896,24 @@ func TestCloudflareZones(t *testing.T) {
assert.Equal(t, "bar.com", zones[0].Name) assert.Equal(t, "bar.com", zones[0].Name)
} }
// test failures on zone lookup
func TestCloudflareZonesFailed(t *testing.T) {
client := NewMockCloudFlareClient()
client.zoneDetailsError = errors.New("zone lookup failed")
provider := &CloudFlareProvider{
Client: client,
domainFilter: endpoint.NewDomainFilter([]string{"bar.com"}),
zoneIDFilter: provider.NewZoneIDFilter([]string{"001"}),
}
_, err := provider.Zones(context.Background())
if err == nil {
t.Errorf("should fail, %s", err)
}
}
func TestCloudFlareZonesWithIDFilter(t *testing.T) { func TestCloudFlareZonesWithIDFilter(t *testing.T) {
client := NewMockCloudFlareClient() client := NewMockCloudFlareClient()
client.listZonesError = errors.New("shouldn't need to list zones when ZoneIDFilter in use") client.listZonesError = errors.New("shouldn't need to list zones when ZoneIDFilter in use")
@ -1002,64 +1025,88 @@ func TestCloudflareRecords(t *testing.T) {
} }
func TestCloudflareProvider(t *testing.T) { func TestCloudflareProvider(t *testing.T) {
_ = os.Setenv("CF_API_TOKEN", "abc123def") var err error
_, err := NewCloudFlareProvider(
endpoint.NewDomainFilter([]string{"bar.com"}), type EnvVar struct {
provider.NewZoneIDFilter([]string{""}), Key string
false, Value string
true,
5000,
"",
CustomHostnamesConfig{Enabled: false})
if err != nil {
t.Errorf("should not fail, %s", err)
} }
_ = os.Unsetenv("CF_API_TOKEN")
tokenFile := "/tmp/cf_api_token" tokenFile := "/tmp/cf_api_token"
if err := os.WriteFile(tokenFile, []byte("abc123def"), 0o644); err != nil { if err := os.WriteFile(tokenFile, []byte("abc123def"), 0o644); err != nil {
t.Errorf("failed to write token file, %s", err) t.Errorf("failed to write token file, %s", err)
} }
_ = os.Setenv("CF_API_TOKEN", tokenFile)
_, err = NewCloudFlareProvider( testCases := []struct {
endpoint.NewDomainFilter([]string{"bar.com"}), Name string
provider.NewZoneIDFilter([]string{""}), Environment []EnvVar
false, ShouldFail bool
true, }{
5000, {
"", Name: "use_api_token",
CustomHostnamesConfig{Enabled: false}) Environment: []EnvVar{
if err != nil { {Key: "CF_API_TOKEN", Value: "abc123def"},
t.Errorf("should not fail, %s", err) },
ShouldFail: false,
},
{
Name: "use_api_token_file_contents",
Environment: []EnvVar{
{Key: "CF_API_TOKEN", Value: tokenFile},
},
ShouldFail: false,
},
{
Name: "use_email_and_key",
Environment: []EnvVar{
{Key: "CF_API_KEY", Value: "xxxxxxxxxxxxxxxxx"},
{Key: "CF_API_EMAIL", Value: "test@test.com"},
},
ShouldFail: false,
},
{
Name: "no_use_email_and_key",
Environment: []EnvVar{},
ShouldFail: true,
},
{
Name: "use_credentials_in_missing_file",
Environment: []EnvVar{
{Key: "CF_API_TOKEN", Value: "file://abc"},
},
ShouldFail: true,
},
{
Name: "use_credentials_in_missing_file",
Environment: []EnvVar{
{Key: "CF_API_TOKEN", Value: "file:/tmp/cf_api_token"},
},
ShouldFail: false,
},
} }
_ = os.Unsetenv("CF_API_TOKEN") for _, tc := range testCases {
_ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx") t.Run(tc.Name, func(t *testing.T) {
_ = os.Setenv("CF_API_EMAIL", "test@test.com") for _, env := range tc.Environment {
_, err = NewCloudFlareProvider( t.Setenv(env.Key, env.Value)
endpoint.NewDomainFilter([]string{"bar.com"}), }
provider.NewZoneIDFilter([]string{""}),
false, _, err = NewCloudFlareProvider(
true, endpoint.NewDomainFilter([]string{"bar.com"}),
5000, provider.NewZoneIDFilter([]string{""}),
"", false,
CustomHostnamesConfig{Enabled: false}) true,
if err != nil { 5000,
t.Errorf("should not fail, %s", err) "",
} CustomHostnamesConfig{Enabled: false})
if err != nil && !tc.ShouldFail {
t.Errorf("should not fail, %s", err)
}
if err == nil && tc.ShouldFail {
t.Errorf("should fail, %s", err)
}
})
_ = os.Unsetenv("CF_API_KEY")
_ = os.Unsetenv("CF_API_EMAIL")
_, err = NewCloudFlareProvider(
endpoint.NewDomainFilter([]string{"bar.com"}),
provider.NewZoneIDFilter([]string{""}),
false,
true,
5000,
"",
CustomHostnamesConfig{Enabled: false})
if err == nil {
t.Errorf("expected to fail")
} }
} }
@ -1132,6 +1179,30 @@ func TestCloudflareApplyChanges(t *testing.T) {
} }
} }
func TestCloudflareDryRunApplyChanges(t *testing.T) {
changes := &plan.Changes{}
client := NewMockCloudFlareClient()
provider := &CloudFlareProvider{
Client: client,
DryRun: true,
}
changes.Create = []*endpoint.Endpoint{{
DNSName: "new.bar.com",
Targets: endpoint.Targets{"target"},
}}
err := provider.ApplyChanges(context.Background(), changes)
if err != nil {
t.Errorf("should not fail, %s", err)
}
ctx := context.Background()
records, err := provider.Records(ctx)
if err != nil {
t.Errorf("should not fail, %s", err)
}
assert.Equal(t, 0, len(records), "should not have any records")
}
func TestCloudflareApplyChangesError(t *testing.T) { func TestCloudflareApplyChangesError(t *testing.T) {
changes := &plan.Changes{} changes := &plan.Changes{}
client := NewMockCloudFlareClient() client := NewMockCloudFlareClient()
@ -1217,7 +1288,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.1", Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
}, },
@ -1226,7 +1297,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com", DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1"}, Targets: endpoint.Targets{"10.10.10.1"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -1244,14 +1315,14 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.1", Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
{ {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.2", Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
}, },
@ -1260,7 +1331,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com", DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -1278,28 +1349,28 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.1", Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
{ {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.2", Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
{ {
Name: "bar.de", Name: "bar.de",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.1", Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
{ {
Name: "bar.de", Name: "bar.de",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.2", Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
}, },
@ -1308,7 +1379,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com", DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -1321,7 +1392,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "bar.de", DNSName: "bar.de",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -1339,21 +1410,21 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.1", Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
{ {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.2", Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
{ {
Name: "bar.de", Name: "bar.de",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.1", Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
}, },
@ -1362,7 +1433,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com", DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -1375,7 +1446,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "bar.de", DNSName: "bar.de",
Targets: endpoint.Targets{"10.10.10.1"}, Targets: endpoint.Targets{"10.10.10.1"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -1393,21 +1464,21 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.1", Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
{ {
Name: "foo.com", Name: "foo.com",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Content: "10.10.10.2", Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
{ {
Name: "bar.de", Name: "bar.de",
Type: "NOT SUPPORTED", Type: "NOT SUPPORTED",
Content: "10.10.10.1", Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL, TTL: defaultTTL,
Proxied: proxyDisabled, Proxied: proxyDisabled,
}, },
}, },
@ -1416,7 +1487,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com", DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"}, Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -1568,7 +1639,7 @@ func TestCloudflareComplexUpdate(t *testing.T) {
DNSName: "foobar.bar.com", DNSName: "foobar.bar.com",
Targets: endpoint.Targets{"1.2.3.4", "2.3.4.5"}, Targets: endpoint.Targets{"1.2.3.4", "2.3.4.5"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -1889,7 +1960,7 @@ func TestCloudflareLongRecordsErrorLog(t *testing.T) {
}, },
}, },
}) })
b := testutils.LogsToBuffer(log.InfoLevel, t) hook := testutils.LogsUnderTestWithLogLevel(log.InfoLevel, t)
p := &CloudFlareProvider{ p := &CloudFlareProvider{
Client: client, Client: client,
CustomHostnamesConfig: CustomHostnamesConfig{Enabled: true}, CustomHostnamesConfig: CustomHostnamesConfig{Enabled: true},
@ -1899,7 +1970,7 @@ func TestCloudflareLongRecordsErrorLog(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("should not fail - too long record, %s", err) t.Errorf("should not fail - too long record, %s", err)
} }
assert.Contains(t, b.String(), "is longer than 63 characters. Cannot create endpoint") testutils.TestHelperLogContains("s longer than 63 characters. Cannot create endpoint", hook, t)
} }
// check if the error is expected // check if the error is expected
@ -1935,7 +2006,7 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
DNSName: "newerror.bar.com", DNSName: "newerror.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
}, },
}, },
@ -1948,7 +2019,7 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
DNSName: "newerror-list-1.foo.bar.com", DNSName: "newerror-list-1.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
}, },
}, },
@ -1966,7 +2037,7 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
DNSName: "newerror-update-1.foo.bar.com", DNSName: "newerror-update-1.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
}, },
}, },
@ -2050,7 +2121,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "create.foo.bar.com", DNSName: "create.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2069,7 +2140,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "origin.foo.bar.com", DNSName: "origin.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4", "2.3.4.5"}, Targets: endpoint.Targets{"1.2.3.4", "2.3.4.5"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2082,7 +2153,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "another-origin.foo.bar.com", DNSName: "another-origin.foo.bar.com",
Targets: endpoint.Targets{"3.4.5.6"}, Targets: endpoint.Targets{"3.4.5.6"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2101,7 +2172,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "c.foo.bar.com", DNSName: "c.foo.bar.com",
Targets: endpoint.Targets{"c.cname.foo.bar.com"}, Targets: endpoint.Targets{"c.cname.foo.bar.com"},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2122,7 +2193,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/external-dns/my-domain-here-app", "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/external-dns/my-domain-here-app",
}, },
RecordType: endpoint.RecordTypeTXT, RecordType: endpoint.RecordTypeTXT,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2141,7 +2212,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "fail.foo.bar.com", DNSName: "fail.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2160,7 +2231,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "fail.list.foo.bar.com", DNSName: "fail.list.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2184,7 +2255,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com", DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2203,7 +2274,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com", DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2222,7 +2293,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com", DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2241,7 +2312,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com", DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
}, },
}, },
@ -2254,7 +2325,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com", DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2285,7 +2356,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "nocustomhostname.foo.bar.com", DNSName: "nocustomhostname.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
}, },
}, },
@ -2298,7 +2369,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2311,7 +2382,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "txt.foo.bar.com", DNSName: "txt.foo.bar.com",
Targets: endpoint.Targets{"value"}, Targets: endpoint.Targets{"value"},
RecordType: endpoint.RecordTypeTXT, RecordType: endpoint.RecordTypeTXT,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2332,7 +2403,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2353,7 +2424,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2376,7 +2447,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2399,7 +2470,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2420,7 +2491,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
}, },
}, },
@ -2509,7 +2580,7 @@ func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.11"}, Targets: endpoint.Targets{"1.2.3.11"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2522,14 +2593,14 @@ func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com", DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.12"}, Targets: endpoint.Targets{"1.2.3.12"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
}, },
{ {
DNSName: "c.foo.bar.com", DNSName: "c.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.13"}, Targets: endpoint.Targets{"1.2.3.13"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2548,14 +2619,14 @@ func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.11"}, Targets: endpoint.Targets{"1.2.3.11"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
}, },
{ {
DNSName: "b.foo.bar.com", DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.12"}, Targets: endpoint.Targets{"1.2.3.12"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2568,7 +2639,7 @@ func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
DNSName: "c.foo.bar.com", DNSName: "c.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.13"}, Targets: endpoint.Targets{"1.2.3.13"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2632,7 +2703,7 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
DNSName: "create.foo.bar.com", DNSName: "create.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2649,7 +2720,7 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
Name: "remove DNS record with unexpectedly missing custom hostname", Name: "remove DNS record with unexpectedly missing custom hostname",
Endpoints: []*endpoint.Endpoint{}, Endpoints: []*endpoint.Endpoint{},
preApplyHook: "corrupt", preApplyHook: "corrupt",
logOutput: "level=warning msg=\"failed to delete custom hostname \\\"newerror-getCustomHostnameOrigin.foo.fancybar.com\\\": failed to get custom hostname: \\\"newerror-getCustomHostnameOrigin.foo.fancybar.com\\\" not found\" action=DELETE record=create.foo.bar.com", logOutput: "failed to delete custom hostname \"newerror-getCustomHostnameOrigin.foo.fancybar.com\": failed to get custom hostname: \"newerror-getCustomHostnameOrigin.foo.fancybar.com\" not found",
}, },
{ {
Name: "duplicate custom hostname", Name: "duplicate custom hostname",
@ -2664,7 +2735,7 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
DNSName: "a.foo.bar.com", DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"}, Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA, RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {
@ -2675,12 +2746,12 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
}, },
}, },
preApplyHook: "", preApplyHook: "",
logOutput: "custom hostname \\\"a.foo.fancybar.com\\\" already exists with the same origin \\\"a.foo.bar.com\\\", continue", logOutput: "custom hostname \"a.foo.fancybar.com\" already exists with the same origin \"a.foo.bar.com\", continue",
}, },
} }
for _, tc := range testCases { for _, tc := range testCases {
b := testutils.LogsToBuffer(log.InfoLevel, t) hook := testutils.LogsUnderTestWithLogLevel(log.InfoLevel, t)
records, err := provider.Records(ctx) records, err := provider.Records(ctx)
if err != nil { if err != nil {
@ -2730,7 +2801,8 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
if e := checkFailed(tc.Name, err, false); !errors.Is(e, nil) { if e := checkFailed(tc.Name, err, false); !errors.Is(e, nil) {
t.Error(e) t.Error(e)
} }
assert.Contains(t, b.String(), tc.logOutput)
testutils.TestHelperLogContains(tc.logOutput, hook, t)
} }
} }
@ -2751,7 +2823,7 @@ func TestCloudflareListCustomHostnamesWithPagionation(t *testing.T) {
DNSName: fmt.Sprintf("host-%d.foo.bar.com", i), DNSName: fmt.Sprintf("host-%d.foo.bar.com", i),
Targets: endpoint.Targets{fmt.Sprintf("cname-%d.foo.bar.com", i)}, Targets: endpoint.Targets{fmt.Sprintf("cname-%d.foo.bar.com", i)},
RecordType: endpoint.RecordTypeCNAME, RecordType: endpoint.RecordTypeCNAME,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL), RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{}, Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{ ProviderSpecific: endpoint.ProviderSpecific{
{ {

View File

@ -92,7 +92,7 @@ type etcdClient struct {
var _ coreDNSClient = etcdClient{} var _ coreDNSClient = etcdClient{}
// GetService return all Service records stored in etcd stored anywhere under the given key (recursively) // GetServices GetService return all Service records stored in etcd stored anywhere under the given key (recursively)
func (c etcdClient) GetServices(prefix string) ([]*Service, error) { func (c etcdClient) GetServices(prefix string) ([]*Service, error) {
ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout) ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
defer cancel() defer cancel()

View File

@ -34,8 +34,8 @@ import (
) )
const ( const (
// digitalOceanRecordTTL is the default TTL value // defaultTTL is the default TTL value
digitalOceanRecordTTL = 300 defaultTTL = 300
) )
// DigitalOceanProvider is an implementation of Provider for Digital Ocean's DNS. // DigitalOceanProvider is an implementation of Provider for Digital Ocean's DNS.
@ -397,7 +397,7 @@ func getTTLFromEndpoint(ep *endpoint.Endpoint) int {
if ep.RecordTTL.IsConfigured() { if ep.RecordTTL.IsConfigured() {
return int(ep.RecordTTL) return int(ep.RecordTTL)
} }
return digitalOceanRecordTTL return defaultTTL
} }
func endpointsByZone(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) map[string][]*endpoint.Endpoint { func endpointsByZone(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) map[string][]*endpoint.Endpoint {

View File

@ -309,32 +309,32 @@ func TestDigitalOceanZones(t *testing.T) {
func TestDigitalOceanMakeDomainEditRequest(t *testing.T) { func TestDigitalOceanMakeDomainEditRequest(t *testing.T) {
// Ensure that records at the root of the zone get `@` as the name. // Ensure that records at the root of the zone get `@` as the name.
r1 := makeDomainEditRequest("example.com", "example.com", endpoint.RecordTypeA, r1 := makeDomainEditRequest("example.com", "example.com", endpoint.RecordTypeA,
"1.2.3.4", digitalOceanRecordTTL) "1.2.3.4", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{ assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Name: "@", Name: "@",
Data: "1.2.3.4", Data: "1.2.3.4",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, r1) }, r1)
// Ensure the CNAME records have a `.` appended. // Ensure the CNAME records have a `.` appended.
r2 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeCNAME, r2 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeCNAME,
"bar.example.com", digitalOceanRecordTTL) "bar.example.com", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{ assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeCNAME, Type: endpoint.RecordTypeCNAME,
Name: "foo", Name: "foo",
Data: "bar.example.com.", Data: "bar.example.com.",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, r2) }, r2)
// Ensure that CNAME records do not have an extra `.` appended if they already have a `.` // Ensure that CNAME records do not have an extra `.` appended if they already have a `.`
r3 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeCNAME, r3 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeCNAME,
"bar.example.com.", digitalOceanRecordTTL) "bar.example.com.", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{ assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeCNAME, Type: endpoint.RecordTypeCNAME,
Name: "foo", Name: "foo",
Data: "bar.example.com.", Data: "bar.example.com.",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, r3) }, r3)
// Ensure that custom TTLs can be set // Ensure that custom TTLs can be set
@ -350,24 +350,24 @@ func TestDigitalOceanMakeDomainEditRequest(t *testing.T) {
// Ensure that MX records have `.` appended. // Ensure that MX records have `.` appended.
r5 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX, r5 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX,
"10 mx.example.com", digitalOceanRecordTTL) "10 mx.example.com", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{ assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeMX, Type: endpoint.RecordTypeMX,
Name: "foo", Name: "foo",
Data: "mx.example.com.", Data: "mx.example.com.",
Priority: 10, Priority: 10,
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, r5) }, r5)
// Ensure that MX records do not have an extra `.` appended if they already have a `.` // Ensure that MX records do not have an extra `.` appended if they already have a `.`
r6 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX, r6 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX,
"10 mx.example.com.", digitalOceanRecordTTL) "10 mx.example.com.", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{ assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeMX, Type: endpoint.RecordTypeMX,
Name: "foo", Name: "foo",
Data: "mx.example.com.", Data: "mx.example.com.",
Priority: 10, Priority: 10,
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, r6) }, r6)
} }
@ -420,7 +420,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
Name: "foo", Name: "foo",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Data: "1.2.3.4", Data: "1.2.3.4",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
{ {
@ -429,7 +429,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
Name: "@", Name: "@",
Type: endpoint.RecordTypeCNAME, Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.", Data: "foo.example.com.",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
{ {
@ -439,7 +439,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
Type: endpoint.RecordTypeMX, Type: endpoint.RecordTypeMX,
Priority: 10, Priority: 10,
Data: "mx.example.com.", Data: "mx.example.com.",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
{ {
@ -448,7 +448,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
Name: "@", Name: "@",
Type: endpoint.RecordTypeTXT, Type: endpoint.RecordTypeTXT,
Data: "SOME-TXT-TEXT", Data: "SOME-TXT-TEXT",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
} }
@ -466,21 +466,21 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Name: "foo", Name: "foo",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Data: "1.2.3.4", Data: "1.2.3.4",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
{ {
ID: 2, ID: 2,
Name: "foo", Name: "foo",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Data: "5.6.7.8", Data: "5.6.7.8",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
{ {
ID: 3, ID: 3,
Name: "@", Name: "@",
Type: endpoint.RecordTypeCNAME, Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.", Data: "foo.example.com.",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
{ {
ID: 4, ID: 4,
@ -488,7 +488,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Type: endpoint.RecordTypeMX, Type: endpoint.RecordTypeMX,
Data: "mx1.example.com.", Data: "mx1.example.com.",
Priority: 10, Priority: 10,
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
{ {
ID: 5, ID: 5,
@ -496,14 +496,14 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Type: endpoint.RecordTypeMX, Type: endpoint.RecordTypeMX,
Data: "mx2.example.com.", Data: "mx2.example.com.",
Priority: 10, Priority: 10,
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
{ {
ID: 6, ID: 6,
Name: "@", Name: "@",
Type: endpoint.RecordTypeTXT, Type: endpoint.RecordTypeTXT,
Data: "SOME_TXTX_TEXT", Data: "SOME_TXTX_TEXT",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
} }
@ -532,7 +532,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Name: "foo", Name: "foo",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Data: "10.11.12.13", Data: "10.11.12.13",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
{ {
@ -541,7 +541,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Name: "@", Name: "@",
Type: endpoint.RecordTypeCNAME, Type: endpoint.RecordTypeCNAME,
Data: "bar.example.com.", Data: "bar.example.com.",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
{ {
@ -551,7 +551,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Type: endpoint.RecordTypeMX, Type: endpoint.RecordTypeMX,
Data: "mx3.example.com.", Data: "mx3.example.com.",
Priority: 10, Priority: 10,
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
{ {
@ -560,7 +560,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Name: "@", Name: "@",
Type: endpoint.RecordTypeTXT, Type: endpoint.RecordTypeTXT,
Data: "ANOTHER-TXT", Data: "ANOTHER-TXT",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
} }
@ -609,7 +609,7 @@ func TestDigitalOceanProcessDeleteActions(t *testing.T) {
Name: "foo", Name: "foo",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Data: "1.2.3.4", Data: "1.2.3.4",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
// This record will not be deleted because it represents a target not specified to be deleted. // This record will not be deleted because it represents a target not specified to be deleted.
{ {
@ -617,14 +617,14 @@ func TestDigitalOceanProcessDeleteActions(t *testing.T) {
Name: "foo", Name: "foo",
Type: endpoint.RecordTypeA, Type: endpoint.RecordTypeA,
Data: "5.6.7.8", Data: "5.6.7.8",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
{ {
ID: 3, ID: 3,
Name: "@", Name: "@",
Type: endpoint.RecordTypeCNAME, Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.", Data: "foo.example.com.",
TTL: digitalOceanRecordTTL, TTL: defaultTTL,
}, },
}, },
} }

View File

@ -33,7 +33,13 @@ import (
"sigs.k8s.io/external-dns/provider" "sigs.k8s.io/external-dns/provider"
) )
const dnsimpleRecordTTL = 3600 // Default TTL of 1 hour if not set (DNSimple's default) const (
dnsimpleCreate = "CREATE"
dnsimpleDelete = "DELETE"
dnsimpleUpdate = "UPDATE"
defaultTTL = 3600 // Default TTL of 1 hour if not set (DNSimple's default)
)
type dnsimpleIdentityService struct { type dnsimpleIdentityService struct {
service *dnsimple.IdentityService service *dnsimple.IdentityService
@ -91,12 +97,6 @@ type dnsimpleChange struct {
ResourceRecordSet dnsimple.ZoneRecord ResourceRecordSet dnsimple.ZoneRecord
} }
const (
dnsimpleCreate = "CREATE"
dnsimpleDelete = "DELETE"
dnsimpleUpdate = "UPDATE"
)
// NewDnsimpleProvider initializes a new Dnsimple based provider // NewDnsimpleProvider initializes a new Dnsimple based provider
func NewDnsimpleProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool) (provider.Provider, error) { func NewDnsimpleProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool) (provider.Provider, error) {
oauthToken := os.Getenv("DNSIMPLE_OAUTH") oauthToken := os.Getenv("DNSIMPLE_OAUTH")
@ -149,7 +149,7 @@ func ZonesFromZoneString(zonestring string) map[string]dnsimple.Zone {
return zones return zones
} }
// Returns a list of filtered Zones // Zones Return a list of filtered Zones
func (p *dnsimpleProvider) Zones(ctx context.Context) (map[string]dnsimple.Zone, error) { func (p *dnsimpleProvider) Zones(ctx context.Context) (map[string]dnsimple.Zone, error) {
zones := make(map[string]dnsimple.Zone) zones := make(map[string]dnsimple.Zone)
@ -231,7 +231,7 @@ func (p *dnsimpleProvider) Records(ctx context.Context) (endpoints []*endpoint.E
// newDnsimpleChange initializes a new change to dns records // newDnsimpleChange initializes a new change to dns records
func newDnsimpleChange(action string, e *endpoint.Endpoint) *dnsimpleChange { func newDnsimpleChange(action string, e *endpoint.Endpoint) *dnsimpleChange {
ttl := dnsimpleRecordTTL ttl := defaultTTL
if e.RecordTTL.IsConfigured() { if e.RecordTTL.IsConfigured() {
ttl = int(e.RecordTTL) ttl = int(e.RecordTTL)
} }

View File

@ -181,7 +181,7 @@ func (ep *ExoscaleProvider) ApplyChanges(ctx context.Context, changes *plan.Chan
} }
for _, epoint := range changes.UpdateOld { for _, epoint := range changes.UpdateOld {
// Since Exoscale "Patches", we ignore UpdateOld // Since Exoscale "Patches", we've ignored UpdateOld
// We leave this logging here for information // We leave this logging here for information
log.Debugf("UPDATE-OLD (ignored) for epoint: %+v", epoint) log.Debugf("UPDATE-OLD (ignored) for epoint: %+v", epoint)
} }
@ -310,10 +310,10 @@ func (f *zoneFilter) EndpointZoneID(endpoint *endpoint.Endpoint, zones map[strin
func merge(updateOld, updateNew []*endpoint.Endpoint) []*endpoint.Endpoint { func merge(updateOld, updateNew []*endpoint.Endpoint) []*endpoint.Endpoint {
findMatch := func(template *endpoint.Endpoint) *endpoint.Endpoint { findMatch := func(template *endpoint.Endpoint) *endpoint.Endpoint {
for _, new := range updateNew { for _, record := range updateNew {
if template.DNSName == new.DNSName && if template.DNSName == record.DNSName &&
template.RecordType == new.RecordType { template.RecordType == record.RecordType {
return new return record
} }
} }
return nil return nil
@ -323,7 +323,7 @@ func merge(updateOld, updateNew []*endpoint.Endpoint) []*endpoint.Endpoint {
for _, old := range updateOld { for _, old := range updateOld {
matchingNew := findMatch(old) matchingNew := findMatch(old)
if matchingNew == nil { if matchingNew == nil {
// no match, shouldn't happen // no match shouldn't happen
continue continue
} }

View File

@ -33,7 +33,7 @@ const (
gandiCreate = "CREATE" gandiCreate = "CREATE"
gandiDelete = "DELETE" gandiDelete = "DELETE"
gandiUpdate = "UPDATE" gandiUpdate = "UPDATE"
gandiTTL = 600 defaultTTL = 600
gandiLiveDNSProvider = "livedns" gandiLiveDNSProvider = "livedns"
) )
@ -255,7 +255,7 @@ func (p *GandiProvider) submitChanges(ctx context.Context, changes []*GandiChang
func (p *GandiProvider) newGandiChanges(action string, endpoints []*endpoint.Endpoint) []*GandiChanges { func (p *GandiProvider) newGandiChanges(action string, endpoints []*endpoint.Endpoint) []*GandiChanges {
changes := make([]*GandiChanges, 0, len(endpoints)) changes := make([]*GandiChanges, 0, len(endpoints))
ttl := gandiTTL ttl := defaultTTL
for _, e := range endpoints { for _, e := range endpoints {
if e.RecordTTL.IsConfigured() { if e.RecordTTL.IsConfigured() {
ttl = int(e.RecordTTL) ttl = int(e.RecordTTL)

View File

@ -34,19 +34,18 @@ import (
"sigs.k8s.io/external-dns/pkg/apis/externaldns" "sigs.k8s.io/external-dns/pkg/apis/externaldns"
) )
// DefaultTimeout api requests after 180s const (
const DefaultTimeout = 180 * time.Second ErrCodeQuotaExceeded = "QUOTA_EXCEEDED"
// DefaultTimeout api requests after
DefaultTimeout = 180 * time.Second
)
// Errors // Errors
var ( var (
ErrAPIDown = errors.New("godaddy: the GoDaddy API is down") ErrAPIDown = errors.New("godaddy: the GoDaddy API is down")
) )
// error codes
const (
ErrCodeQuotaExceeded = "QUOTA_EXCEEDED"
)
// APIError error // APIError error
type APIError struct { type APIError struct {
Code string Code string

View File

@ -19,8 +19,8 @@ package godaddy
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"slices"
"strings" "strings"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -32,10 +32,12 @@ import (
) )
const ( const (
gdMinimalTTL = 600 defaultTTL = 600
gdCreate = 0 gdCreate = 0
gdReplace = 1 gdReplace = 1
gdDelete = 2 gdDelete = 2
domainsURI = "/v1/domains?statuses=ACTIVE,PENDING_DNS_ACTIVE"
) )
var actionNames = []string{ var actionNames = []string{
@ -44,11 +46,6 @@ var actionNames = []string{
"delete", "delete",
} }
const domainsURI = "/v1/domains?statuses=ACTIVE,PENDING_DNS_ACTIVE"
// ErrRecordToMutateNotFound when ApplyChange has to update/delete and didn't found the record in the existing zone (Change with no record ID)
var ErrRecordToMutateNotFound = errors.New("record to mutate not found in current zone")
type gdClient interface { type gdClient interface {
Patch(string, interface{}, interface{}) error Patch(string, interface{}, interface{}) error
Post(string, interface{}, interface{}) error Post(string, interface{}, interface{}) error
@ -147,7 +144,7 @@ func NewGoDaddyProvider(ctx context.Context, domainFilter endpoint.DomainFilter,
return &GDProvider{ return &GDProvider{
client: client, client: client,
domainFilter: domainFilter, domainFilter: domainFilter,
ttl: maxOf(gdMinimalTTL, ttl), ttl: maxOf(defaultTTL, ttl),
DryRun: dryRun, DryRun: dryRun,
}, nil }, nil
} }
@ -294,14 +291,14 @@ func (p *GDProvider) groupByNameAndType(zoneRecords []gdRecords) []*endpoint.End
recordName = strings.TrimPrefix(fmt.Sprintf("%s.%s", records[0].Name, zoneName), ".") recordName = strings.TrimPrefix(fmt.Sprintf("%s.%s", records[0].Name, zoneName), ".")
} }
endpoint := endpoint.NewEndpointWithTTL( ep := endpoint.NewEndpointWithTTL(
recordName, recordName,
records[0].Type, records[0].Type,
endpoint.TTL(records[0].TTL), endpoint.TTL(records[0].TTL),
targets..., targets...,
) )
endpoints = append(endpoints, endpoint) endpoints = append(endpoints, ep)
} }
} }
@ -356,7 +353,7 @@ func (p *GDProvider) changeAllRecords(endpoints []gdEndpoint, zoneRecords []*gdR
dnsName = "@" dnsName = "@"
} }
e.endpoint.RecordTTL = endpoint.TTL(maxOf(gdMinimalTTL, int64(e.endpoint.RecordTTL))) e.endpoint.RecordTTL = endpoint.TTL(maxOf(defaultTTL, int64(e.endpoint.RecordTTL)))
if err := zoneRecord.applyEndpoint(e.action, p.client, *e.endpoint, dnsName, p.DryRun); err != nil { if err := zoneRecord.applyEndpoint(e.action, p.client, *e.endpoint, dnsName, p.DryRun); err != nil {
log.Errorf("Unable to apply change %s on record %s type %s, %v", actionNames[e.action], dnsName, e.endpoint.RecordType, err) log.Errorf("Unable to apply change %s on record %s type %s, %v", actionNames[e.action], dnsName, e.endpoint.RecordType, err)
@ -584,8 +581,8 @@ func countTargets(p *plan.Changes) int {
count := 0 count := 0
for _, endpoints := range changes { for _, endpoints := range changes {
for _, endpoint := range endpoints { for _, ep := range endpoints {
count += len(endpoint.Targets) count += len(ep.Targets)
} }
} }
@ -593,15 +590,7 @@ func countTargets(p *plan.Changes) int {
} }
func maxOf(vars ...int64) int64 { func maxOf(vars ...int64) int64 {
max := vars[0] return slices.Max(vars)
for _, i := range vars {
if max < i {
max = i
}
}
return max
} }
func toString(obj interface{}) string { func toString(obj interface{}) string {

View File

@ -138,13 +138,13 @@ func TestGoDaddyZoneRecords(t *testing.T) {
{ {
Name: "godaddy", Name: "godaddy",
Type: "NS", Type: "NS",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.42", Data: "203.0.113.42",
}, },
{ {
Name: "godaddy", Name: "godaddy",
Type: "A", Type: "A",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.42", Data: "203.0.113.42",
}, },
}, nil).Once() }, nil).Once()
@ -164,13 +164,13 @@ func TestGoDaddyZoneRecords(t *testing.T) {
{ {
Name: "godaddy", Name: "godaddy",
Type: "NS", Type: "NS",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.42", Data: "203.0.113.42",
}, },
{ {
Name: "godaddy", Name: "godaddy",
Type: "A", Type: "A",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.42", Data: "203.0.113.42",
}, },
}, },
@ -240,13 +240,13 @@ func TestGoDaddyRecords(t *testing.T) {
{ {
Name: "@", Name: "@",
Type: "A", Type: "A",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.42", Data: "203.0.113.42",
}, },
{ {
Name: "www", Name: "www",
Type: "CNAME", Type: "CNAME",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "example.org", Data: "example.org",
}, },
}, nil).Once() }, nil).Once()
@ -255,13 +255,13 @@ func TestGoDaddyRecords(t *testing.T) {
{ {
Name: "godaddy", Name: "godaddy",
Type: "A", Type: "A",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.42", Data: "203.0.113.42",
}, },
{ {
Name: "godaddy", Name: "godaddy",
Type: "A", Type: "A",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.43", Data: "203.0.113.43",
}, },
}, nil).Once() }, nil).Once()
@ -278,7 +278,7 @@ func TestGoDaddyRecords(t *testing.T) {
{ {
DNSName: "godaddy.example.net", DNSName: "godaddy.example.net",
RecordType: "A", RecordType: "A",
RecordTTL: gdMinimalTTL, RecordTTL: defaultTTL,
Labels: endpoint.NewLabels(), Labels: endpoint.NewLabels(),
Targets: []string{ Targets: []string{
"203.0.113.42", "203.0.113.42",
@ -288,7 +288,7 @@ func TestGoDaddyRecords(t *testing.T) {
{ {
DNSName: "example.org", DNSName: "example.org",
RecordType: "A", RecordType: "A",
RecordTTL: gdMinimalTTL, RecordTTL: defaultTTL,
Labels: endpoint.NewLabels(), Labels: endpoint.NewLabels(),
Targets: []string{ Targets: []string{
"203.0.113.42", "203.0.113.42",
@ -297,7 +297,7 @@ func TestGoDaddyRecords(t *testing.T) {
{ {
DNSName: "www.example.org", DNSName: "www.example.org",
RecordType: "CNAME", RecordType: "CNAME",
RecordTTL: gdMinimalTTL, RecordTTL: defaultTTL,
Labels: endpoint.NewLabels(), Labels: endpoint.NewLabels(),
Targets: []string{ Targets: []string{
"example.org", "example.org",
@ -327,7 +327,7 @@ func TestGoDaddyChange(t *testing.T) {
{ {
DNSName: ".example.net", DNSName: ".example.net",
RecordType: "A", RecordType: "A",
RecordTTL: gdMinimalTTL, RecordTTL: defaultTTL,
Targets: []string{ Targets: []string{
"203.0.113.42", "203.0.113.42",
}, },
@ -356,7 +356,7 @@ func TestGoDaddyChange(t *testing.T) {
{ {
Name: "godaddy", Name: "godaddy",
Type: "A", Type: "A",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.43", Data: "203.0.113.43",
}, },
}, nil).Once() }, nil).Once()
@ -366,7 +366,7 @@ func TestGoDaddyChange(t *testing.T) {
{ {
Name: "@", Name: "@",
Type: "A", Type: "A",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.42", Data: "203.0.113.42",
}, },
}).Return(nil, nil).Once() }).Return(nil, nil).Once()
@ -398,7 +398,7 @@ func TestGoDaddyErrorResponse(t *testing.T) {
{ {
DNSName: ".example.net", DNSName: ".example.net",
RecordType: "A", RecordType: "A",
RecordTTL: gdMinimalTTL, RecordTTL: defaultTTL,
Targets: []string{ Targets: []string{
"203.0.113.42", "203.0.113.42",
}, },
@ -427,7 +427,7 @@ func TestGoDaddyErrorResponse(t *testing.T) {
{ {
Name: "godaddy", Name: "godaddy",
Type: "A", Type: "A",
TTL: gdMinimalTTL, TTL: defaultTTL,
Data: "203.0.113.43", Data: "203.0.113.43",
}, },
}, nil).Once() }, nil).Once()

View File

@ -37,7 +37,7 @@ import (
) )
const ( const (
googleRecordTTL = 300 defaultTTL = 300
) )
type managedZonesCreateCallInterface interface { type managedZonesCreateCallInterface interface {
@ -154,7 +154,7 @@ func NewGoogleProvider(ctx context.Context, project string, domainFilter endpoin
zoneTypeFilter := provider.NewZoneTypeFilter(zoneVisibility) zoneTypeFilter := provider.NewZoneTypeFilter(zoneVisibility)
provider := &GoogleProvider{ return &GoogleProvider{
project: project, project: project,
dryRun: dryRun, dryRun: dryRun,
batchChangeSize: batchChangeSize, batchChangeSize: batchChangeSize,
@ -166,9 +166,7 @@ func NewGoogleProvider(ctx context.Context, project string, domainFilter endpoin
managedZonesClient: managedZonesService{dnsClient.ManagedZones}, managedZonesClient: managedZonesService{dnsClient.ManagedZones},
changesClient: changesService{dnsClient.Changes}, changesClient: changesService{dnsClient.Changes},
ctx: ctx, ctx: ctx,
} }, nil
return provider, nil
} }
// Zones returns the list of hosted zones. // Zones returns the list of hosted zones.
@ -261,11 +259,11 @@ func (p *GoogleProvider) SupportedRecordType(recordType string) bool {
// newFilteredRecords returns a collection of RecordSets based on the given endpoints and domainFilter. // newFilteredRecords returns a collection of RecordSets based on the given endpoints and domainFilter.
func (p *GoogleProvider) newFilteredRecords(endpoints []*endpoint.Endpoint) []*dns.ResourceRecordSet { func (p *GoogleProvider) newFilteredRecords(endpoints []*endpoint.Endpoint) []*dns.ResourceRecordSet {
records := []*dns.ResourceRecordSet{} var records []*dns.ResourceRecordSet
for _, endpoint := range endpoints { for _, ep := range endpoints {
if p.domainFilter.Match(endpoint.DNSName) { if p.domainFilter.Match(ep.DNSName) {
records = append(records, newRecord(endpoint)) records = append(records, newRecord(ep))
} }
} }
@ -314,7 +312,7 @@ func (p *GoogleProvider) submitChange(ctx context.Context, change *dns.Change) e
// batchChange separates a zone in multiple transaction. // batchChange separates a zone in multiple transaction.
func batchChange(change *dns.Change, batchSize int) []*dns.Change { func batchChange(change *dns.Change, batchSize int) []*dns.Change {
changes := []*dns.Change{} var changes []*dns.Change
if batchSize == 0 { if batchSize == 0 {
return append(changes, change) return append(changes, change)
@ -452,7 +450,7 @@ func newRecord(ep *endpoint.Endpoint) *dns.ResourceRecordSet {
} }
// no annotation results in a Ttl of 0, default to 300 for backwards-compatibility // no annotation results in a Ttl of 0, default to 300 for backwards-compatibility
var ttl int64 = googleRecordTTL var ttl int64 = defaultTTL
if ep.RecordTTL.IsConfigured() { if ep.RecordTTL.IsConfigured() {
ttl = int64(ep.RecordTTL) ttl = int64(ep.RecordTTL)
} }

View File

@ -279,12 +279,12 @@ func TestGoogleRecords(t *testing.T) {
func TestGoogleRecordsFilter(t *testing.T) { func TestGoogleRecordsFilter(t *testing.T) {
originalEndpoints := []*endpoint.Endpoint{ originalEndpoints := []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"), endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.8.8"),
endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"), endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.8.8"),
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"), endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.4.4"),
endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"), endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.4.4"),
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "bar.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, defaultTTL, "bar.elb.amazonaws.com"),
endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, defaultTTL, "qux.elb.amazonaws.com"),
} }
provider := newGoogleProvider( provider := newGoogleProvider(
@ -339,12 +339,12 @@ func TestGoogleApplyChanges(t *testing.T) {
provider.NewZoneIDFilter([]string{""}), provider.NewZoneIDFilter([]string{""}),
false, false,
[]*endpoint.Endpoint{ []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"), endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.8.8"),
endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"), endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.8.8"),
endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(10), "8.8.4.4"), endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(10), "8.8.4.4"),
endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"), endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.4.4"),
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "bar.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, defaultTTL, "bar.elb.amazonaws.com"),
endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, defaultTTL, "qux.elb.amazonaws.com"),
}, },
nil, nil,
nil, nil,
@ -393,23 +393,23 @@ func TestGoogleApplyChanges(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
validateEndpoints(t, records, []*endpoint.Endpoint{ validateEndpoints(t, records, []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"), endpoint.NewEndpointWithTTL("create-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.8.8"),
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "1.2.3.4"), endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "1.2.3.4"),
endpoint.NewEndpointWithTTL("create-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(15), "8.8.4.4"), endpoint.NewEndpointWithTTL("create-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(15), "8.8.4.4"),
endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(25), "4.3.2.1"), endpoint.NewEndpointWithTTL("update-test-ttl.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, endpoint.TTL(25), "4.3.2.1"),
endpoint.NewEndpointWithTTL("create-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "foo.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("create-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, defaultTTL, "foo.elb.amazonaws.com"),
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "baz.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, defaultTTL, "baz.elb.amazonaws.com"),
}) })
} }
func TestGoogleApplyChangesDryRun(t *testing.T) { func TestGoogleApplyChangesDryRun(t *testing.T) {
originalEndpoints := []*endpoint.Endpoint{ originalEndpoints := []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"), endpoint.NewEndpointWithTTL("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.8.8"),
endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.8.8"), endpoint.NewEndpointWithTTL("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.8.8"),
endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"), endpoint.NewEndpointWithTTL("update-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.4.4"),
endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "8.8.4.4"), endpoint.NewEndpointWithTTL("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, defaultTTL, "8.8.4.4"),
endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "bar.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, defaultTTL, "bar.elb.amazonaws.com"),
endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"), endpoint.NewEndpointWithTTL("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, defaultTTL, "qux.elb.amazonaws.com"),
} }
provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), true, originalEndpoints, nil, nil) provider := newGoogleProvider(t, endpoint.NewDomainFilter([]string{"ext-dns-test-2.gcp.zalan.do."}), provider.NewZoneIDFilter([]string{""}), true, originalEndpoints, nil, nil)

View File

@ -61,8 +61,8 @@ const (
recordDelete = "DELETE" recordDelete = "DELETE"
// recordUpdate is a ChangeAction enum value // recordUpdate is a ChangeAction enum value
recordUpdate = "UPDATE" recordUpdate = "UPDATE"
// defaultPublicRecordTTL 1 = automatic // defaultTTL 1 = automatic
defaultPublicRecordTTL = 1 defaultTTL = 1
proxyFilter = "ibmcloud-proxied" proxyFilter = "ibmcloud-proxied"
vpcFilter = "ibmcloud-vpc" vpcFilter = "ibmcloud-vpc"
@ -244,7 +244,7 @@ func (c *ibmcloudConfig) Validate(authenticator core.Authenticator, domainFilter
return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud public zones client: %v", err) return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud public zones client: %v", err)
} }
if c.Endpoint != "" { if c.Endpoint != "" {
service.publicZonesService.SetServiceURL(c.Endpoint) _ = service.publicZonesService.SetServiceURL(c.Endpoint)
} }
zonesResp, _, err := service.publicZonesService.ListZones(&zonesv1.ListZonesOptions{}) zonesResp, _, err := service.publicZonesService.ListZones(&zonesv1.ListZonesOptions{})
@ -277,7 +277,7 @@ func (c *ibmcloudConfig) Validate(authenticator core.Authenticator, domainFilter
return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud public records client: %v", err) return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud public records client: %v", err)
} }
if c.Endpoint != "" { if c.Endpoint != "" {
service.publicRecordsService.SetServiceURL(c.Endpoint) _ = service.publicRecordsService.SetServiceURL(c.Endpoint)
} }
case strings.Contains(crn.ServiceName, "dns-svcs"): case strings.Contains(crn.ServiceName, "dns-svcs"):
isPrivate = true isPrivate = true
@ -289,7 +289,7 @@ func (c *ibmcloudConfig) Validate(authenticator core.Authenticator, domainFilter
return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud private records client: %v", err) return service, isPrivate, fmt.Errorf("failed to initialize ibmcloud private records client: %v", err)
} }
if c.Endpoint != "" { if c.Endpoint != "" {
service.privateDNSService.SetServiceURL(c.Endpoint) _ = service.privateDNSService.SetServiceURL(c.Endpoint)
} }
default: default:
return service, isPrivate, fmt.Errorf("IBM Cloud instance crn is not provided or invalid dns crn : %s", c.CRN) return service, isPrivate, fmt.Errorf("IBM Cloud instance crn is not provided or invalid dns crn : %s", c.CRN)
@ -322,7 +322,7 @@ func NewIBMCloudProvider(configFile string, domainFilter endpoint.DomainFilter,
return nil, err return nil, err
} }
provider := &IBMCloudProvider{ return &IBMCloudProvider{
Client: client, Client: client,
source: source, source: source,
domainFilter: domainFilter, domainFilter: domainFilter,
@ -331,8 +331,7 @@ func NewIBMCloudProvider(configFile string, domainFilter endpoint.DomainFilter,
privateZone: isPrivate, privateZone: isPrivate,
proxiedByDefault: proxiedByDefault, proxiedByDefault: proxiedByDefault,
DryRun: dryRun, DryRun: dryRun,
} }, nil
return provider, nil
} }
// Records gets the current records. // Records gets the current records.
@ -351,9 +350,9 @@ func (p *IBMCloudProvider) Records(ctx context.Context) (endpoints []*endpoint.E
func (p *IBMCloudProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { func (p *IBMCloudProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
log.Debugln("applying change...") log.Debugln("applying change...")
ibmcloudChanges := []*ibmcloudChange{} ibmcloudChanges := []*ibmcloudChange{}
for _, endpoint := range changes.Create { for _, et := range changes.Create {
for _, target := range endpoint.Targets { for _, target := range et.Targets {
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordCreate, endpoint, target)) ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordCreate, et, target))
} }
} }
@ -376,9 +375,9 @@ func (p *IBMCloudProvider) ApplyChanges(ctx context.Context, changes *plan.Chang
} }
} }
for _, endpoint := range changes.Delete { for _, et := range changes.Delete {
for _, target := range endpoint.Targets { for _, target := range et.Targets {
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordDelete, endpoint, target)) ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordDelete, et, target))
} }
} }
@ -680,15 +679,15 @@ func (p *IBMCloudProvider) privateRecords(ctx context.Context) ([]*endpoint.Endp
return nil, err return nil, err
} }
// Filter VPC annoation for private zone active // Filter VPC annoation for private zone active
for _, source := range sources { for _, src := range sources {
vpc = checkVPCAnnotation(source) vpc = checkVPCAnnotation(src)
if len(vpc) > 0 { if len(vpc) > 0 {
log.Debugf("VPC found: %s", vpc) log.Debugf("VPC found: %s", vpc)
break break
} }
} }
endpoints := []*endpoint.Endpoint{} var endpoints []*endpoint.Endpoint
for _, zone := range zones { for _, zone := range zones {
if len(vpc) > 0 && *zone.State == zoneStatePendingNetwork { if len(vpc) > 0 && *zone.State == zoneStatePendingNetwork {
log.Debugf("active zone: %s", *zone.ID) log.Debugf("active zone: %s", *zone.ID)
@ -730,7 +729,7 @@ GETRECORDS:
} }
func (p *IBMCloudProvider) groupPrivateRecords(records []dnssvcsv1.ResourceRecord) []*endpoint.Endpoint { func (p *IBMCloudProvider) groupPrivateRecords(records []dnssvcsv1.ResourceRecord) []*endpoint.Endpoint {
endpoints := []*endpoint.Endpoint{} var endpoints []*endpoint.Endpoint
// group supported records by name and type // group supported records by name and type
groups := map[string][]dnssvcsv1.ResourceRecord{} groups := map[string][]dnssvcsv1.ResourceRecord{}
for _, r := range records { for _, r := range records {
@ -806,7 +805,7 @@ func (p *IBMCloudProvider) getPrivateRecordID(records []dnssvcsv1.ResourceRecord
} }
func (p *IBMCloudProvider) newIBMCloudChange(action string, endpoint *endpoint.Endpoint, target string) *ibmcloudChange { func (p *IBMCloudProvider) newIBMCloudChange(action string, endpoint *endpoint.Endpoint, target string) *ibmcloudChange {
ttl := defaultPublicRecordTTL ttl := defaultTTL
proxied := shouldBeProxied(endpoint, p.proxiedByDefault) proxied := shouldBeProxied(endpoint, p.proxiedByDefault)
if endpoint.RecordTTL.IsConfigured() { if endpoint.RecordTTL.IsConfigured() {
@ -984,7 +983,7 @@ func checkVPCAnnotation(endpoint *endpoint.Endpoint) string {
for _, v := range endpoint.ProviderSpecific { for _, v := range endpoint.ProviderSpecific {
if v.Name == vpcFilter { if v.Name == vpcFilter {
vpcCrn, err := crn.Parse(v.Value) vpcCrn, err := crn.Parse(v.Value)
if vpcCrn.ResourceType != "vpc" || err != nil { if err != nil || vpcCrn.ResourceType != "vpc" {
log.Errorf("Failed to parse vpc [%s]: %v", v.Value, err) log.Errorf("Failed to parse vpc [%s]: %v", v.Value, err)
} else { } else {
vpc = v.Value vpc = v.Value
@ -995,6 +994,7 @@ func checkVPCAnnotation(endpoint *endpoint.Endpoint) string {
return vpc return vpc
} }
// TODO: could be shared function
func isNil(i interface{}) bool { func isNil(i interface{}) bool {
if i == nil { if i == nil {
return true return true
@ -1002,6 +1002,7 @@ func isNil(i interface{}) bool {
switch reflect.TypeOf(i).Kind() { switch reflect.TypeOf(i).Kind() {
case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice:
return reflect.ValueOf(i).IsNil() return reflect.ValueOf(i).IsNil()
default:
return false
} }
return false
} }

View File

@ -96,15 +96,14 @@ func NewLinodeProvider(domainFilter endpoint.DomainFilter, dryRun bool) (*Linode
linodeClient := linodego.NewClient(oauth2Client) linodeClient := linodego.NewClient(oauth2Client)
linodeClient.SetUserAgent(fmt.Sprintf("%s linodego/%s", externaldns.UserAgent(), linodego.Version)) linodeClient.SetUserAgent(fmt.Sprintf("%s linodego/%s", externaldns.UserAgent(), linodego.Version))
provider := &LinodeProvider{ return &LinodeProvider{
Client: &linodeClient, Client: &linodeClient,
domainFilter: domainFilter, domainFilter: domainFilter,
DryRun: dryRun, DryRun: dryRun,
} }, nil
return provider, nil
} }
// Zones returns the list of hosted zones. // Zones return the list of hosted zones.
func (p *LinodeProvider) Zones(ctx context.Context) ([]linodego.Domain, error) { func (p *LinodeProvider) Zones(ctx context.Context) ([]linodego.Domain, error) {
zones, err := p.fetchZones(ctx) zones, err := p.fetchZones(ctx)
if err != nil { if err != nil {

View File

@ -40,8 +40,8 @@ const (
ns1Delete = "DELETE" ns1Delete = "DELETE"
// ns1Update is a ChangeAction enum value // ns1Update is a ChangeAction enum value
ns1Update = "UPDATE" ns1Update = "UPDATE"
// ns1DefaultTTL is the default ttl for ttls that are not set // defaultTTL is the default ttl for ttls that are not set
ns1DefaultTTL = 10 defaultTTL = 10
) )
// NS1DomainClient is a subset of the NS1 API the the provider uses, to ease testing // NS1DomainClient is a subset of the NS1 API the the provider uses, to ease testing
@ -136,13 +136,12 @@ func newNS1ProviderWithHTTPClient(config NS1Config, client *http.Client) (*NS1Pr
apiClient := api.NewClient(client, clientArgs...) apiClient := api.NewClient(client, clientArgs...)
provider := &NS1Provider{ return &NS1Provider{
client: NS1DomainService{apiClient}, client: NS1DomainService{apiClient},
domainFilter: config.DomainFilter, domainFilter: config.DomainFilter,
zoneIDFilter: config.ZoneIDFilter, zoneIDFilter: config.ZoneIDFilter,
minTTLSeconds: config.MinTTLSeconds, minTTLSeconds: config.MinTTLSeconds,
} }, nil
return provider, nil
} }
// Records returns the endpoints this provider knows about // Records returns the endpoints this provider knows about
@ -184,7 +183,7 @@ func (p *NS1Provider) ns1BuildRecord(zoneName string, change *ns1Change) *dns.Re
record.AddAnswer(dns.NewAnswer(strings.Split(v, " "))) record.AddAnswer(dns.NewAnswer(strings.Split(v, " ")))
} }
// set default ttl, but respect minTTLSeconds // set default ttl, but respect minTTLSeconds
ttl := ns1DefaultTTL ttl := defaultTTL
if p.minTTLSeconds > ttl { if p.minTTLSeconds > ttl {
ttl = p.minTTLSeconds ttl = p.minTTLSeconds
} }
@ -257,7 +256,7 @@ func (p *NS1Provider) zonesFiltered() ([]*dns.Zone, error) {
return nil, err return nil, err
} }
toReturn := []*dns.Zone{} var toReturn []*dns.Zone
for _, z := range zones { for _, z := range zones {
if p.domainFilter.Match(z.Zone) && p.zoneIDFilter.Match(z.ID) { if p.domainFilter.Match(z.Zone) && p.zoneIDFilter.Match(z.ID) {
@ -292,10 +291,10 @@ func (p *NS1Provider) ApplyChanges(ctx context.Context, changes *plan.Changes) e
func newNS1Changes(action string, endpoints []*endpoint.Endpoint) []*ns1Change { func newNS1Changes(action string, endpoints []*endpoint.Endpoint) []*ns1Change {
changes := make([]*ns1Change, 0, len(endpoints)) changes := make([]*ns1Change, 0, len(endpoints))
for _, endpoint := range endpoints { for _, ep := range endpoints {
changes = append(changes, &ns1Change{ changes = append(changes, &ns1Change{
Action: action, Action: action,
Endpoint: endpoint, Endpoint: ep,
}, },
) )
} }

View File

@ -18,16 +18,16 @@ package oci
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"time" "time"
yaml "github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/common/auth" "github.com/oracle/oci-go-sdk/v65/common/auth"
"github.com/oracle/oci-go-sdk/v65/dns" "github.com/oracle/oci-go-sdk/v65/dns"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
@ -35,7 +35,7 @@ import (
"sigs.k8s.io/external-dns/provider" "sigs.k8s.io/external-dns/provider"
) )
const ociRecordTTL = 300 const defaultTTL = 300
// OCIAuthConfig holds connection parameters for the OCI API. // OCIAuthConfig holds connection parameters for the OCI API.
type OCIAuthConfig struct { type OCIAuthConfig struct {
@ -153,7 +153,7 @@ func (p *OCIProvider) zones(ctx context.Context) (map[string]dns.ZoneSummary, er
} }
zones := make(map[string]dns.ZoneSummary) zones := make(map[string]dns.ZoneSummary)
scopes := []dns.GetZoneScopeEnum{dns.GetZoneScopeEnum(p.zoneScope)} scopes := []dns.GetZoneScopeEnum{dns.GetZoneScopeEnum(p.zoneScope)}
// If zone scope is empty, list all zones types. // If the zone scope is empty, list all zones types.
if p.zoneScope == "" { if p.zoneScope == "" {
scopes = dns.GetGetZoneScopeEnumValues() scopes = dns.GetGetZoneScopeEnumValues()
} }
@ -186,13 +186,13 @@ func mergeEndpointsMultiTargets(endpoints []*endpoint.Endpoint) []*endpoint.Endp
// Otherwise, create a new list of endpoints with the consolidated targets. // Otherwise, create a new list of endpoints with the consolidated targets.
var mergedEndpoints []*endpoint.Endpoint var mergedEndpoints []*endpoint.Endpoint
for _, endpoints := range endpointsByNameType { for _, ep := range endpointsByNameType {
dnsName := endpoints[0].DNSName dnsName := ep[0].DNSName
recordType := endpoints[0].RecordType recordType := ep[0].RecordType
recordTTL := endpoints[0].RecordTTL recordTTL := ep[0].RecordTTL
targets := make([]string, len(endpoints)) targets := make([]string, len(ep))
for i, e := range endpoints { for i, e := range ep {
targets[i] = e.Targets[0] targets[i] = e.Targets[0]
} }
@ -232,7 +232,7 @@ func (p *OCIProvider) addPaginatedZones(ctx context.Context, zones map[string]dn
} }
func (p *OCIProvider) newFilteredRecordOperations(endpoints []*endpoint.Endpoint, opType dns.RecordOperationOperationEnum) []dns.RecordOperation { func (p *OCIProvider) newFilteredRecordOperations(endpoints []*endpoint.Endpoint, opType dns.RecordOperationOperationEnum) []dns.RecordOperation {
ops := []dns.RecordOperation{} var ops []dns.RecordOperation
for _, ep := range endpoints { for _, ep := range endpoints {
if ep == nil { if ep == nil {
continue continue
@ -261,7 +261,7 @@ func (p *OCIProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error)
return nil, provider.NewSoftError(fmt.Errorf("getting zones: %w", err)) return nil, provider.NewSoftError(fmt.Errorf("getting zones: %w", err))
} }
endpoints := []*endpoint.Endpoint{} var endpoints []*endpoint.Endpoint
for _, zone := range zones { for _, zone := range zones {
var page *string var page *string
for { for {
@ -303,7 +303,7 @@ func (p *OCIProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error)
func (p *OCIProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { func (p *OCIProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
log.Debugf("Processing changes: %+v", changes) log.Debugf("Processing changes: %+v", changes)
ops := []dns.RecordOperation{} var ops []dns.RecordOperation
ops = append(ops, p.newFilteredRecordOperations(changes.Create, dns.RecordOperationOperationAdd)...) ops = append(ops, p.newFilteredRecordOperations(changes.Create, dns.RecordOperationOperationAdd)...)
ops = append(ops, p.newFilteredRecordOperations(changes.UpdateNew, dns.RecordOperationOperationAdd)...) ops = append(ops, p.newFilteredRecordOperations(changes.UpdateNew, dns.RecordOperationOperationAdd)...)
@ -349,7 +349,7 @@ func (p *OCIProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) e
// AdjustEndpoints modifies the endpoints as needed by the specific provider // AdjustEndpoints modifies the endpoints as needed by the specific provider
func (p *OCIProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) { func (p *OCIProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
adjustedEndpoints := []*endpoint.Endpoint{} var adjustedEndpoints []*endpoint.Endpoint
for _, e := range endpoints { for _, e := range endpoints {
// OCI DNS does not support the set-identifier attribute, so we remove it to avoid plan failure // OCI DNS does not support the set-identifier attribute, so we remove it to avoid plan failure
if e.SetIdentifier != "" { if e.SetIdentifier != "" {
@ -370,7 +370,7 @@ func newRecordOperation(ep *endpoint.Endpoint, opType dns.RecordOperationOperati
} }
rdata := strings.Join(targets, " ") rdata := strings.Join(targets, " ")
ttl := ociRecordTTL ttl := defaultTTL
if ep.RecordTTL.IsConfigured() { if ep.RecordTTL.IsConfigured() {
ttl = int(ep.RecordTTL) ttl = int(ep.RecordTTL)
} }

View File

@ -18,6 +18,8 @@ package oci
import ( import (
"context" "context"
"errors"
"fmt"
"sort" "sort"
"strings" "strings"
"testing" "testing"
@ -25,7 +27,6 @@ import (
"github.com/oracle/oci-go-sdk/v65/common" "github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/dns" "github.com/oracle/oci-go-sdk/v65/dns"
"github.com/pkg/errors"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
@ -93,12 +94,12 @@ func (c *mockOCIDNSClient) GetZoneRecords(ctx context.Context, request dns.GetZo
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("127.0.0.1"), Rdata: common.String("127.0.0.1"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}, { }, {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"), Rdata: common.String("heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"),
Rtype: common.String(endpoint.RecordTypeTXT), Rtype: common.String(endpoint.RecordTypeTXT),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}} }}
response.OpcNextPage = common.String("1") response.OpcNextPage = common.String("1")
} else { } else {
@ -106,7 +107,7 @@ func (c *mockOCIDNSClient) GetZoneRecords(ctx context.Context, request dns.GetZo
Domain: common.String("bar.foo.com"), Domain: common.String("bar.foo.com"),
Rdata: common.String("bar.com."), Rdata: common.String("bar.com."),
Rtype: common.String(endpoint.RecordTypeCNAME), Rtype: common.String(endpoint.RecordTypeCNAME),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}} }}
} }
case "ocid1.dns-zone.oc1..502aeddba262b92fd13ed7874f6f1404": case "ocid1.dns-zone.oc1..502aeddba262b92fd13ed7874f6f1404":
@ -115,7 +116,7 @@ func (c *mockOCIDNSClient) GetZoneRecords(ctx context.Context, request dns.GetZo
Domain: common.String("foo.bar.com"), Domain: common.String("foo.bar.com"),
Rdata: common.String("127.0.0.1"), Rdata: common.String("127.0.0.1"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}} }}
} }
} }
@ -329,26 +330,26 @@ func TestOCIRecords(t *testing.T) {
domainFilter: endpoint.NewDomainFilter([]string{""}), domainFilter: endpoint.NewDomainFilter([]string{""}),
zoneIDFilter: provider.NewZoneIDFilter([]string{""}), zoneIDFilter: provider.NewZoneIDFilter([]string{""}),
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("foo.foo.com", endpoint.RecordTypeA, endpoint.TTL(ociRecordTTL), "127.0.0.1"), endpoint.NewEndpointWithTTL("foo.foo.com", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "127.0.0.1"),
endpoint.NewEndpointWithTTL("foo.foo.com", endpoint.RecordTypeTXT, endpoint.TTL(ociRecordTTL), "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"), endpoint.NewEndpointWithTTL("foo.foo.com", endpoint.RecordTypeTXT, endpoint.TTL(defaultTTL), "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"),
endpoint.NewEndpointWithTTL("bar.foo.com", endpoint.RecordTypeCNAME, endpoint.TTL(ociRecordTTL), "bar.com."), endpoint.NewEndpointWithTTL("bar.foo.com", endpoint.RecordTypeCNAME, endpoint.TTL(defaultTTL), "bar.com."),
endpoint.NewEndpointWithTTL("foo.bar.com", endpoint.RecordTypeA, endpoint.TTL(ociRecordTTL), "127.0.0.1"), endpoint.NewEndpointWithTTL("foo.bar.com", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "127.0.0.1"),
}, },
}, { }, {
name: "DomainFilter_foo.com", name: "DomainFilter_foo.com",
domainFilter: endpoint.NewDomainFilter([]string{"foo.com"}), domainFilter: endpoint.NewDomainFilter([]string{"foo.com"}),
zoneIDFilter: provider.NewZoneIDFilter([]string{""}), zoneIDFilter: provider.NewZoneIDFilter([]string{""}),
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("foo.foo.com", endpoint.RecordTypeA, endpoint.TTL(ociRecordTTL), "127.0.0.1"), endpoint.NewEndpointWithTTL("foo.foo.com", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "127.0.0.1"),
endpoint.NewEndpointWithTTL("foo.foo.com", endpoint.RecordTypeTXT, endpoint.TTL(ociRecordTTL), "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"), endpoint.NewEndpointWithTTL("foo.foo.com", endpoint.RecordTypeTXT, endpoint.TTL(defaultTTL), "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"),
endpoint.NewEndpointWithTTL("bar.foo.com", endpoint.RecordTypeCNAME, endpoint.TTL(ociRecordTTL), "bar.com."), endpoint.NewEndpointWithTTL("bar.foo.com", endpoint.RecordTypeCNAME, endpoint.TTL(defaultTTL), "bar.com."),
}, },
}, { }, {
name: "ZoneIDFilter_ocid1.dns-zone.oc1..502aeddba262b92fd13ed7874f6f1404", name: "ZoneIDFilter_ocid1.dns-zone.oc1..502aeddba262b92fd13ed7874f6f1404",
domainFilter: endpoint.NewDomainFilter([]string{""}), domainFilter: endpoint.NewDomainFilter([]string{""}),
zoneIDFilter: provider.NewZoneIDFilter([]string{"ocid1.dns-zone.oc1..502aeddba262b92fd13ed7874f6f1404"}), zoneIDFilter: provider.NewZoneIDFilter([]string{"ocid1.dns-zone.oc1..502aeddba262b92fd13ed7874f6f1404"}),
expected: []*endpoint.Endpoint{ expected: []*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("foo.bar.com", endpoint.RecordTypeA, endpoint.TTL(ociRecordTTL), "127.0.0.1"), endpoint.NewEndpointWithTTL("foo.bar.com", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "127.0.0.1"),
}, },
}, },
} }
@ -375,7 +376,7 @@ func TestNewRecordOperation(t *testing.T) {
ep: endpoint.NewEndpointWithTTL( ep: endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1"), "127.0.0.1"),
expected: dns.RecordOperation{ expected: dns.RecordOperation{
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
@ -390,7 +391,7 @@ func TestNewRecordOperation(t *testing.T) {
ep: endpoint.NewEndpointWithTTL( ep: endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeTXT, endpoint.RecordTypeTXT,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"), "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"),
expected: dns.RecordOperation{ expected: dns.RecordOperation{
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
@ -405,7 +406,7 @@ func TestNewRecordOperation(t *testing.T) {
ep: endpoint.NewEndpointWithTTL( ep: endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeCNAME, endpoint.RecordTypeCNAME,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"bar.com."), "bar.com."),
expected: dns.RecordOperation{ expected: dns.RecordOperation{
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
@ -621,7 +622,7 @@ func (c *mutableMockOCIDNSClient) PatchZoneRecords(ctx context.Context, request
case dns.RecordOperationOperationRemove: case dns.RecordOperationOperationRemove:
delete(records, k) delete(records, k)
default: default:
err = errors.Errorf("unsupported operation %q", op.Operation) err = fmt.Errorf("unsupported operation %q", op.Operation)
return return
} }
} }
@ -640,12 +641,12 @@ func TestMutableMockOCIDNSClient(t *testing.T) {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("127.0.0.1"), Rdata: common.String("127.0.0.1"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}, { }, {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"), Rdata: common.String("heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"),
Rtype: common.String(endpoint.RecordTypeTXT), Rtype: common.String(endpoint.RecordTypeTXT),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}}, }},
} }
client := newMutableMockOCIDNSClient(zones, records) client := newMutableMockOCIDNSClient(zones, records)
@ -732,14 +733,14 @@ func TestOCIApplyChanges(t *testing.T) {
Create: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( Create: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
}, },
expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
}, { }, {
@ -753,26 +754,26 @@ func TestOCIApplyChanges(t *testing.T) {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("127.0.0.1"), Rdata: common.String("127.0.0.1"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}, { }, {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"), Rdata: common.String("heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/my-svc"),
Rtype: common.String(endpoint.RecordTypeTXT), Rtype: common.String(endpoint.RecordTypeTXT),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}}, }},
}, },
changes: &plan.Changes{ changes: &plan.Changes{
Delete: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( Delete: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeTXT, endpoint.RecordTypeTXT,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
}, },
expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
}, { }, {
@ -786,27 +787,27 @@ func TestOCIApplyChanges(t *testing.T) {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("127.0.0.1"), Rdata: common.String("127.0.0.1"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}}, }},
}, },
changes: &plan.Changes{ changes: &plan.Changes{
UpdateOld: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( UpdateOld: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
UpdateNew: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( UpdateNew: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"10.0.0.1", "10.0.0.1",
)}, )},
}, },
expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"10.0.0.1", "10.0.0.1",
)}, )},
}, { }, {
@ -820,14 +821,14 @@ func TestOCIApplyChanges(t *testing.T) {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("127.0.0.1"), Rdata: common.String("127.0.0.1"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}}, }},
}, },
changes: &plan.Changes{ changes: &plan.Changes{
Delete: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( Delete: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
}, },
@ -835,7 +836,7 @@ func TestOCIApplyChanges(t *testing.T) {
expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
}, { }, {
@ -849,42 +850,42 @@ func TestOCIApplyChanges(t *testing.T) {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("127.0.0.1"), Rdata: common.String("127.0.0.1"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}, { }, {
Domain: common.String("car.foo.com"), Domain: common.String("car.foo.com"),
Rdata: common.String("bar.com."), Rdata: common.String("bar.com."),
Rtype: common.String(endpoint.RecordTypeCNAME), Rtype: common.String(endpoint.RecordTypeCNAME),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}, { }, {
Domain: common.String("bar.foo.com"), Domain: common.String("bar.foo.com"),
Rdata: common.String("baz.com."), Rdata: common.String("baz.com."),
Rtype: common.String(endpoint.RecordTypeCNAME), Rtype: common.String(endpoint.RecordTypeCNAME),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}}, }},
}, },
changes: &plan.Changes{ changes: &plan.Changes{
Delete: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( Delete: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
UpdateOld: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( UpdateOld: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"car.foo.com", "car.foo.com",
endpoint.RecordTypeCNAME, endpoint.RecordTypeCNAME,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"baz.com.", "baz.com.",
)}, )},
UpdateNew: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( UpdateNew: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"bar.foo.com", "bar.foo.com",
endpoint.RecordTypeCNAME, endpoint.RecordTypeCNAME,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"foo.bar.com.", "foo.bar.com.",
)}, )},
Create: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( Create: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"baz.foo.com", "baz.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1", "127.0.0.1",
)}, )},
}, },
@ -892,13 +893,13 @@ func TestOCIApplyChanges(t *testing.T) {
endpoint.NewEndpointWithTTL( endpoint.NewEndpointWithTTL(
"bar.foo.com", "bar.foo.com",
endpoint.RecordTypeCNAME, endpoint.RecordTypeCNAME,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"foo.bar.com.", "foo.bar.com.",
), ),
endpoint.NewEndpointWithTTL( endpoint.NewEndpointWithTTL(
"baz.foo.com", "baz.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"127.0.0.1"), "127.0.0.1"),
}, },
}, },
@ -913,19 +914,19 @@ func TestOCIApplyChanges(t *testing.T) {
Create: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( Create: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"192.168.1.2", "192.168.1.2",
), endpoint.NewEndpointWithTTL( ), endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"192.168.2.5", "192.168.2.5",
)}, )},
}, },
expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), "192.168.1.2", "192.168.2.5", endpoint.TTL(defaultTTL), "192.168.1.2", "192.168.2.5",
)}, )},
}, },
{ {
@ -939,26 +940,26 @@ func TestOCIApplyChanges(t *testing.T) {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("192.168.1.2"), Rdata: common.String("192.168.1.2"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}, { }, {
Domain: common.String("foo.foo.com"), Domain: common.String("foo.foo.com"),
Rdata: common.String("192.168.2.5"), Rdata: common.String("192.168.2.5"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}}, }},
}, },
changes: &plan.Changes{ changes: &plan.Changes{
Delete: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( Delete: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"192.168.1.2", "192.168.1.2",
)}, )},
}, },
expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"foo.foo.com", "foo.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), "192.168.2.5", endpoint.TTL(defaultTTL), "192.168.2.5",
)}, )},
}, },
{ {
@ -972,27 +973,27 @@ func TestOCIApplyChanges(t *testing.T) {
Domain: common.String("first.foo.com"), Domain: common.String("first.foo.com"),
Rdata: common.String("10.77.4.5"), Rdata: common.String("10.77.4.5"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}}, }},
}, },
changes: &plan.Changes{ changes: &plan.Changes{
UpdateOld: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( UpdateOld: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"first.foo.com", "first.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"10.77.4.5", "10.77.4.5",
)}, )},
UpdateNew: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( UpdateNew: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"first.foo.com", "first.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"10.77.6.10", "10.77.6.10",
)}, )},
}, },
expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"first.foo.com", "first.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"10.77.6.10", "10.77.6.10",
)}, )},
}, },
@ -1007,21 +1008,21 @@ func TestOCIApplyChanges(t *testing.T) {
Domain: common.String("first.foo.com"), Domain: common.String("first.foo.com"),
Rdata: common.String("10.77.4.5"), Rdata: common.String("10.77.4.5"),
Rtype: common.String(endpoint.RecordTypeA), Rtype: common.String(endpoint.RecordTypeA),
Ttl: common.Int(ociRecordTTL), Ttl: common.Int(defaultTTL),
}}, }},
}, },
changes: &plan.Changes{ changes: &plan.Changes{
Create: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( Create: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"first.foo.com", "first.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"10.77.6.10", "10.77.6.10",
)}, )},
}, },
expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL( expectedEndpoints: []*endpoint.Endpoint{endpoint.NewEndpointWithTTL(
"first.foo.com", "first.foo.com",
endpoint.RecordTypeA, endpoint.RecordTypeA,
endpoint.TTL(ociRecordTTL), endpoint.TTL(defaultTTL),
"10.77.4.5", "10.77.6.10", "10.77.4.5", "10.77.6.10",
)}, )},
}, },

View File

@ -41,8 +41,8 @@ import (
) )
const ( const (
ovhDefaultTTL = 0 defaultTTL = 0
ovhCreate = iota ovhCreate = iota
ovhDelete ovhDelete
ovhUpdate ovhUpdate
) )
@ -324,9 +324,9 @@ func (p *OVHProvider) change(ctx context.Context, change ovhChange) error {
return nil return nil
} }
return p.client.PutWithContext(ctx, fmt.Sprintf("/domain/zone/%s/record/%d", url.PathEscape(change.Zone), change.ID), change.ovhRecordFieldUpdate, nil) return p.client.PutWithContext(ctx, fmt.Sprintf("/domain/zone/%s/record/%d", url.PathEscape(change.Zone), change.ID), change.ovhRecordFieldUpdate, nil)
default:
return nil
} }
return nil
} }
func (p *OVHProvider) invalidateCache(zone string) { func (p *OVHProvider) invalidateCache(zone string) {
@ -357,8 +357,8 @@ func (p *OVHProvider) zonesRecords(ctx context.Context) ([]string, []ovhRecord,
} }
func (p *OVHProvider) zones(ctx context.Context) ([]string, error) { func (p *OVHProvider) zones(ctx context.Context) ([]string, error) {
zones := []string{} var zones []string
filteredZones := []string{} var filteredZones []string
p.apiRateLimiter.Take() p.apiRateLimiter.Take()
if err := p.client.GetWithContext(ctx, "/domain/zone", &zones); err != nil { if err := p.client.GetWithContext(ctx, "/domain/zone", &zones); err != nil {
@ -476,25 +476,25 @@ func ovhGroupByNameAndType(records []ovhRecord) []*endpoint.Endpoint {
// create single endpoint with all the targets for each name/type // create single endpoint with all the targets for each name/type
for _, records := range groups { for _, records := range groups {
targets := []string{} var targets []string
for _, record := range records { for _, record := range records {
targets = append(targets, record.Target) targets = append(targets, record.Target)
} }
endpoint := endpoint.NewEndpointWithTTL( ep := endpoint.NewEndpointWithTTL(
strings.TrimPrefix(records[0].SubDomain+"."+records[0].Zone, "."), strings.TrimPrefix(records[0].SubDomain+"."+records[0].Zone, "."),
records[0].FieldType, records[0].FieldType,
endpoint.TTL(records[0].TTL), endpoint.TTL(records[0].TTL),
targets..., targets...,
) )
endpoints = append(endpoints, endpoint) endpoints = append(endpoints, ep)
} }
return endpoints return endpoints
} }
func (p OVHProvider) newOvhChangeCreateDelete(action int, endpoints []*endpoint.Endpoint, zone string, existingRecords []ovhRecord) ([]ovhChange, []ovhRecord) { func (p OVHProvider) newOvhChangeCreateDelete(action int, endpoints []*endpoint.Endpoint, zone string, existingRecords []ovhRecord) ([]ovhChange, []ovhRecord) {
ovhChanges := []ovhChange{} var ovhChanges []ovhChange
toDeleteIds := []int{} var toDeleteIds []int
for _, e := range endpoints { for _, e := range endpoints {
for _, target := range e.Targets { for _, target := range e.Targets {
@ -506,7 +506,7 @@ func (p OVHProvider) newOvhChangeCreateDelete(action int, endpoints []*endpoint.
FieldType: e.RecordType, FieldType: e.RecordType,
ovhRecordFieldUpdate: ovhRecordFieldUpdate{ ovhRecordFieldUpdate: ovhRecordFieldUpdate{
SubDomain: convertDNSNameIntoSubDomain(e.DNSName, zone), SubDomain: convertDNSNameIntoSubDomain(e.DNSName, zone),
TTL: ovhDefaultTTL, TTL: defaultTTL,
Target: target, Target: target,
}, },
}, },
@ -579,13 +579,13 @@ func (p OVHProvider) newOvhChangeUpdate(endpointsOld []*endpoint.Endpoint, endpo
} }
} }
changes := []ovhChange{} var changes []ovhChange
for id := range oldEndpointByTypeAndName { for id := range oldEndpointByTypeAndName {
oldRecords := slices.Clone(oldRecordsInZone[id]) oldRecords := slices.Clone(oldRecordsInZone[id])
endpointsNew := newEndpointByTypeAndName[id] endpointsNew := newEndpointByTypeAndName[id]
toInsertTarget := []string{} var toInsertTarget []string
for _, target := range endpointsNew.Targets { for _, target := range endpointsNew.Targets {
var toDelete = -1 var toDelete = -1
@ -617,7 +617,7 @@ func (p OVHProvider) newOvhChangeUpdate(endpointsOld []*endpoint.Endpoint, endpo
if endpointsNew.RecordTTL.IsConfigured() { if endpointsNew.RecordTTL.IsConfigured() {
record.TTL = int64(endpointsNew.RecordTTL) record.TTL = int64(endpointsNew.RecordTTL)
} else { } else {
record.TTL = ovhDefaultTTL record.TTL = defaultTTL
} }
change := ovhChange{ change := ovhChange{
@ -634,7 +634,7 @@ func (p OVHProvider) newOvhChangeUpdate(endpointsOld []*endpoint.Endpoint, endpo
if len(toInsertTarget) > 0 { if len(toInsertTarget) > 0 {
for _, target := range toInsertTarget { for _, target := range toInsertTarget {
recordTTL := int64(ovhDefaultTTL) recordTTL := int64(defaultTTL)
if endpointsNew.RecordTTL.IsConfigured() { if endpointsNew.RecordTTL.IsConfigured() {
recordTTL = int64(endpointsNew.RecordTTL) recordTTL = int64(endpointsNew.RecordTTL)
} }
@ -680,6 +680,8 @@ func (c *ovhChange) String() string {
action = "update" action = "update"
case ovhDelete: case ovhDelete:
action = "delete" action = "delete"
default:
action = "unknown"
} }
if c.ID != 0 { if c.ID != 0 {

View File

@ -371,8 +371,8 @@ func TestOvhNewChange(t *testing.T) {
changes, _ := provider.newOvhChangeCreateDelete(ovhCreate, endpoints, "example.net", []ovhRecord{}) changes, _ := provider.newOvhChangeCreateDelete(ovhCreate, endpoints, "example.net", []ovhRecord{})
td.Cmp(t, changes, []ovhChange{ td.Cmp(t, changes, []ovhChange{
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "", TTL: 10, Target: "203.0.113.42"}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "", TTL: 10, Target: "203.0.113.42"}}}},
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: ovhDefaultTTL, Target: "203.0.113.43"}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: defaultTTL, Target: "203.0.113.43"}}}},
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "CNAME", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh2", TTL: ovhDefaultTTL, Target: "ovh.example.net."}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "CNAME", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh2", TTL: defaultTTL, Target: "ovh.example.net."}}}},
}) })
// Delete change // Delete change
@ -386,9 +386,9 @@ func TestOvhNewChange(t *testing.T) {
} }
changes, _ = provider.newOvhChangeCreateDelete(ovhDelete, endpoints, "example.net", records) changes, _ = provider.newOvhChangeCreateDelete(ovhDelete, endpoints, "example.net", records)
td.Cmp(t, changes, []ovhChange{ td.Cmp(t, changes, []ovhChange{
{Action: ovhDelete, ovhRecord: ovhRecord{ID: 42, Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: ovhDefaultTTL, Target: "203.0.113.42"}}}}, {Action: ovhDelete, ovhRecord: ovhRecord{ID: 42, Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: defaultTTL, Target: "203.0.113.42"}}}},
{Action: ovhDelete, ovhRecord: ovhRecord{ID: 43, Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: ovhDefaultTTL, Target: "203.0.113.42"}}}}, {Action: ovhDelete, ovhRecord: ovhRecord{ID: 43, Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: defaultTTL, Target: "203.0.113.42"}}}},
{Action: ovhDelete, ovhRecord: ovhRecord{ID: 44, Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: ovhDefaultTTL, Target: "203.0.113.42"}}}}, {Action: ovhDelete, ovhRecord: ovhRecord{ID: 44, Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: defaultTTL, Target: "203.0.113.42"}}}},
}) })
// Create change with CNAME relative // Create change with CNAME relative
@ -403,8 +403,8 @@ func TestOvhNewChange(t *testing.T) {
changes, _ = provider.newOvhChangeCreateDelete(ovhCreate, endpoints, "example.net", []ovhRecord{}) changes, _ = provider.newOvhChangeCreateDelete(ovhCreate, endpoints, "example.net", []ovhRecord{})
td.Cmp(t, changes, []ovhChange{ td.Cmp(t, changes, []ovhChange{
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "", TTL: 10, Target: "203.0.113.42"}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "", TTL: 10, Target: "203.0.113.42"}}}},
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: ovhDefaultTTL, Target: "203.0.113.43"}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: defaultTTL, Target: "203.0.113.43"}}}},
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "CNAME", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh2", TTL: ovhDefaultTTL, Target: "ovh"}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "CNAME", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh2", TTL: defaultTTL, Target: "ovh"}}}},
}) })
// Test with CNAME when target has already final dot // Test with CNAME when target has already final dot
@ -419,8 +419,8 @@ func TestOvhNewChange(t *testing.T) {
changes, _ = provider.newOvhChangeCreateDelete(ovhCreate, endpoints, "example.net", []ovhRecord{}) changes, _ = provider.newOvhChangeCreateDelete(ovhCreate, endpoints, "example.net", []ovhRecord{})
td.Cmp(t, changes, []ovhChange{ td.Cmp(t, changes, []ovhChange{
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "", TTL: 10, Target: "203.0.113.42"}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "", TTL: 10, Target: "203.0.113.42"}}}},
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: ovhDefaultTTL, Target: "203.0.113.43"}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "A", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh", TTL: defaultTTL, Target: "203.0.113.43"}}}},
{Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "CNAME", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh2", TTL: ovhDefaultTTL, Target: "ovh.example.com."}}}}, {Action: ovhCreate, ovhRecord: ovhRecord{Zone: "example.net", ovhRecordFields: ovhRecordFields{FieldType: "CNAME", ovhRecordFieldUpdate: ovhRecordFieldUpdate{SubDomain: "ovh2", TTL: defaultTTL, Target: "ovh.example.com."}}}},
}) })
} }

View File

@ -25,6 +25,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/netip"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -63,6 +64,7 @@ func newPiholeClientV6(cfg PiholeConfig) (piholeAPI, error) {
}, },
}, },
} }
cl := instrumented_http.NewClient(httpClient, &instrumented_http.Callbacks{}) cl := instrumented_http.NewClient(httpClient, &instrumented_http.Callbacks{})
p := &piholeClientV6{ p := &piholeClientV6{
@ -114,6 +116,32 @@ func (p *piholeClientV6) getConfigValue(ctx context.Context, rtype string) ([]st
return results, nil return results, nil
} }
/**
* isValidIPv4 checks if the given IP address is a valid IPv4 address.
* It returns true if the IP address is valid, false otherwise.
* If the IP address is in IPv6 format, it will return false.
*/
func isValidIPv4(ip string) bool {
addr, err := netip.ParseAddr(ip)
if err != nil {
return false
}
return addr.Is4()
}
/**
* isValidIPv6 checks if the given IP address is a valid IPv6 address.
* It returns true if the IP address is valid, false otherwise.
* If the IP address is in IPv6 with dual format y:y:y:y:y:y:x.x.x.x. , it will return true.
*/
func isValidIPv6(ip string) bool {
addr, err := netip.ParseAddr(ip)
if err != nil {
return false
}
return addr.Is6()
}
func (p *piholeClientV6) listRecords(ctx context.Context, rtype string) ([]*endpoint.Endpoint, error) { func (p *piholeClientV6) listRecords(ctx context.Context, rtype string) ([]*endpoint.Endpoint, error) {
out := make([]*endpoint.Endpoint, 0) out := make([]*endpoint.Endpoint, 0)
results, err := p.getConfigValue(ctx, rtype) results, err := p.getConfigValue(ctx, rtype)
@ -126,42 +154,39 @@ func (p *piholeClientV6) listRecords(ctx context.Context, rtype string) ([]*endp
return r == ' ' || r == ',' return r == ' ' || r == ','
}) })
if len(recs) < 2 { if len(recs) < 2 {
log.Warnf("skipping record %s: invalid format", rec) log.Warnf("skipping record %s: invalid format received from PiHole", rec)
continue continue
} }
var DNSName, Target string var DNSName, Target string
var Ttl endpoint.TTL = 0 var Ttl = endpoint.TTL(0)
// A/AAAA record format is target(IP) DNSName // A/AAAA record format is target(IP) DNSName
DNSName, Target = recs[1], recs[0] DNSName, Target = recs[1], recs[0]
switch rtype { switch rtype {
case endpoint.RecordTypeA: case endpoint.RecordTypeA:
if strings.Contains(Target, ":") { //PiHole return A and AAAA records. Filter to only keep the A records
if !isValidIPv4(Target) {
continue continue
} }
case endpoint.RecordTypeAAAA: case endpoint.RecordTypeAAAA:
if strings.Contains(Target, ".") { //PiHole return A and AAAA records. Filter to only keep the AAAA records
if !isValidIPv6(Target) {
continue continue
} }
case endpoint.RecordTypeCNAME: case endpoint.RecordTypeCNAME:
// CNAME format is DNSName,target //PiHole return only CNAME records.
// CNAME format is DNSName,target, ttl?
DNSName, Target = recs[0], recs[1] DNSName, Target = recs[0], recs[1]
if len(recs) == 3 { // TTL is present if len(recs) == 3 { // TTL is present
// Parse string to int64 first // Parse string to int64 first
if ttlInt, err := strconv.ParseInt(recs[2], 10, 64); err == nil { if ttlInt, err := strconv.ParseInt(recs[2], 10, 64); err == nil {
Ttl = endpoint.TTL(ttlInt) Ttl = endpoint.TTL(ttlInt)
} else { } else {
log.Warnf("failed to parse TTL value '%s': %v; using a TTL of %d", recs[2], err, Ttl) log.Warnf("failed to parse TTL value received from PiHole '%s': %v; using a TTL of %d", recs[2], err, Ttl)
} }
} }
} }
out = append(out, &endpoint.Endpoint{ out = append(out, endpoint.NewEndpointWithTTL(DNSName, rtype, Ttl, Target))
DNSName: DNSName,
Targets: []string{Target},
RecordTTL: Ttl,
RecordType: rtype,
})
} }
return out, nil return out, nil
} }
@ -375,7 +400,13 @@ func (p *piholeClientV6) do(req *http.Request) ([]byte, error) {
if err := json.Unmarshal(jRes, &apiError); err != nil { if err := json.Unmarshal(jRes, &apiError); err != nil {
return nil, fmt.Errorf("failed to unmarshal error response: %w", err) return nil, fmt.Errorf("failed to unmarshal error response: %w", err)
} }
log.Debugf("Error on request %s", req.Body) if log.IsLevelEnabled(log.DebugLevel) {
log.Debugf("Error on request %s", req.URL)
if req.Body != nil {
log.Debugf("Body of the request %s", req.Body)
}
}
if res.StatusCode == http.StatusUnauthorized && p.token != "" { if res.StatusCode == http.StatusUnauthorized && p.token != "" {
tryCount := 1 tryCount := 1
maxRetries := 3 maxRetries := 3

View File

@ -29,6 +29,62 @@ import (
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
) )
func TestIsValidIPv4(t *testing.T) {
tests := []struct {
ip string
expected bool
}{
{"192.168.1.1", true},
{"255.255.255.255", true},
{"0.0.0.0", true},
{"", false},
{"256.256.256.256", false},
{"192.168.0.1/22", false},
{"192.168.1", false},
{"abc.def.ghi.jkl", false},
{"::ffff:192.168.20.3", false},
}
for _, test := range tests {
t.Run(test.ip, func(t *testing.T) {
if got := isValidIPv4(test.ip); got != test.expected {
t.Errorf("isValidIPv4(%s) = %v; want %v", test.ip, got, test.expected)
}
})
}
}
func TestIsValidIPv6(t *testing.T) {
tests := []struct {
ip string
expected bool
}{
{"2001:0db8:85a3:0000:0000:8a2e:0370:7334", true},
{"2001:db8:85a3::8a2e:370:7334", true},
//IPV6 dual, the format is y:y:y:y:y:y:x.x.x.x.
{"::ffff:192.168.20.3", true},
{"::1", true},
{"::", true},
{"2001:db8::", true},
{"", false},
{":", false},
{"::ffff:", false},
{"192.168.20.3", false},
{"2001:db8:85a3:0:0:8a2e:370:7334:1234", false},
{"2001:db8:85a3::8a2e:370g:7334", false},
{"2001:db8:85a3::8a2e:370:7334::", false},
{"2001:db8:85a3::8a2e:370:7334::1", false},
}
for _, test := range tests {
t.Run(test.ip, func(t *testing.T) {
if got := isValidIPv6(test.ip); got != test.expected {
t.Errorf("isValidIPv6(%s) = %v; want %v", test.ip, got, test.expected)
}
})
}
}
func newTestServerV6(t *testing.T, hdlr http.HandlerFunc) *httptest.Server { func newTestServerV6(t *testing.T, hdlr http.HandlerFunc) *httptest.Server {
t.Helper() t.Helper()
svr := httptest.NewServer(hdlr) svr := httptest.NewServer(hdlr)
@ -137,7 +193,9 @@ func TestListRecordsV6(t *testing.T) {
"192.168.178.34 service3.example.com", "192.168.178.34 service3.example.com",
"fc00::1:192:168:1:1 service4.example.com", "fc00::1:192:168:1:1 service4.example.com",
"fc00::1:192:168:1:2 service5.example.com", "fc00::1:192:168:1:2 service5.example.com",
"fc00::1:192:168:1:3 service6.example.com" "fc00::1:192:168:1:3 service6.example.com",
"::ffff:192.168.20.3 service7.example.com",
"192.168.20.3 service7.example.com"
] ]
} }
}, },
@ -177,20 +235,22 @@ func TestListRecordsV6(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// Ensure A records were parsed correctly
expected := [][]string{
{"service1.example.com", "192.168.178.33"},
{"service2.example.com", "192.168.178.34"},
{"service3.example.com", "192.168.178.34"},
{"service7.example.com", "192.168.20.3"},
}
// Test retrieve A records unfiltered // Test retrieve A records unfiltered
arecs, err := cl.listRecords(context.Background(), endpoint.RecordTypeA) arecs, err := cl.listRecords(context.Background(), endpoint.RecordTypeA)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(arecs) != 3 { if len(arecs) != len(expected) {
t.Fatal("Expected 3 A records returned, got:", len(arecs)) t.Fatalf("Expected %d A records returned, got: %d", len(expected), len(arecs))
}
// Ensure records were parsed correctly
expected := [][]string{
{"service1.example.com", "192.168.178.33"},
{"service2.example.com", "192.168.178.34"},
{"service3.example.com", "192.168.178.34"},
} }
for idx, rec := range arecs { for idx, rec := range arecs {
if rec.DNSName != expected[idx][0] { if rec.DNSName != expected[idx][0] {
t.Error("Got invalid DNS Name:", rec.DNSName, "expected:", expected[idx][0]) t.Error("Got invalid DNS Name:", rec.DNSName, "expected:", expected[idx][0])
@ -200,20 +260,23 @@ func TestListRecordsV6(t *testing.T) {
} }
} }
// Ensure AAAA records were parsed correctly
expected = [][]string{
{"service4.example.com", "fc00::1:192:168:1:1"},
{"service5.example.com", "fc00::1:192:168:1:2"},
{"service6.example.com", "fc00::1:192:168:1:3"},
{"service7.example.com", "::ffff:192.168.20.3"},
}
// Test retrieve AAAA records unfiltered // Test retrieve AAAA records unfiltered
arecs, err = cl.listRecords(context.Background(), endpoint.RecordTypeAAAA) arecs, err = cl.listRecords(context.Background(), endpoint.RecordTypeAAAA)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(arecs) != 3 {
t.Fatal("Expected 3 AAAA records returned, got:", len(arecs)) if len(arecs) != len(expected) {
} t.Fatalf("Expected %d AAAA records returned, got: %d", len(expected), len(arecs))
// Ensure records were parsed correctly
expected = [][]string{
{"service4.example.com", "fc00::1:192:168:1:1"},
{"service5.example.com", "fc00::1:192:168:1:2"},
{"service6.example.com", "fc00::1:192:168:1:3"},
} }
for idx, rec := range arecs { for idx, rec := range arecs {
if rec.DNSName != expected[idx][0] { if rec.DNSName != expected[idx][0] {
t.Error("Got invalid DNS Name:", rec.DNSName, "expected:", expected[idx][0]) t.Error("Got invalid DNS Name:", rec.DNSName, "expected:", expected[idx][0])
@ -223,20 +286,22 @@ func TestListRecordsV6(t *testing.T) {
} }
} }
// Ensure CNAME records were parsed correctly
expected = [][]string{
{"source1.example.com", "target1.domain.com", "1000"},
{"source2.example.com", "target2.domain.com", "50"},
{"source3.example.com", "target3.domain.com"},
}
// Test retrieve CNAME records unfiltered // Test retrieve CNAME records unfiltered
cnamerecs, err := cl.listRecords(context.Background(), endpoint.RecordTypeCNAME) cnamerecs, err := cl.listRecords(context.Background(), endpoint.RecordTypeCNAME)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(cnamerecs) != 3 { if len(cnamerecs) != len(expected) {
t.Fatal("Expected 3 CAME records returned, got:", len(cnamerecs)) t.Fatalf("Expected %d CAME records returned, got: %d", len(expected), len(cnamerecs))
}
// Ensure records were parsed correctly
expected = [][]string{
{"source1.example.com", "target1.domain.com", "1000"},
{"source2.example.com", "target2.domain.com", "50"},
{"source3.example.com", "target3.domain.com"},
} }
for idx, rec := range cnamerecs { for idx, rec := range cnamerecs {
if rec.DNSName != expected[idx][0] { if rec.DNSName != expected[idx][0] {
t.Error("Got invalid DNS Name:", rec.DNSName, "expected:", expected[idx][0]) t.Error("Got invalid DNS Name:", rec.DNSName, "expected:", expected[idx][0])
@ -261,6 +326,7 @@ func TestListRecordsV6(t *testing.T) {
t.Fatal("Expected error for using unsupported record type") t.Fatal("Expected error for using unsupported record type")
} }
} }
func TestErrorsV6(t *testing.T) { func TestErrorsV6(t *testing.T) {
//Error test cases //Error test cases

View File

@ -45,29 +45,25 @@ type RecordChange struct {
func NewPluralProvider(cluster, provider string) (*PluralProvider, error) { func NewPluralProvider(cluster, provider string) (*PluralProvider, error) {
token := os.Getenv("PLURAL_ACCESS_TOKEN") token := os.Getenv("PLURAL_ACCESS_TOKEN")
endpoint := os.Getenv("PLURAL_ENDPOINT")
if token == "" { if token == "" {
return nil, fmt.Errorf("no plural access token provided, you must set the PLURAL_ACCESS_TOKEN env var") return nil, fmt.Errorf("no plural access token provided, you must set the PLURAL_ACCESS_TOKEN env var")
} }
config := &Config{ config := &Config{
Token: token, Token: token,
Endpoint: endpoint, Endpoint: os.Getenv("PLURAL_ENDPOINT"),
Cluster: cluster, Cluster: cluster,
Provider: provider, Provider: provider,
} }
client, err := NewClient(config) cl, err := NewClient(config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
prov := &PluralProvider{ return &PluralProvider{
Client: client, Client: cl,
} }, nil
return prov, nil
} }
func (p *PluralProvider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, err error) { func (p *PluralProvider) Records(_ context.Context) (endpoints []*endpoint.Endpoint, err error) {
@ -89,8 +85,8 @@ func (p *PluralProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*end
func (p *PluralProvider) ApplyChanges(_ context.Context, diffs *plan.Changes) error { func (p *PluralProvider) ApplyChanges(_ context.Context, diffs *plan.Changes) error {
var changes []*RecordChange var changes []*RecordChange
for _, endpoint := range diffs.Create { for _, ep := range diffs.Create {
changes = append(changes, makeChange(CreateAction, endpoint.Targets, endpoint)) changes = append(changes, makeChange(CreateAction, ep.Targets, ep))
} }
for _, desired := range diffs.UpdateNew { for _, desired := range diffs.UpdateNew {

View File

@ -19,6 +19,7 @@ package rfc2136
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
@ -32,7 +33,6 @@ import (
"github.com/bodgit/tsig/gss" "github.com/bodgit/tsig/gss"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
@ -116,7 +116,7 @@ type rfc2136Actions interface {
func NewRfc2136Provider(hosts []string, port int, zoneNames []string, insecure bool, keyName string, secret string, secretAlg string, axfr bool, domainFilter endpoint.DomainFilter, dryRun bool, minTTL time.Duration, createPTR bool, gssTsig bool, krb5Username string, krb5Password string, krb5Realm string, batchChangeSize int, tlsConfig TLSConfig, loadBalancingStrategy string, actions rfc2136Actions) (provider.Provider, error) { func NewRfc2136Provider(hosts []string, port int, zoneNames []string, insecure bool, keyName string, secret string, secretAlg string, axfr bool, domainFilter endpoint.DomainFilter, dryRun bool, minTTL time.Duration, createPTR bool, gssTsig bool, krb5Username string, krb5Password string, krb5Realm string, batchChangeSize int, tlsConfig TLSConfig, loadBalancingStrategy string, actions rfc2136Actions) (provider.Provider, error) {
secretAlgChecked, ok := tsigAlgs[secretAlg] secretAlgChecked, ok := tsigAlgs[secretAlg]
if !ok && !insecure && !gssTsig { if !ok && !insecure && !gssTsig {
return nil, errors.Errorf("%s is not supported TSIG algorithm", secretAlg) return nil, fmt.Errorf("%s is not supported TSIG algorithm", secretAlg)
} }
// Set zone to root if no set // Set zone to root if no set
@ -307,7 +307,7 @@ func (r *rfc2136Provider) List() ([]dns.RR, error) {
for e := range env { for e := range env {
if e.Error != nil { if e.Error != nil {
if e.Error == dns.ErrSoa { if errors.Is(e.Error, dns.ErrSoa) {
log.Error("AXFR error: unexpected response received from the server") log.Error("AXFR error: unexpected response received from the server")
} else { } else {
log.Errorf("AXFR error: %v", e.Error) log.Errorf("AXFR error: %v", e.Error)
@ -342,8 +342,6 @@ func (r *rfc2136Provider) RemoveReverseRecord(ip string, hostname string) error
} }
func (r *rfc2136Provider) GenerateReverseRecord(ip string, hostname string) []*endpoint.Endpoint { func (r *rfc2136Provider) GenerateReverseRecord(ip string, hostname string) []*endpoint.Endpoint {
// Find the zone for the PTR record
// zone := findMsgZone(&endpoint.Endpoint{DNSName: ip}, p.ptrZoneNames)
// Generate PTR notation record starting from the IP address // Generate PTR notation record starting from the IP address
var records []*endpoint.Endpoint var records []*endpoint.Endpoint
@ -364,7 +362,7 @@ func (r *rfc2136Provider) GenerateReverseRecord(ip string, hostname string) []*e
func (r *rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { func (r *rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
log.Debugf("ApplyChanges (Create: %d, UpdateOld: %d, UpdateNew: %d, Delete: %d)", len(changes.Create), len(changes.UpdateOld), len(changes.UpdateNew), len(changes.Delete)) log.Debugf("ApplyChanges (Create: %d, UpdateOld: %d, UpdateNew: %d, Delete: %d)", len(changes.Create), len(changes.UpdateOld), len(changes.UpdateNew), len(changes.Delete))
var errors []error var errs []error
for c, chunk := range chunkBy(changes.Create, r.batchChangeSize) { for c, chunk := range chunkBy(changes.Create, r.batchChangeSize) {
log.Debugf("Processing batch %d of create changes", c) log.Debugf("Processing batch %d of create changes", c)
@ -397,7 +395,7 @@ func (r *rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Change
if len(z.Ns) > 0 { if len(z.Ns) > 0 {
if err := r.actions.SendMessage(z); err != nil { if err := r.actions.SendMessage(z); err != nil {
log.Errorf("RFC2136 create record failed: %v", err) log.Errorf("RFC2136 create record failed: %v", err)
errors = append(errors, err) errs = append(errs, err)
continue continue
} }
} }
@ -436,7 +434,7 @@ func (r *rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Change
if len(z.Ns) > 0 { if len(z.Ns) > 0 {
if err := r.actions.SendMessage(z); err != nil { if err := r.actions.SendMessage(z); err != nil {
log.Errorf("RFC2136 update record failed: %v", err) log.Errorf("RFC2136 update record failed: %v", err)
errors = append(errors, err) errs = append(errs, err)
continue continue
} }
} }
@ -473,15 +471,15 @@ func (r *rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Change
if len(z.Ns) > 0 { if len(z.Ns) > 0 {
if err := r.actions.SendMessage(z); err != nil { if err := r.actions.SendMessage(z); err != nil {
log.Errorf("RFC2136 delete record failed: %v", err) log.Errorf("RFC2136 delete record failed: %v", err)
errors = append(errors, err) errs = append(errs, err)
continue continue
} }
} }
} }
} }
if len(errors) > 0 { if len(errs) > 0 {
return fmt.Errorf("RFC2136 had errors in one or more of its batches: %v", errors) return fmt.Errorf("RFC2136 had errors in one or more of its batches: %v", errs)
} }
return nil return nil

View File

@ -34,7 +34,7 @@ import (
) )
const ( const (
scalewyRecordTTL uint32 = 300 defaultTTL uint32 = 300
scalewayDefaultPriority uint32 = 0 scalewayDefaultPriority uint32 = 0
scalewayPriorityKey string = "scw/priority" scalewayPriorityKey string = "scw/priority"
) )
@ -110,7 +110,7 @@ func (p *ScalewayProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*e
for i := range endpoints { for i := range endpoints {
eps[i] = endpoints[i] eps[i] = endpoints[i]
if !eps[i].RecordTTL.IsConfigured() { if !eps[i].RecordTTL.IsConfigured() {
eps[i].RecordTTL = endpoint.TTL(scalewyRecordTTL) eps[i].RecordTTL = endpoint.TTL(defaultTTL)
} }
if _, ok := eps[i].GetProviderSpecificProperty(scalewayPriorityKey); !ok { if _, ok := eps[i].GetProviderSpecificProperty(scalewayPriorityKey); !ok {
eps[i] = eps[i].WithProviderSpecific(scalewayPriorityKey, fmt.Sprintf("%d", scalewayDefaultPriority)) eps[i] = eps[i].WithProviderSpecific(scalewayPriorityKey, fmt.Sprintf("%d", scalewayDefaultPriority))
@ -300,7 +300,7 @@ func getCompleteZoneName(zone *domain.DNSZone) string {
func endpointToScalewayRecords(zoneName string, ep *endpoint.Endpoint) []*domain.Record { func endpointToScalewayRecords(zoneName string, ep *endpoint.Endpoint) []*domain.Record {
// no annotation results in a TTL of 0, default to 300 for consistency with other providers // no annotation results in a TTL of 0, default to 300 for consistency with other providers
ttl := scalewyRecordTTL ttl := defaultTTL
if ep.RecordTTL.IsConfigured() { if ep.RecordTTL.IsConfigured() {
ttl = uint32(ep.RecordTTL) ttl = uint32(ep.RecordTTL)
} }

View File

@ -32,8 +32,6 @@ import (
func NewMockTencentCloudProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneType string) *TencentCloudProvider { func NewMockTencentCloudProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, zoneType string) *TencentCloudProvider {
cfg := tencentCloudConfig{ cfg := tencentCloudConfig{
// SecretId: "",
// SecretKey: "",
RegionId: "ap-shanghai", RegionId: "ap-shanghai",
VPCId: "vpc-abcdefg", VPCId: "vpc-abcdefg",
} }

View File

@ -34,7 +34,7 @@ import (
const ( const (
// 60 seconds is the current minimal TTL for TransIP and will replace unconfigured // 60 seconds is the current minimal TTL for TransIP and will replace unconfigured
// TTL's for Endpoints // TTL's for Endpoints
transipMinimalValidTTL = 60 defaultTTL = 60
) )
// TransIPProvider is an implementation of Provider for TransIP. // TransIPProvider is an implementation of Provider for TransIP.
@ -336,11 +336,11 @@ func recordNameForEndpoint(ep *endpoint.Endpoint, zoneName string) string {
} }
// getMinimalValidTTL returns max between given Endpoint's RecordTTL and // getMinimalValidTTL returns max between given Endpoint's RecordTTL and
// transipMinimalValidTTL // defaultTTL
func getMinimalValidTTL(ep *endpoint.Endpoint) int { func getMinimalValidTTL(ep *endpoint.Endpoint) int {
// TTL cannot be lower than transipMinimalValidTTL // TTL cannot be lower than defaultTTL
if ep.RecordTTL < transipMinimalValidTTL { if ep.RecordTTL < defaultTTL {
return transipMinimalValidTTL return defaultTTL
} }
return int(ep.RecordTTL) return int(ep.RecordTTL)

View File

@ -94,15 +94,15 @@ func TestTransIPDnsEntriesAreEqual(t *testing.T) {
func TestTransIPGetMinimalValidTTL(t *testing.T) { func TestTransIPGetMinimalValidTTL(t *testing.T) {
// test with 'unconfigured' TTL // test with 'unconfigured' TTL
ep := &endpoint.Endpoint{} ep := &endpoint.Endpoint{}
assert.EqualValues(t, transipMinimalValidTTL, getMinimalValidTTL(ep)) assert.EqualValues(t, defaultTTL, getMinimalValidTTL(ep))
// test with lower than minimal ttl // test with lower than minimal ttl
ep.RecordTTL = (transipMinimalValidTTL - 1) ep.RecordTTL = (defaultTTL - 1)
assert.EqualValues(t, transipMinimalValidTTL, getMinimalValidTTL(ep)) assert.EqualValues(t, defaultTTL, getMinimalValidTTL(ep))
// test with higher than minimal ttl // test with higher than minimal ttl
ep.RecordTTL = (transipMinimalValidTTL + 1) ep.RecordTTL = (defaultTTL + 1)
assert.EqualValues(t, transipMinimalValidTTL+1, getMinimalValidTTL(ep)) assert.EqualValues(t, defaultTTL+1, getMinimalValidTTL(ep))
} }
func TestTransIPRecordNameForEndpoint(t *testing.T) { func TestTransIPRecordNameForEndpoint(t *testing.T) {

View File

@ -39,23 +39,20 @@ const (
rdPoolOrder = "ROUND_ROBIN" rdPoolOrder = "ROUND_ROBIN"
) )
// global variables
var sbPoolRunProbes = true
var ( var (
sbPoolActOnProbes = true sbPoolActOnProbes = true
ultradnsPoolType = "rdpool" ultradnsPoolType = "rdpool"
accountName string accountName string
sbPoolRunProbes = true
// Setting custom headers for ultradns api calls
customHeader = []udnssdk.CustomHeader{
{
Key: "UltraClient",
Value: "kube-client",
},
}
) )
// Setting custom headers for ultradns api calls
var customHeader = []udnssdk.CustomHeader{
{
Key: "UltraClient",
Value: "kube-client",
},
}
// UltraDNSProvider struct // UltraDNSProvider struct
type UltraDNSProvider struct { type UltraDNSProvider struct {
provider.BaseProvider provider.BaseProvider
@ -128,13 +125,11 @@ func NewUltraDNSProvider(domainFilter endpoint.DomainFilter, dryRun bool) (*Ultr
return nil, fmt.Errorf("connection cannot be established") return nil, fmt.Errorf("connection cannot be established")
} }
provider := &UltraDNSProvider{ return &UltraDNSProvider{
client: *client, client: *client,
domainFilter: domainFilter, domainFilter: domainFilter,
dryRun: dryRun, dryRun: dryRun,
} }, nil
return provider, nil
} }
// Zones returns list of hosted zones // Zones returns list of hosted zones
@ -216,7 +211,7 @@ func (p *UltraDNSProvider) fetchRecords(ctx context.Context, k udnssdk.RRSetKey)
maxerrs := 5 maxerrs := 5
waittime := 5 * time.Second waittime := 5 * time.Second
rrsets := []udnssdk.RRSet{} var rrsets []udnssdk.RRSet
errcnt := 0 errcnt := 0
offset := 0 offset := 0
limit := 1000 limit := 1000

View File

@ -33,6 +33,9 @@ import (
const ( const (
MediaTypeFormatAndVersion = "application/external.dns.webhook+json;version=1" MediaTypeFormatAndVersion = "application/external.dns.webhook+json;version=1"
ContentTypeHeader = "Content-Type" ContentTypeHeader = "Content-Type"
UrlAdjustEndpoints = "/adjustendpoints"
UrlApplyChanges = "/applychanges"
UrlRecords = "/records"
) )
type WebhookServer struct { type WebhookServer struct {
@ -82,7 +85,7 @@ func (p *WebhookServer) AdjustEndpointsHandler(w http.ResponseWriter, req *http.
return return
} }
pve := []*endpoint.Endpoint{} var pve []*endpoint.Endpoint
if err := json.NewDecoder(req.Body).Decode(&pve); err != nil { if err := json.NewDecoder(req.Body).Decode(&pve); err != nil {
log.Errorf("Failed to decode in adjustEndpointsHandler: %v", err) log.Errorf("Failed to decode in adjustEndpointsHandler: %v", err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -101,7 +104,7 @@ func (p *WebhookServer) AdjustEndpointsHandler(w http.ResponseWriter, req *http.
} }
} }
func (p *WebhookServer) NegotiateHandler(w http.ResponseWriter, req *http.Request) { func (p *WebhookServer) NegotiateHandler(w http.ResponseWriter, _ *http.Request) {
w.Header().Set(ContentTypeHeader, MediaTypeFormatAndVersion) w.Header().Set(ContentTypeHeader, MediaTypeFormatAndVersion)
json.NewEncoder(w).Encode(p.Provider.GetDomainFilter()) json.NewEncoder(w).Encode(p.Provider.GetDomainFilter())
} }
@ -121,8 +124,8 @@ func StartHTTPApi(provider provider.Provider, startedChan chan struct{}, readTim
m := http.NewServeMux() m := http.NewServeMux()
m.HandleFunc("/", p.NegotiateHandler) m.HandleFunc("/", p.NegotiateHandler)
m.HandleFunc("/records", p.RecordsHandler) m.HandleFunc(UrlRecords, p.RecordsHandler)
m.HandleFunc("/adjustendpoints", p.AdjustEndpointsHandler) m.HandleFunc(UrlAdjustEndpoints, p.AdjustEndpointsHandler)
s := &http.Server{ s := &http.Server{
Addr: providerPort, Addr: providerPort,

View File

@ -24,9 +24,11 @@ import (
"io" "io"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strings"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan" "sigs.k8s.io/external-dns/plan"
@ -35,8 +37,9 @@ import (
var records []*endpoint.Endpoint var records []*endpoint.Endpoint
type FakeWebhookProvider struct { type FakeWebhookProvider struct {
err error err error
domainFilter endpoint.DomainFilter domainFilter endpoint.DomainFilter
assertChanges func(*plan.Changes)
} }
func (p FakeWebhookProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) { func (p FakeWebhookProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
@ -51,6 +54,9 @@ func (p FakeWebhookProvider) ApplyChanges(ctx context.Context, changes *plan.Cha
return p.err return p.err
} }
records = append(records, changes.Create...) records = append(records, changes.Create...)
if p.assertChanges != nil {
p.assertChanges(changes)
}
return nil return nil
} }
@ -77,7 +83,7 @@ func TestMain(m *testing.M) {
} }
func TestRecordsHandlerRecords(t *testing.T) { func TestRecordsHandlerRecords(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/records", nil) req := httptest.NewRequest(http.MethodGet, UrlRecords, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{ providerAPIServer := &WebhookServer{
@ -99,7 +105,7 @@ func TestRecordsHandlerRecords(t *testing.T) {
} }
func TestRecordsHandlerRecordsWithErrors(t *testing.T) { func TestRecordsHandlerRecordsWithErrors(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/records", nil) req := httptest.NewRequest(http.MethodGet, UrlRecords, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{ providerAPIServer := &WebhookServer{
@ -139,7 +145,7 @@ func TestRecordsHandlerApplyChangesWithValidRequest(t *testing.T) {
reader := bytes.NewReader(j) reader := bytes.NewReader(j)
req := httptest.NewRequest(http.MethodPost, "/applychanges", reader) req := httptest.NewRequest(http.MethodPost, UrlApplyChanges, reader)
w := httptest.NewRecorder() w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{ providerAPIServer := &WebhookServer{
@ -165,7 +171,7 @@ func TestRecordsHandlerApplyChangesWithErrors(t *testing.T) {
reader := bytes.NewReader(j) reader := bytes.NewReader(j)
req := httptest.NewRequest(http.MethodPost, "/applychanges", reader) req := httptest.NewRequest(http.MethodPost, UrlApplyChanges, reader)
w := httptest.NewRecorder() w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{ providerAPIServer := &WebhookServer{
@ -179,7 +185,7 @@ func TestRecordsHandlerApplyChangesWithErrors(t *testing.T) {
} }
func TestRecordsHandlerWithWrongHTTPMethod(t *testing.T) { func TestRecordsHandlerWithWrongHTTPMethod(t *testing.T) {
req := httptest.NewRequest(http.MethodPut, "/records", nil) req := httptest.NewRequest(http.MethodPut, UrlRecords, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{ providerAPIServer := &WebhookServer{
@ -190,8 +196,43 @@ func TestRecordsHandlerWithWrongHTTPMethod(t *testing.T) {
require.Equal(t, http.StatusBadRequest, res.StatusCode) require.Equal(t, http.StatusBadRequest, res.StatusCode)
} }
func TestRecordsHandlerWithMixedCase(t *testing.T) {
input := `{"Create":[{"dnsName":"foo"}],"updateOld":[{"dnsName":"bar"}],"updateNew":[{"dnsName":"baz"}],"Delete":[{"dnsName":"qux"}]}`
req := httptest.NewRequest(http.MethodPost, UrlRecords, strings.NewReader(input))
w := httptest.NewRecorder()
records = []*endpoint.Endpoint{}
providerAPIServer := &WebhookServer{
Provider: &FakeWebhookProvider{
assertChanges: func(changes *plan.Changes) {
t.Helper()
require.Equal(t, []*endpoint.Endpoint{
{
DNSName: "foo",
},
}, changes.Create)
require.Equal(t, []*endpoint.Endpoint{
{
DNSName: "bar",
},
}, changes.UpdateOld)
require.Equal(t, []*endpoint.Endpoint{
{
DNSName: "qux",
},
}, changes.Delete)
},
},
}
providerAPIServer.RecordsHandler(w, req)
res := w.Result()
require.Equal(t, http.StatusNoContent, res.StatusCode)
assert.Equal(t, 1, len(records))
}
func TestAdjustEndpointsHandlerWithInvalidRequest(t *testing.T) { func TestAdjustEndpointsHandlerWithInvalidRequest(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/adjustendpoints", nil) req := httptest.NewRequest(http.MethodPost, UrlAdjustEndpoints, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{ providerAPIServer := &WebhookServer{
@ -201,7 +242,7 @@ func TestAdjustEndpointsHandlerWithInvalidRequest(t *testing.T) {
res := w.Result() res := w.Result()
require.Equal(t, http.StatusBadRequest, res.StatusCode) require.Equal(t, http.StatusBadRequest, res.StatusCode)
req = httptest.NewRequest(http.MethodGet, "/adjustendpoints", nil) req = httptest.NewRequest(http.MethodGet, UrlAdjustEndpoints, nil)
providerAPIServer.AdjustEndpointsHandler(w, req) providerAPIServer.AdjustEndpointsHandler(w, req)
res = w.Result() res = w.Result()
@ -222,7 +263,7 @@ func TestAdjustEndpointsHandlerWithValidRequest(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
reader := bytes.NewReader(j) reader := bytes.NewReader(j)
req := httptest.NewRequest(http.MethodPost, "/adjustendpoints", reader) req := httptest.NewRequest(http.MethodPost, UrlAdjustEndpoints, reader)
w := httptest.NewRecorder() w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{ providerAPIServer := &WebhookServer{
@ -248,7 +289,7 @@ func TestAdjustEndpointsHandlerWithError(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
reader := bytes.NewReader(j) reader := bytes.NewReader(j)
req := httptest.NewRequest(http.MethodPost, "/adjustendpoints", reader) req := httptest.NewRequest(http.MethodPost, UrlAdjustEndpoints, reader)
w := httptest.NewRecorder() w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{ providerAPIServer := &WebhookServer{

View File

@ -30,7 +30,7 @@ import (
"sigs.k8s.io/external-dns/provider" "sigs.k8s.io/external-dns/provider"
webhookapi "sigs.k8s.io/external-dns/provider/webhook/api" webhookapi "sigs.k8s.io/external-dns/provider/webhook/api"
"github.com/cenkalti/backoff/v4" "github.com/cenkalti/backoff/v5"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -113,45 +113,30 @@ func NewWebhookProvider(u string) (*WebhookProvider, error) {
} }
// negotiate API information // negotiate API information
req, err := http.NewRequest("GET", u, nil) req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Set(acceptHeader, webhookapi.MediaTypeFormatAndVersion) req.Header.Set(acceptHeader, webhookapi.MediaTypeFormatAndVersion)
client := &http.Client{} client := &http.Client{}
var resp *http.Response
err = backoff.Retry(func() error {
resp, err = client.Do(req)
if err != nil {
log.Debugf("Failed to connect to webhook: %v", err)
return err
}
// we currently only use 200 as success, but considering okay all 2XX for future usage
if resp.StatusCode >= 300 && resp.StatusCode < 500 {
return backoff.Permanent(fmt.Errorf("status code < 500"))
}
return nil
}, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), maxRetries))
resp, err := requestWithRetry(client, req)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to connect to webhook: %v", err) return nil, fmt.Errorf("failed to connect to webhook: %v", err)
} }
contentType := resp.Header.Get(webhookapi.ContentTypeHeader)
// read the serialized DomainFilter from the response body and set it in the webhook provider struct // read the serialized DomainFilter from the response body and set it in the webhook provider struct
defer resp.Body.Close() defer resp.Body.Close()
if ct := resp.Header.Get(webhookapi.ContentTypeHeader); ct != webhookapi.MediaTypeFormatAndVersion {
return nil, fmt.Errorf("wrong content type returned from server: %s", ct)
}
df := endpoint.DomainFilter{} df := endpoint.DomainFilter{}
if err := json.NewDecoder(resp.Body).Decode(&df); err != nil { if err := json.NewDecoder(resp.Body).Decode(&df); err != nil {
return nil, fmt.Errorf("failed to unmarshal response body of DomainFilter: %v", err) return nil, fmt.Errorf("failed to unmarshal response body of DomainFilter: %v", err)
} }
if contentType != webhookapi.MediaTypeFormatAndVersion {
return nil, fmt.Errorf("wrong content type returned from server: %s", contentType)
}
return &WebhookProvider{ return &WebhookProvider{
client: client, client: client,
remoteServerURL: parsedURL, remoteServerURL: parsedURL,
@ -159,12 +144,28 @@ func NewWebhookProvider(u string) (*WebhookProvider, error) {
}, nil }, nil
} }
func requestWithRetry(client *http.Client, req *http.Request) (*http.Response, error) {
resp, err := backoff.Retry(context.Background(), func() (*http.Response, error) {
resp, err := client.Do(req)
if err != nil {
log.Debugf("Failed to connect to webhook: %v", err)
return nil, err
}
// we currently only use 200 as success, but considering okay all 2XX for future usage
if resp.StatusCode >= http.StatusMultipleChoices && resp.StatusCode < http.StatusInternalServerError {
return nil, backoff.Permanent(fmt.Errorf("status code < %d", http.StatusInternalServerError))
}
return resp, nil
}, backoff.WithMaxTries(maxRetries))
return resp, err
}
// Records will make a GET call to remoteServerURL/records and return the results // Records will make a GET call to remoteServerURL/records and return the results
func (p WebhookProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) { func (p WebhookProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
recordsRequestsGauge.Gauge.Inc() recordsRequestsGauge.Gauge.Inc()
u := p.remoteServerURL.JoinPath("records").String() u := p.remoteServerURL.JoinPath("records").String()
req, err := http.NewRequest("GET", u, nil) req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil { if err != nil {
recordsErrorsGauge.Gauge.Inc() recordsErrorsGauge.Gauge.Inc()
log.Debugf("Failed to create request: %s", err.Error()) log.Debugf("Failed to create request: %s", err.Error())
@ -189,7 +190,7 @@ func (p WebhookProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, err
return nil, err return nil, err
} }
endpoints := []*endpoint.Endpoint{} var endpoints []*endpoint.Endpoint
if err := json.NewDecoder(resp.Body).Decode(&endpoints); err != nil { if err := json.NewDecoder(resp.Body).Decode(&endpoints); err != nil {
recordsErrorsGauge.Gauge.Inc() recordsErrorsGauge.Gauge.Inc()
log.Debugf("Failed to decode response body: %s", err.Error()) log.Debugf("Failed to decode response body: %s", err.Error())
@ -199,9 +200,9 @@ func (p WebhookProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, err
} }
// ApplyChanges will make a POST to remoteServerURL/records with the changes // ApplyChanges will make a POST to remoteServerURL/records with the changes
func (p WebhookProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error { func (p WebhookProvider) ApplyChanges(_ context.Context, changes *plan.Changes) error {
applyChangesRequestsGauge.Gauge.Inc() applyChangesRequestsGauge.Gauge.Inc()
u := p.remoteServerURL.JoinPath("records").String() u := p.remoteServerURL.JoinPath(webhookapi.UrlRecords).String()
b := new(bytes.Buffer) b := new(bytes.Buffer)
if err := json.NewEncoder(b).Encode(changes); err != nil { if err := json.NewEncoder(b).Encode(changes); err != nil {
@ -210,7 +211,7 @@ func (p WebhookProvider) ApplyChanges(ctx context.Context, changes *plan.Changes
return err return err
} }
req, err := http.NewRequest("POST", u, b) req, err := http.NewRequest(http.MethodPost, u, b)
if err != nil { if err != nil {
applyChangesErrorsGauge.Gauge.Inc() applyChangesErrorsGauge.Gauge.Inc()
log.Debugf("Failed to create request: %s", err.Error()) log.Debugf("Failed to create request: %s", err.Error())
@ -225,6 +226,7 @@ func (p WebhookProvider) ApplyChanges(ctx context.Context, changes *plan.Changes
log.Debugf("Failed to perform request: %s", err.Error()) log.Debugf("Failed to perform request: %s", err.Error())
return err return err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusNoContent { if resp.StatusCode != http.StatusNoContent {
@ -240,12 +242,12 @@ func (p WebhookProvider) ApplyChanges(ctx context.Context, changes *plan.Changes
} }
// AdjustEndpoints will call the provider doing a POST on `/adjustendpoints` which will return a list of modified endpoints // AdjustEndpoints will call the provider doing a POST on `/adjustendpoints` which will return a list of modified endpoints
// based on a provider specific requirement. // based on a provider-specific requirement.
// This method returns an empty slice in case there is a technical error on the provider's side so that no endpoints will be considered. // This method returns an empty slice in case there is a technical error on the provider's side so that no endpoints will be considered.
func (p WebhookProvider) AdjustEndpoints(e []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) { func (p WebhookProvider) AdjustEndpoints(e []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
adjustEndpointsRequestsGauge.Gauge.Inc() adjustEndpointsRequestsGauge.Gauge.Inc()
endpoints := []*endpoint.Endpoint{} var endpoints []*endpoint.Endpoint
u, err := url.JoinPath(p.remoteServerURL.String(), "adjustendpoints") u, err := url.JoinPath(p.remoteServerURL.String(), webhookapi.UrlAdjustEndpoints)
if err != nil { if err != nil {
adjustEndpointsErrorsGauge.Gauge.Inc() adjustEndpointsErrorsGauge.Gauge.Inc()
log.Debugf("Failed to join path, %s", err) log.Debugf("Failed to join path, %s", err)
@ -259,7 +261,7 @@ func (p WebhookProvider) AdjustEndpoints(e []*endpoint.Endpoint) ([]*endpoint.En
return nil, err return nil, err
} }
req, err := http.NewRequest("POST", u, b) req, err := http.NewRequest(http.MethodPost, u, b)
if err != nil { if err != nil {
adjustEndpointsErrorsGauge.Gauge.Inc() adjustEndpointsErrorsGauge.Gauge.Inc()
log.Debugf("Failed to create new HTTP request, %s", err) log.Debugf("Failed to create new HTTP request, %s", err)

View File

@ -22,8 +22,11 @@ import (
"io" "io"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan" "sigs.k8s.io/external-dns/plan"
@ -31,6 +34,55 @@ import (
webhookapi "sigs.k8s.io/external-dns/provider/webhook/api" webhookapi "sigs.k8s.io/external-dns/provider/webhook/api"
) )
func TestNewWebhookProvider_InvalidURL(t *testing.T) {
_, err := NewWebhookProvider("://invalid-url")
require.Error(t, err)
}
func TestNewWebhookProvider_HTTPRequestFailure(t *testing.T) {
_, err := NewWebhookProvider("http://nonexistent.url")
require.Error(t, err)
}
func TestNewWebhookProvider_InvalidResponseBody(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(webhookapi.ContentTypeHeader, webhookapi.MediaTypeFormatAndVersion)
w.WriteHeader(http.StatusOK)
w.Write([]byte("invalid-json")) // Invalid JSON
}))
defer svr.Close()
_, err := NewWebhookProvider(svr.URL)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to unmarshal response body of DomainFilter")
}
func TestNewWebhookProvider_Non2XXStatusCode(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
}))
defer svr.Close()
_, err := NewWebhookProvider(svr.URL)
require.Error(t, err)
require.Contains(t, err.Error(), "status code < 500")
}
func TestNewWebhookProvider_WrongContentTypeHeader(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
w.Header().Set(webhookapi.ContentTypeHeader, webhookapi.MediaTypeFormatAndVersion+"wrong")
_, _ = w.Write([]byte(`{}`))
return
}
}))
defer svr.Close()
_, err := NewWebhookProvider(svr.URL)
require.Error(t, err)
require.Contains(t, err.Error(), "wrong content type returned from server")
}
func TestInvalidDomainFilter(t *testing.T) { func TestInvalidDomainFilter(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" { if r.URL.Path == "/" {
@ -108,6 +160,68 @@ func TestRecordsWithErrors(t *testing.T) {
require.ErrorIs(t, err, provider.SoftError) require.ErrorIs(t, err, provider.SoftError)
} }
func TestRecords_HTTPRequestErrorMissingHost0(t *testing.T) {
wpr := WebhookProvider{
remoteServerURL: &url.URL{Scheme: "http", Host: "example\\x00.com", Path: "\\x00"},
client: &http.Client{},
}
_, err := wpr.Records(nil)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid URL escape")
}
func TestRecords_HTTPRequestErrorMissingHost(t *testing.T) {
wpr := WebhookProvider{
remoteServerURL: &url.URL{Host: "example.com", Path: "\\x00"},
client: &http.Client{},
}
_, err := wpr.Records(nil)
require.Error(t, err)
require.Contains(t, err.Error(), "unsupported protocol scheme")
}
func TestRecords_DecodeError(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == webhookapi.UrlRecords {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("invalid-json")) // Simulate invalid JSON response
return
}
}))
defer svr.Close()
parsedURL, _ := url.Parse(svr.URL)
p := WebhookProvider{
remoteServerURL: parsedURL,
client: &http.Client{},
}
_, err := p.Records(context.Background())
require.Error(t, err)
require.Contains(t, err.Error(), "invalid character 'i' looking for beginning of value")
}
func TestRecords_NonOKStatusCode(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNetworkAuthenticationRequired)
return
}))
defer svr.Close()
parsedURL, _ := url.Parse(svr.URL)
p := WebhookProvider{
remoteServerURL: &url.URL{Scheme: parsedURL.Scheme, Host: parsedURL.Host},
client: &http.Client{},
}
_, err := p.Records(nil)
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to get records with code 511")
}
func TestApplyChanges(t *testing.T) { func TestApplyChanges(t *testing.T) {
successfulApplyChanges := true successfulApplyChanges := true
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -137,6 +251,49 @@ func TestApplyChanges(t *testing.T) {
require.ErrorIs(t, err, provider.SoftError) require.ErrorIs(t, err, provider.SoftError)
} }
func TestApplyChanges_HTTPNewRequestErrorWrongHost(t *testing.T) {
wpr := WebhookProvider{
remoteServerURL: &url.URL{Host: "exa\\x00mple.com"},
client: &http.Client{},
}
err := wpr.ApplyChanges(context.Background(), nil)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid URL escape")
}
func TestApplyChanges_GetFailed(t *testing.T) {
p := WebhookProvider{
remoteServerURL: &url.URL{Host: "localhost"},
client: &http.Client{},
}
err := p.ApplyChanges(context.TODO(), &plan.Changes{})
require.Error(t, err)
assert.Contains(t, err.Error(), "unsupported protocol scheme")
}
func TestApplyChanges_StatusCodeError(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
w.Header().Set(webhookapi.ContentTypeHeader, webhookapi.MediaTypeFormatAndVersion)
w.Write([]byte(`{}`))
return
}
require.Equal(t, webhookapi.UrlRecords, r.URL.Path)
w.WriteHeader(http.StatusNetworkAuthenticationRequired)
}))
defer svr.Close()
p, err := NewWebhookProvider(svr.URL)
require.NoError(t, err)
err = p.ApplyChanges(context.TODO(), nil)
require.NotNil(t, err)
require.NotErrorIs(t, err, provider.SoftError)
assert.Contains(t, err.Error(), "failed to apply changes with code 511")
}
func TestAdjustEndpoints(t *testing.T) { func TestAdjustEndpoints(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" { if r.URL.Path == "/" {
@ -144,7 +301,7 @@ func TestAdjustEndpoints(t *testing.T) {
w.Write([]byte(`{}`)) w.Write([]byte(`{}`))
return return
} }
require.Equal(t, "/adjustendpoints", r.URL.Path) require.Equal(t, webhookapi.UrlAdjustEndpoints, r.URL.Path)
var endpoints []*endpoint.Endpoint var endpoints []*endpoint.Endpoint
defer r.Body.Close() defer r.Body.Close()
@ -162,7 +319,6 @@ func TestAdjustEndpoints(t *testing.T) {
} }
j, _ := json.Marshal(endpoints) j, _ := json.Marshal(endpoints)
w.Write(j) w.Write(j)
})) }))
defer svr.Close() defer svr.Close()
@ -197,7 +353,7 @@ func TestAdjustendpointsWithError(t *testing.T) {
w.Write([]byte(`{}`)) w.Write([]byte(`{}`))
return return
} }
require.Equal(t, "/adjustendpoints", r.URL.Path) require.Equal(t, webhookapi.UrlAdjustEndpoints, r.URL.Path)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
})) }))
defer svr.Close() defer svr.Close()
@ -269,3 +425,108 @@ func TestApplyChangesWithProviderSpecificProperty(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
} }
func TestAdjustEndpoints_JoinPathError(t *testing.T) {
wpr := WebhookProvider{
remoteServerURL: &url.URL{Scheme: "http", Host: "example\\x00.com"},
}
_, err := wpr.AdjustEndpoints(nil)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid URL escape")
}
func TestAdjustEndpoints_HTTPRequestErrorMissingHost(t *testing.T) {
wpr := WebhookProvider{
remoteServerURL: &url.URL{Host: "example.com", Path: "\\x00"},
client: &http.Client{},
}
_, err := wpr.AdjustEndpoints(nil)
require.Error(t, err)
require.Contains(t, err.Error(), "unsupported protocol scheme") // Ensure the "BINGO" log is triggered
}
func TestAdjustEndpoints_NonOKStatusCode(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNetworkAuthenticationRequired)
return
}))
defer svr.Close()
parsedURL, _ := url.Parse(svr.URL)
p := WebhookProvider{
remoteServerURL: &url.URL{Scheme: parsedURL.Scheme, Host: parsedURL.Host},
client: &http.Client{},
}
endpoints := []*endpoint.Endpoint{
{
DNSName: "test.example.com",
RecordTTL: 10,
RecordType: "A",
Targets: endpoint.Targets{""},
},
}
_, err := p.AdjustEndpoints(endpoints)
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to AdjustEndpoints with code 511")
}
func TestAdjustEndpoints_DecodeError(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == webhookapi.UrlAdjustEndpoints {
w.Header().Set(webhookapi.ContentTypeHeader, webhookapi.MediaTypeFormatAndVersion)
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("invalid-json")) // Simulate invalid JSON response
return
}
}))
defer svr.Close()
parsedURL, _ := url.Parse(svr.URL)
p := WebhookProvider{
remoteServerURL: parsedURL,
client: &http.Client{},
}
var endpoints []*endpoint.Endpoint
_, err := p.AdjustEndpoints(endpoints)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid character 'i' looking for beginning of value")
}
func TestRequestWithRetry_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
io.WriteString(w, "ok")
}))
defer server.Close()
client := &http.Client{Timeout: 2 * time.Second}
req, err := http.NewRequest(http.MethodGet, server.URL, nil)
require.NoError(t, err)
resp, err := requestWithRetry(client, req)
require.NoError(t, err)
require.NotNil(t, resp)
require.Equal(t, http.StatusOK, resp.StatusCode)
}
func TestRequestWithRetry_NonRetriableStatus(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
}))
defer server.Close()
client := &http.Client{Timeout: 2 * time.Second}
req, err := http.NewRequest(http.MethodGet, server.URL, nil)
require.NoError(t, err)
resp, err := requestWithRetry(client, req)
require.Error(t, err)
require.Nil(t, resp)
}

View File

@ -29,12 +29,34 @@ func (z ZoneIDName) Add(zoneID, zoneName string) {
z[zoneID] = zoneName z[zoneID] = zoneName
} }
// FindZone identifies the most suitable DNS zone for a given hostname.
// It returns the zone ID and name that best match the hostname.
//
// The function processes the hostname by splitting it into labels and
// converting each label to its Unicode form using IDNA (Internationalized
// Domain Names for Applications) standards.
//
// Labels containing underscores ('_') are skipped during Unicode conversion.
// This is because underscores are often used in special DNS records (e.g.,
// SRV records as per RFC 2782, or TXT record for services) that are not
// IDNA-aware and cannot represent non-ASCII labels. Skipping these labels
// ensures compatibility with such use cases.
func (z ZoneIDName) FindZone(hostname string) (suitableZoneID, suitableZoneName string) { func (z ZoneIDName) FindZone(hostname string) (suitableZoneID, suitableZoneName string) {
name, err := idna.Lookup.ToUnicode(hostname) var name string
if err != nil { domain_labels := strings.Split(hostname, ".")
log.Warnf("Failed to convert hostname '%s' to its Unicode form: %v", hostname, err) for i, label := range domain_labels {
name = hostname if strings.Contains(label, "_") {
continue
}
convertedLabel, err := idna.Lookup.ToUnicode(label)
if err != nil {
log.Warnf("Failed to convert label '%s' of hostname '%s' to its Unicode form: %v", label, hostname, err)
convertedLabel = label
}
domain_labels[i] = convertedLabel
} }
name = strings.Join(domain_labels, ".")
for zoneID, zoneName := range z { for zoneID, zoneName := range z {
if name == zoneName || strings.HasSuffix(name, "."+zoneName) { if name == zoneName || strings.HasSuffix(name, "."+zoneName) {
if suitableZoneName == "" || len(zoneName) > len(suitableZoneName) { if suitableZoneName == "" || len(zoneName) > len(suitableZoneName) {

View File

@ -30,11 +30,15 @@ func TestZoneIDName(t *testing.T) {
z.Add("123456", "qux.baz") z.Add("123456", "qux.baz")
z.Add("654321", "foo.qux.baz") z.Add("654321", "foo.qux.baz")
z.Add("987654", "エイミー.みんな") z.Add("987654", "エイミー.みんな")
z.Add("123123", "_metadata.example.com")
z.Add("456456", "_metadata.エイミー.みんな")
assert.Equal(t, ZoneIDName{ assert.Equal(t, ZoneIDName{
"123456": "qux.baz", "123456": "qux.baz",
"654321": "foo.qux.baz", "654321": "foo.qux.baz",
"987654": "エイミー.みんな", "987654": "エイミー.みんな",
"123123": "_metadata.example.com",
"456456": "_metadata.エイミー.みんな",
}, z) }, z)
// simple entry in a domain // simple entry in a domain
@ -72,7 +76,8 @@ func TestZoneIDName(t *testing.T) {
assert.Equal(t, "エイミー.みんな", zoneName) assert.Equal(t, "エイミー.みんな", zoneName)
assert.Equal(t, "987654", zoneID) assert.Equal(t, "987654", zoneID)
b := testutils.LogsToBuffer(log.WarnLevel, t) hook := testutils.LogsUnderTestWithLogLevel(log.WarnLevel, t)
zoneID, zoneName = z.FindZone("???") _, _ = z.FindZone("???")
assert.Contains(t, b.String(), "level=warning msg=\"Failed to convert hostname '???' to its Unicode form: idna: disallowed rune U+003F\"")
testutils.TestHelperLogContains("Failed to convert label '???' of hostname '???' to its Unicode form: idna: disallowed rune U+003F", hook, t)
} }

View File

@ -300,12 +300,12 @@ func (im *DynamoDBRegistry) ApplyChanges(ctx context.Context, changes *plan.Chan
if err != nil { if err != nil {
return err return err
} }
for i, endpoint := range filteredChanges.Create { for i, ep := range filteredChanges.Create {
if endpoint.Key() == key { if ep.Key() == key {
log.Infof("Skipping endpoint %v because owner does not match", endpoint) log.Infof("Skipping endpoint %v because owner does not match", ep)
filteredChanges.Create = append(filteredChanges.Create[:i], filteredChanges.Create[i+1:]...) filteredChanges.Create = append(filteredChanges.Create[:i], filteredChanges.Create[i+1:]...)
// The dynamodb insertion failed; remove from our cache. // The dynamodb insertion failed; remove from our cache.
im.removeFromCache(endpoint) im.removeFromCache(ep)
delete(im.labels, key) delete(im.labels, key)
return nil return nil
} }
@ -466,7 +466,7 @@ func toDynamoLabels(labels endpoint.Labels) dynamodbtypes.AttributeValue {
return &dynamodbtypes.AttributeValueMemberM{Value: labelMap} return &dynamodbtypes.AttributeValueMemberM{Value: labelMap}
} }
func (im *DynamoDBRegistry) appendInsert(statements []dynamodbtypes.BatchStatementRequest, key endpoint.EndpointKey, new endpoint.Labels) []dynamodbtypes.BatchStatementRequest { func (im *DynamoDBRegistry) appendInsert(statements []dynamodbtypes.BatchStatementRequest, key endpoint.EndpointKey, newL endpoint.Labels) []dynamodbtypes.BatchStatementRequest {
return append(statements, dynamodbtypes.BatchStatementRequest{ return append(statements, dynamodbtypes.BatchStatementRequest{
Statement: aws.String(fmt.Sprintf("INSERT INTO %q VALUE {'k':?, 'o':?, 'l':?}", im.table)), Statement: aws.String(fmt.Sprintf("INSERT INTO %q VALUE {'k':?, 'o':?, 'l':?}", im.table)),
ConsistentRead: aws.Bool(true), ConsistentRead: aws.Bool(true),
@ -475,16 +475,16 @@ func (im *DynamoDBRegistry) appendInsert(statements []dynamodbtypes.BatchStateme
&dynamodbtypes.AttributeValueMemberS{ &dynamodbtypes.AttributeValueMemberS{
Value: im.ownerID, Value: im.ownerID,
}, },
toDynamoLabels(new), toDynamoLabels(newL),
}, },
}) })
} }
func (im *DynamoDBRegistry) appendUpdate(statements []dynamodbtypes.BatchStatementRequest, key endpoint.EndpointKey, old endpoint.Labels, new endpoint.Labels) []dynamodbtypes.BatchStatementRequest { func (im *DynamoDBRegistry) appendUpdate(statements []dynamodbtypes.BatchStatementRequest, key endpoint.EndpointKey, old endpoint.Labels, newE endpoint.Labels) []dynamodbtypes.BatchStatementRequest {
if len(old) == len(new) { if len(old) == len(newE) {
equal := true equal := true
for k, v := range old { for k, v := range old {
if newV, exists := new[k]; !exists || v != newV { if newV, exists := newE[k]; !exists || v != newV {
equal = false equal = false
break break
} }
@ -497,7 +497,7 @@ func (im *DynamoDBRegistry) appendUpdate(statements []dynamodbtypes.BatchStateme
return append(statements, dynamodbtypes.BatchStatementRequest{ return append(statements, dynamodbtypes.BatchStatementRequest{
Statement: aws.String(fmt.Sprintf("UPDATE %q SET \"l\"=? WHERE \"k\"=?", im.table)), Statement: aws.String(fmt.Sprintf("UPDATE %q SET \"l\"=? WHERE \"k\"=?", im.table)),
Parameters: []dynamodbtypes.AttributeValue{ Parameters: []dynamodbtypes.AttributeValue{
toDynamoLabels(new), toDynamoLabels(newE),
toDynamoKey(key), toDynamoKey(key),
}, },
}) })

View File

@ -1821,7 +1821,7 @@ func TestTXTRegistryRecordsWithEmptyTargets(t *testing.T) {
}) })
r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false) r, _ := NewTXTRegistry(p, "", "", "owner", time.Hour, "", []string{}, []string{}, false, nil, false)
b := testutils.LogsToBuffer(log.ErrorLevel, t) hook := testutils.LogsUnderTestWithLogLevel(log.ErrorLevel, t)
records, err := r.Records(ctx) records, err := r.Records(ctx)
require.NoError(t, err) require.NoError(t, err)
@ -1835,5 +1835,6 @@ func TestTXTRegistryRecordsWithEmptyTargets(t *testing.T) {
} }
assert.True(t, testutils.SameEndpoints(records, expectedRecords)) assert.True(t, testutils.SameEndpoints(records, expectedRecords))
assert.Contains(t, b.String(), "TXT record has no targets empty-targets.test-zone.example.org")
testutils.TestHelperLogContains("TXT record has no targets empty-targets.test-zone.example.org", hook, t)
} }

View File

@ -116,7 +116,7 @@ function main() {
helm_unittest helm_unittest
;; ;;
--helm-template) --helm-template)
helm_unittest helm_template
;; ;;
-d|--diff) -d|--diff)
diff_schema diff_schema

View File

@ -15,9 +15,9 @@
# limitations under the License. # limitations under the License.
# renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools # renovate: datasource=github-releases depName=kubernetes-sigs/controller-tools
CONTROLLER_TOOLS_GENERATOR_VERSION=v0.15.0 CONTROLLER_TOOLS_GENERATOR_VERSION=v0.17.2
# renovate: datasource=github-releases depName=golangci/golangci-lint # renovate: datasource=github-releases depName=golangci/golangci-lint
GOLANG_CI_LINTER_VERSION=v2.0.2 GOLANG_CI_LINTER_VERSION=v2.1.6
# Execute # Execute
# scripts/install-tools.sh # scripts/install-tools.sh

View File

@ -18,12 +18,12 @@ package source
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"sort" "sort"
"strings" "strings"
ambassador "github.com/datawire/ambassador/pkg/api/getambassador.io/v2" ambassador "github.com/datawire/ambassador/pkg/api/getambassador.io/v2"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -97,7 +97,7 @@ func NewAmbassadorHostSource(
uc, err := newUnstructuredConverter() uc, err := newUnstructuredConverter()
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to setup Unstructured Converter") return nil, fmt.Errorf("failed to setup Unstructured Converter: %w", err)
} }
return &ambassadorHostSource{ return &ambassadorHostSource{
@ -138,7 +138,7 @@ func (sc *ambassadorHostSource) Endpoints(ctx context.Context) ([]*endpoint.Endp
// Filter Ambassador Hosts // Filter Ambassador Hosts
ambassadorHosts, err = sc.filterByAnnotations(ambassadorHosts) ambassadorHosts, err = sc.filterByAnnotations(ambassadorHosts)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to filter Ambassador Hosts by annotation") return nil, fmt.Errorf("failed to filter Ambassador Hosts by annotation: %w", err)
} }
var endpoints []*endpoint.Endpoint var endpoints []*endpoint.Endpoint
@ -260,7 +260,7 @@ func parseAmbLoadBalancerService(service string) (namespace, name string, err er
} }
// If we got here, this string is simply ill-formatted. Return an error. // If we got here, this string is simply ill-formatted. Return an error.
return "", "", errors.New(fmt.Sprintf("invalid external-dns service: %s", service)) return "", "", fmt.Errorf("invalid external-dns service: %s", service)
} }
func (sc *ambassadorHostSource) AddEventHandler(ctx context.Context, handler func()) { func (sc *ambassadorHostSource) AddEventHandler(ctx context.Context, handler func()) {
@ -311,9 +311,8 @@ func (sc *ambassadorHostSource) filterByAnnotations(ambassadorHosts []*ambassado
// Return a filtered list of Ambassador Hosts // Return a filtered list of Ambassador Hosts
filteredList := []*ambassador.Host{} filteredList := []*ambassador.Host{}
for _, host := range ambassadorHosts { for _, host := range ambassadorHosts {
annotations := labels.Set(host.Annotations)
// include Ambassador Host if its annotations match the annotation filter // include Ambassador Host if its annotations match the annotation filter
if selector.Matches(annotations) { if selector.Matches(labels.Set(host.Annotations)) {
filteredList = append(filteredList, host) filteredList = append(filteredList, host)
} }
} }

View File

@ -69,6 +69,11 @@ func ProviderSpecificAnnotations(annotations map[string]string) (endpoint.Provid
Name: CloudflareProxiedKey, Name: CloudflareProxiedKey,
Value: v, Value: v,
}) })
} else if strings.Contains(k, CloudflareRegionKey) {
providerSpecificAnnotations = append(providerSpecificAnnotations, endpoint.ProviderSpecificProperty{
Name: CloudflareRegionKey,
Value: v,
})
} }
} }
} }

View File

@ -125,6 +125,41 @@ func TestGetProviderSpecificCloudflareAnnotations(t *testing.T) {
}) })
} }
for _, tc := range []struct {
title string
annotations map[string]string
expectedKey string
expectedValue string
}{
{
title: "Cloudflare region key annotation is set correctly",
annotations: map[string]string{CloudflareRegionKey: "us"},
expectedKey: CloudflareRegionKey,
expectedValue: "us",
},
{
title: "Cloudflare region key annotation among another annotations is set correctly",
annotations: map[string]string{
"random annotation 1": "random value 1",
CloudflareRegionKey: "us",
"random annotation 2": "random value 2",
},
expectedKey: CloudflareRegionKey,
expectedValue: "us",
},
} {
t.Run(tc.title, func(t *testing.T) {
providerSpecificAnnotations, _ := ProviderSpecificAnnotations(tc.annotations)
for _, providerSpecificAnnotation := range providerSpecificAnnotations {
if providerSpecificAnnotation.Name == tc.expectedKey {
assert.Equal(t, tc.expectedValue, providerSpecificAnnotation.Value)
return
}
}
t.Errorf("Cloudflare provider specific annotation %s is not set correctly to %v", tc.expectedKey, tc.expectedValue)
})
}
for _, tc := range []struct { for _, tc := range []struct {
title string title string
annotations map[string]string annotations map[string]string

View File

@ -18,11 +18,11 @@ package source
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"sort" "sort"
"text/template" "text/template"
"github.com/pkg/errors"
projectcontour "github.com/projectcontour/contour/apis/projectcontour/v1" projectcontour "github.com/projectcontour/contour/apis/projectcontour/v1"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -33,6 +33,8 @@ import (
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"sigs.k8s.io/external-dns/source/fqdn"
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/source/annotations" "sigs.k8s.io/external-dns/source/annotations"
) )
@ -61,7 +63,7 @@ func NewContourHTTPProxySource(
combineFqdnAnnotation bool, combineFqdnAnnotation bool,
ignoreHostnameAnnotation bool, ignoreHostnameAnnotation bool,
) (Source, error) { ) (Source, error) {
tmpl, err := parseTemplate(fqdnTemplate) tmpl, err := fqdn.ParseTemplate(fqdnTemplate)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -88,7 +90,7 @@ func NewContourHTTPProxySource(
uc, err := NewUnstructuredConverter() uc, err := NewUnstructuredConverter()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to setup Unstructured Converter") return nil, fmt.Errorf("failed to setup Unstructured Converter: %w", err)
} }
return &httpProxySource{ return &httpProxySource{
@ -122,14 +124,14 @@ func (sc *httpProxySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint,
hpConverted := &projectcontour.HTTPProxy{} hpConverted := &projectcontour.HTTPProxy{}
err := sc.unstructuredConverter.scheme.Convert(unstructuredHP, hpConverted, nil) err := sc.unstructuredConverter.scheme.Convert(unstructuredHP, hpConverted, nil)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to convert to HTTPProxy") return nil, fmt.Errorf("failed to convert to HTTPProxy: %w", err)
} }
httpProxies = append(httpProxies, hpConverted) httpProxies = append(httpProxies, hpConverted)
} }
httpProxies, err = sc.filterByAnnotations(httpProxies) httpProxies, err = sc.filterByAnnotations(httpProxies)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to filter HTTPProxies") return nil, fmt.Errorf("failed to filter HTTPProxies: %w", err)
} }
endpoints := []*endpoint.Endpoint{} endpoints := []*endpoint.Endpoint{}
@ -145,14 +147,14 @@ func (sc *httpProxySource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint,
hpEndpoints, err := sc.endpointsFromHTTPProxy(hp) hpEndpoints, err := sc.endpointsFromHTTPProxy(hp)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to get endpoints from HTTPProxy") return nil, fmt.Errorf("failed to get endpoints from HTTPProxy: %w", err)
} }
// apply template if fqdn is missing on HTTPProxy // apply template if fqdn is missing on HTTPProxy
if (sc.combineFQDNAnnotation || len(hpEndpoints) == 0) && sc.fqdnTemplate != nil { if (sc.combineFQDNAnnotation || len(hpEndpoints) == 0) && sc.fqdnTemplate != nil {
tmplEndpoints, err := sc.endpointsFromTemplate(hp) tmplEndpoints, err := sc.endpointsFromTemplate(hp)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to get endpoints from template") return nil, fmt.Errorf("failed to get endpoints from template: %w", err)
} }
if sc.combineFQDNAnnotation { if sc.combineFQDNAnnotation {

View File

@ -18,11 +18,11 @@ package source
import ( import (
"context" "context"
"errors"
"testing" "testing"
fakeDynamic "k8s.io/client-go/dynamic/fake" fakeDynamic "k8s.io/client-go/dynamic/fake"
"github.com/pkg/errors"
projectcontour "github.com/projectcontour/contour/apis/projectcontour/v1" projectcontour "github.com/projectcontour/contour/apis/projectcontour/v1"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

View File

@ -148,7 +148,7 @@ func (cs *crdSource) AddEventHandler(ctx context.Context, handler func()) {
AddFunc: func(obj interface{}) { AddFunc: func(obj interface{}) {
handler() handler()
}, },
UpdateFunc: func(old interface{}, new interface{}) { UpdateFunc: func(old interface{}, newI interface{}) {
handler() handler()
}, },
DeleteFunc: func(obj interface{}) { DeleteFunc: func(obj interface{}) {
@ -287,11 +287,8 @@ func (cs *crdSource) filterByAnnotations(dnsendpoints *endpoint.DNSEndpointList)
filteredList := endpoint.DNSEndpointList{} filteredList := endpoint.DNSEndpointList{}
for _, dnsendpoint := range dnsendpoints.Items { for _, dnsendpoint := range dnsendpoints.Items {
// convert the dnsendpoint' annotations to an equivalent label selector
annotations := labels.Set(dnsendpoint.Annotations)
// include dnsendpoint if its annotations match the selector // include dnsendpoint if its annotations match the selector
if selector.Matches(annotations) { if selector.Matches(labels.Set(dnsendpoint.Annotations)) {
filteredList.Items = append(filteredList.Items, dnsendpoint) filteredList.Items = append(filteredList.Items, dnsendpoint)
} }
} }

View File

@ -22,7 +22,8 @@ import (
"sigs.k8s.io/external-dns/endpoint" "sigs.k8s.io/external-dns/endpoint"
) )
func EndpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoint.TTL, providerSpecific endpoint.ProviderSpecific, setIdentifier string, resource string) []*endpoint.Endpoint { // endpointsForHostname returns the endpoint objects for each host-target combination.
func endpointsForHostname(hostname string, targets endpoint.Targets, ttl endpoint.TTL, providerSpecific endpoint.ProviderSpecific, setIdentifier string, resource string) []*endpoint.Endpoint {
var ( var (
endpoints []*endpoint.Endpoint endpoints []*endpoint.Endpoint
aTargets endpoint.Targets aTargets endpoint.Targets

Some files were not shown because too many files have changed in this diff Show More