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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,8 @@
# https://golangci-lint.run/usage/configuration/
version: "2"
linters:
default: none
enable:
enable: # golangci-lint help linters
- dogsled
- goprintffuncname
- govet
@ -13,6 +14,9 @@ linters:
- unconvert
- unused
- 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:
exhaustive:
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)
- [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
- [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)
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:
@scripts/install-tools.sh --golangci
@ -67,10 +63,11 @@ oas-lint:
.PHONY: 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
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
.PHONY: test
@ -201,3 +198,7 @@ helm-template:
helm-lint:
scripts/helm-tools.sh --schema
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]
### 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
### 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. |
| 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. |
| 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`. |
| 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`. |
@ -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/). |
| 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. |
| 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`. |
| 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
kind: CustomResourceDefinition
metadata:
name: dnsendpoints.externaldns.k8s.io
annotations:
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:
group: externaldns.k8s.io
names:
@ -13,90 +15,86 @@ spec:
singular: dnsendpoint
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: DNSEndpointSpec defines the desired state of DNSEndpoint
properties:
endpoints:
items:
description:
Endpoint is a high-level way of a connection between
a service and an IP
properties:
dnsName:
description: The hostname of the DNS record
- name: v1alpha1
schema:
openAPIV3Schema:
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: DNSEndpointSpec defines the desired state of DNSEndpoint
properties:
endpoints:
items:
description: Endpoint is a high-level way of a connection between
a service and an IP
properties:
dnsName:
description: The hostname of the DNS record
type: string
labels:
additionalProperties:
type: string
labels:
additionalProperties:
type: string
description: Labels stores labels defined for the Endpoint
description: Labels stores labels defined for the Endpoint
type: object
providerSpecific:
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
providerSpecific:
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: array
recordTTL:
description: TTL for the record
format: int64
type: integer
recordType:
description:
RecordType type of record, e.g. CNAME, A, AAAA,
SRV, TXT etc
type: array
recordTTL:
description: TTL for the record
format: int64
type: integer
recordType:
description: RecordType type of record, e.g. CNAME, A, AAAA,
SRV, TXT etc
type: string
setIdentifier:
description: Identifier to distinguish multiple records with
the same name and type (e.g. Route53 records with routing
policies other than 'simple')
type: string
targets:
description: The targets the DNS record points to
items:
type: string
setIdentifier:
description:
Identifier to distinguish multiple records with
the same name and type (e.g. Route53 records with routing
policies other than 'simple')
type: string
targets:
description: The targets the DNS record points to
items:
type: string
type: array
type: object
type: array
type: object
status:
description: DNSEndpointStatus defines the observed state of DNSEndpoint
properties:
observedGeneration:
description: The generation observed by the external-dns controller.
format: int64
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}
type: array
type: object
type: array
type: object
status:
description: DNSEndpointStatus defines the observed state of DNSEndpoint
properties:
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={{ . }}
{{- end }}
- --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 . $ }}
{{- end }}
{{- end }}
{{- end }}
ports:
- name: http
protocol: TCP

View File

@ -103,6 +103,60 @@ tests:
- --zone-id-filter=/hostedzone/Z00004
- --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
set:
txtPrefix: "test-prefix"

View File

@ -50,3 +50,15 @@ tests:
labelFilter: "mydomain.io/enable-dns-record in (true)"
asserts:
- 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": {
"type": "string"
},
"properties": {},
"type": [
"array",
"null"
"null",
"object"
],
"uniqueItems": true
},
@ -308,30 +310,51 @@
"livenessProbe": {
"properties": {
"failureThreshold": {
"type": "integer"
"type": [
"integer",
"null"
]
},
"httpGet": {
"properties": {
"path": {
"type": "string"
"type": [
"string",
"null"
]
},
"port": {
"type": "string"
"type": [
"integer",
"string"
]
}
},
"type": "object"
},
"initialDelaySeconds": {
"type": "integer"
"type": [
"integer",
"null"
]
},
"periodSeconds": {
"type": "integer"
"type": [
"integer",
"null"
]
},
"successThreshold": {
"type": "integer"
"type": [
"integer",
"null"
]
},
"timeoutSeconds": {
"type": "integer"
"type": [
"integer",
"null"
]
}
},
"type": "object"
@ -339,30 +362,51 @@
"readinessProbe": {
"properties": {
"failureThreshold": {
"type": "integer"
"type": [
"integer",
"null"
]
},
"httpGet": {
"properties": {
"path": {
"type": "string"
"type": [
"string",
"null"
]
},
"port": {
"type": "string"
"type": [
"integer",
"string"
]
}
},
"type": "object"
},
"initialDelaySeconds": {
"type": "integer"
"type": [
"integer",
"null"
]
},
"periodSeconds": {
"type": "integer"
"type": [
"integer",
"null"
]
},
"successThreshold": {
"type": "integer"
"type": [
"integer",
"null"
]
},
"timeoutSeconds": {
"type": "integer"
"type": [
"integer",
"null"
]
}
},
"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.
# Valid values are `txt`, `aws-sd`, `dynamodb` & `noop`.
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
# -- (string) Specify a prefix for the domain names of TXT records created for the `txt` registry.
# Mutually exclusive with `txtSuffix`.
@ -263,24 +263,24 @@ provider: # @schema type: [object, string];
# @default -- See _values.yaml_
livenessProbe:
httpGet:
path: /healthz
port: http-webhook
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 2
successThreshold: 1
path: /healthz # @schema type:[string, null]; default: null
port: http-webhook # @schema type:[integer,string]; default: string
initialDelaySeconds: 10 # @schema type:[integer, null]; default: null
periodSeconds: 10 # @schema type:[integer, null]; default: null
timeoutSeconds: 5 # @schema type:[integer, null]; default: null
failureThreshold: 2 # @schema type:[integer, null]; default: null
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.
# @default -- See _values.yaml_
readinessProbe:
httpGet:
path: /healthz
port: http-webhook
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
path: /healthz # @schema type:[string, null]; default: null
port: http-webhook # @schema type:[integer,string]; default: string
initialDelaySeconds: 5 # @schema type:[integer, null]; default: null
periodSeconds: 10 # @schema type:[integer, null]; default: null
timeoutSeconds: 5 # @schema type:[integer, null]; default: null
failureThreshold: 6 # @schema type:[integer, null]; default: null
successThreshold: 1 # @schema type:[integer, null]; default: null
service:
# -- Webhook exposed HTTP port for the service.
port: 8080
@ -296,7 +296,8 @@ provider: # @schema type: [object, string];
relabelings: []
# -- 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:
# -- If `true`, create a `Secret` to store sensitive provider configuration (**DEPRECATED**).

View File

@ -4,7 +4,7 @@ kind: CustomResourceDefinition
metadata:
annotations:
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
spec:
group: externaldns.k8s.io

View File

@ -18,6 +18,7 @@ package controller
import (
"bytes"
"context"
"fmt"
"net"
"net/http"
@ -29,10 +30,9 @@ import (
"testing"
"time"
"context"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
"sigs.k8s.io/external-dns/plan"
@ -219,18 +219,45 @@ func TestHandleSigterm(t *testing.T) {
}
}
func TestServeMetrics(t *testing.T) {
l, _ := net.Listen("tcp", ":0")
_ = l.Close()
_, port, _ := net.SplitHostPort(l.Addr().String())
func getRandomPort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return 0, err
}
go serveMetrics(fmt.Sprintf(":%s", port))
resp, err := http.Get(fmt.Sprintf("http://localhost:%s", port) + "/healthz")
assert.NoError(t, err)
l, err := net.ListenTCP("tcp", addr)
if err != nil {
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)
resp, err = http.Get(fmt.Sprintf("http://localhost:%s", port) + "/metrics")
assert.NoError(t, err)
resp, err = http.Get(fmt.Sprintf("http://%s/metrics", addresse))
require.NoError(t, err)
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 {
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.
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
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.
## 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?
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 |
| `--gloo-namespace=gloo-system` | The Gloo Proxy namespace; specify multiple times for multiple namespaces. (default: gloo-system) |
| `--skipper-routegroup-groupversion="zalando.org/v1"` | The resource version for skipper routegroup |
| `--source=source` | The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, pod, fake, connector, gateway-httproute, gateway-grpcroute, gateway-tlsroute, gateway-tcproute, gateway-udproute, istio-gateway, istio-virtualservice, cloudfoundry, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress, f5-virtualserver, 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) |
| `--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 |
| `--crd-source-apiversion="externaldns.k8s.io/v1alpha1"` | API version of the CRD for crd source, e.g. `externaldns.k8s.io/v1alpha1`, valid only when using crd source |
| `--crd-source-kind="DNSEndpoint"` | Kind of the CRD for the crd source in API group and version specified by crd-source-apiversion |
| `--service-type-filter=SERVICE-TYPE-FILTER` | The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName) |
| `--managed-record-types=A...` | Record types to manage; specify multiple times to include many; (default: A, AAAA, CNAME) (supported records: A, AAAA, CNAME, NS, SRV, TXT) |
| `--exclude-record-types=EXCLUDE-RECORD-TYPES` | Record types to exclude from management; specify multiple times to exclude many; (optional) |
| `--default-targets=DEFAULT-TARGETS` | Set globally default host/IP that will apply as a target instead of source addresses. Specify multiple times for multiple targets (optional) |
| `--target-net-filter=TARGET-NET-FILTER` | Limit possible targets by a net filter; specify multiple times for multiple possible nets (optional) |
| `--exclude-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) |
| `--[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-]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-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) |
@ -133,7 +133,7 @@
| `--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-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-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) |

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
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
```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
```

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
```
### 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
- 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
// +kubebuilder:object:generate=true
type Endpoint struct {
// The hostname of the DNS record
DNSName string `json:"dnsName,omitempty"`
@ -337,6 +338,7 @@ func FilterEndpointsByOwnerID(ownerID string, eps []*Endpoint) []*Endpoint {
}
// DNSEndpointSpec defines the desired state of DNSEndpoint
// +kubebuilder:object:generate=true
type DNSEndpointSpec struct {
Endpoints []*Endpoint `json:"endpoints,omitempty"`
}

View File

@ -1,28 +1,11 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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.
// Code generated by controller-gen. DO NOT EDIT.
package endpoint
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.
@ -32,7 +15,6 @@ func (in *DNSEndpoint) DeepCopyInto(out *DNSEndpoint) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
return
}
// 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) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]DNSEndpoint, len(*in))
@ -65,7 +47,6 @@ func (in *DNSEndpointList) DeepCopyInto(out *DNSEndpointList) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// 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
*out = make([]*Endpoint, len(*in))
for i := range *in {
if (*in)[i] == nil {
(*out)[i] = nil
} else {
(*out)[i] = new(Endpoint)
(*in)[i].DeepCopyInto((*out)[i])
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Endpoint)
(*in).DeepCopyInto(*out)
}
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSEndpointSpec.
@ -114,22 +93,6 @@ func (in *DNSEndpointSpec) DeepCopy() *DNSEndpointSpec {
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.
func (in *Endpoint) DeepCopyInto(out *Endpoint) {
*out = *in
@ -148,11 +111,8 @@ func (in *Endpoint) DeepCopyInto(out *Endpoint) {
if in.ProviderSpecific != nil {
in, out := &in.ProviderSpecific, &out.ProviderSpecific
*out = make(ProviderSpecific, len(*in))
for key, val := range *in {
(*out)[key] = val
}
copy(*out, *in)
}
return
}
// 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)
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
go 1.24.0
go 1.24.2
require (
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/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/privatedns/armprivatedns v1.3.0
github.com/F5Networks/k8s-bigip-ctlr/v2 v2.19.1
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.0
github.com/IBM/go-sdk-core/v5 v5.19.0
github.com/IBM/networking-go-sdk v0.51.3
github.com/Yamashou/gqlgenc v0.31.0
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.7.2
github.com/IBM/go-sdk-core/v5 v5.19.1
github.com/IBM/networking-go-sdk v0.51.4
github.com/Yamashou/gqlgenc v0.32.1
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/aliyun/alibaba-cloud-sdk-go v1.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/config v1.29.13
github.com/aws/aws-sdk-go-v2/credentials v1.17.66
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.18.9
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.42.1
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.0
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.2
github.com/aws/aws-sdk-go-v2/service/sts v1.33.18
github.com/aws/aws-sdk-go-v2/config v1.29.14
github.com/aws/aws-sdk-go-v2/credentials v1.17.67
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.19.0
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.43.1
github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1
github.com/aws/aws-sdk-go-v2/service/servicediscovery v1.35.4
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19
github.com/bodgit/tsig v1.2.2
github.com/cenkalti/backoff/v4 v4.3.0
github.com/civo/civogo v0.3.96
github.com/cenkalti/backoff/v5 v5.0.2
github.com/civo/civogo v0.4.1
github.com/cloudflare/cloudflare-go v0.115.0
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
github.com/datawire/ambassador v1.12.4
github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace
github.com/digitalocean/godo v1.142.0
github.com/digitalocean/godo v1.145.0
github.com/dnsimple/dnsimple-go v1.7.0
github.com/exoscale/egoscale v0.102.3
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
@ -47,19 +47,18 @@ require (
github.com/onsi/ginkgo v1.16.5
github.com/openshift/api v0.0.0-20230607130528-611114dca681
github.com/openshift/client-go v0.0.0-20230607134213-3cd0021bbee3
github.com/oracle/oci-go-sdk/v65 v65.88.1
github.com/oracle/oci-go-sdk/v65 v65.89.3
github.com/ovh/go-ovh v1.7.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/pluralsh/gqlclient v1.12.2
github.com/projectcontour/contour v1.30.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/sirupsen/logrus v1.9.3
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/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/ultradns/ultradns-sdk-go v1.3.7
go.etcd.io/etcd/client/v3 v3.5.21
@ -69,25 +68,25 @@ require (
golang.org/x/sync v0.13.0
golang.org/x/text v0.24.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
istio.io/api v1.25.1
istio.io/client-go v1.25.1
k8s.io/api v0.32.3
k8s.io/apimachinery v0.32.3
k8s.io/client-go v0.32.3
istio.io/api v1.25.2
istio.io/client-go v1.25.2
k8s.io/api v0.33.0
k8s.io/apimachinery v0.33.0
k8s.io/client-go v0.33.0
k8s.io/klog/v2 v2.130.1
sigs.k8s.io/gateway-api v1.2.1
sigs.k8s.io/gateway-api v1.3.0
)
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
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
github.com/99designs/gqlgen v0.17.61 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.0 // indirect
github.com/99designs/gqlgen v0.17.71 // 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/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/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // 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/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/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/endpoint-discovery v1.10.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-playground/locales v0.14.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/goccy/go-json v0.10.5 // indirect
github.com/gofrs/flock v0.8.1 // indirect
@ -131,9 +130,8 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // 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/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/s2a-go v0.1.9 // 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/josharian/intern v1.0.0 // 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/leodido/go-urn v1.4.0 // 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/peterhellberg/link v1.1.0 // 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/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
@ -180,10 +178,10 @@ require (
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sony/gobreaker v0.5.0 // 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/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/xhit/go-str2duration/v2 v2.1.0 // 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.mongodb.org/mongo-driver v1.17.2 // 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/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.uber.org/atomic v1.10.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/mod v0.23.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/term v0.31.0 // indirect
golang.org/x/tools v0.30.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
google.golang.org/grpc v1.71.0 // indirect
golang.org/x/tools v0.32.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 // indirect
google.golang.org/grpc v1.72.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // 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/yaml.v2 v2.4.0 // 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
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/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
)

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.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
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/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
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=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.lukeshu.com/go/libsystemd v0.5.3/go.mod h1:FfDoP0i92r4p5Vn4NCLxvjkd7rCOe6otPa4L6hZg9WM=
github.com/99designs/gqlgen v0.17.61 h1:vE7xLRC066n9wehgjeplILOWtwz75zbzcV2/Iv9i3pw=
github.com/99designs/gqlgen v0.17.61/go.mod h1:rFU1T3lhv/tPeAlww/DJ4ol2YxT/pPpue+xxPbkd3r4=
github.com/99designs/gqlgen v0.17.71 h1:6JdwweHlSMWGY+6VWY5ey0tO+sF8LckbUV0NmdOQi04=
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/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/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.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
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.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/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.0/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
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.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/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0=
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/go.mod h1:JwdtGjHFTmUM1zjzvvCotCCyP55S146IuVPOJZ7D/Jw=
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.0/go.mod h1:+fXHt9ZUw/AlSAHvQXkTcEMKXLVwSBvA7uReQXqDwmU=
github.com/IBM/go-sdk-core/v5 v5.19.0 h1:YN2S5JUvq/EwYulmcNFwgyYBxZhVWl9nkY22H7Hpghw=
github.com/IBM/go-sdk-core/v5 v5.19.0/go.mod h1:deZO1J5TSlU69bCnl/YV7nPxFZA2UEaup7cq/7ZTOgw=
github.com/IBM/networking-go-sdk v0.51.3 h1:GW2VLP7XVcAHin6eWnCNA9l3gsv5tnyjf5UgGwMA7hk=
github.com/IBM/networking-go-sdk v0.51.3/go.mod h1:T27XI2gtPjT7tW9nkHgrpBUNbmAc9OR41Z78Y493PoA=
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.2/go.mod h1:HulyrJLLc9FSZlwKQ9vu5Jq83thNlUfg1afonOdhrRA=
github.com/IBM/go-sdk-core/v5 v5.19.1 h1:sleVks1O4XjgF4YEGvyDh6PZbP6iZhlTPeDkQc8nWDs=
github.com/IBM/go-sdk-core/v5 v5.19.1/go.mod h1:Q3BYO6iDA2zweQPDGbNTtqft5tDcEpm6RTuqMlPcvbw=
github.com/IBM/networking-go-sdk v0.51.4 h1:rkbR+gUkksLKjNYL5YEWEAMv3qddR0mUkxObDMa4l/s=
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/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/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
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/sprig v2.17.1+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
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/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/Yamashou/gqlgenc v0.31.0 h1:t54Gi6Zx9Ub1oa/RvJ+DkOC1c2wdK1R4n7wyRBuQkyc=
github.com/Yamashou/gqlgenc v0.31.0/go.mod h1:0VNZtzXhyDofkNNZXXPw8LSLvi3vyG6LXo7XvcWv2X4=
github.com/Yamashou/gqlgenc v0.32.1 h1:EHs9//xQxXlyltkSFXM+fhO2rTXcWNw6FPKRJ6t+iQQ=
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/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=
@ -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/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.104 h1:/6jF6rhmKZzvZZhzFXWqATazSKL5OSv7p+8PfoT6MG0=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.104/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU=
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/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
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 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/config v1.29.13 h1:RgdPqWoE8nPpIekpVpDJsBckbqT4Liiaq9f35pbTh1Y=
github.com/aws/aws-sdk-go-v2/config v1.29.13/go.mod h1:NI28qs/IOUIRhsR7GQ/JdexoqRN9tDxkIrYZq0SOF44=
github.com/aws/aws-sdk-go-v2/credentials v1.17.66 h1:aKpEKaTy6n4CEJeYI1MNj97oSDLi4xro3UzQfwf5RWE=
github.com/aws/aws-sdk-go-v2/credentials v1.17.66/go.mod h1:xQ5SusDmHb/fy55wU0QqTy0yNfLqxzec59YcsRZB+rI=
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.18.9/go.mod h1:JlH7zEPanxEEBLAAnKBRNZz+nrxTTMVKO40P5+umoUQ=
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.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
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.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
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.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/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
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/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/service/dynamodb v1.42.1 h1:67oYHlAdIoWS65kdTKatf9o1eDNkR2wan6TlBdP3oe4=
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/dynamodbstreams v1.25.2 h1:D1Af/NlGfG2/8S3EY/hCUlvPcfu2UrX4+XaGeiFzJQM=
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/dynamodb v1.43.1 h1:YYjNTAyPL0425ECmq6Xm48NSXdT6hDVQmLOJZxyhNTM=
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.3 h1:GHC1WTF3ZBZy+gvz2qtYB6ttALVx35hlwc4IzOIUY7g=
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/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/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/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.0/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.2/go.mod h1:IbC8X3WZvsN+w48OrHBDUKcVnhhzO1YpXkCkFlr0qs8=
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.1/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y=
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.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/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/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.18/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
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.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/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
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/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/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
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/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.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/civo/civogo v0.3.96 h1:9R3yZS3B8B0oAqIlNDnMvsONk0mqZUkHREd0EH6HRIc=
github.com/civo/civogo v0.3.96/go.mod h1:LaEbkszc+9nXSh4YNG0sYXFGYqdQFmXXzQg0gESs2hc=
github.com/civo/civogo v0.4.1 h1:C+lwZ7hBqKy6eKy6qgviuselF0V5Z/um0x7X/eLEQ64=
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/digitalocean/godo v1.142.0 h1:OZwDccTSL7yrgkGIiPMcJ4DXD8tCpu3xfbEVHAIB8mw=
github.com/digitalocean/godo v1.142.0/go.mod h1:tYeiWY5ZXVpU48YaFv0M5irUFHXGorZpDNm7zzdWMzM=
github.com/digitalocean/godo v1.145.0 h1:xBhWr+vCBy7GsexCUsWC+dKhPAWBMRLazavvXwyPBp8=
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/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY=
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/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.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
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/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
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/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.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
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 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
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/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.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
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-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/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
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/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/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
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.3.0/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/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.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-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/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
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.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
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/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.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
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/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=
@ -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.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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
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.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
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.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
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.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
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 v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
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/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=
@ -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.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/oracle/oci-go-sdk/v65 v65.88.1 h1:Y9Y5jlQX8oVDe3UN+O4IcQnLN/aQmi4jR1/2RsXMN3M=
github.com/oracle/oci-go-sdk/v65 v65.88.1/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA=
github.com/oracle/oci-go-sdk/v65 v65.89.3 h1:KSUykb5Ou54jF4SeJNjBwcDg+umbAwcvT+xhrvNDog0=
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/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c=
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.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.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
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-20180712105110-5c3871d89910/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/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
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/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
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/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.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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.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.1140 h1:F/vuesfLQmJhBZTHYOktz8IVWTs1nHwZYh74/H64uEs=
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.1145/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/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.1132/go.mod h1:2xDMNe2ohV2hRBldVgv++eByAhj3vSMnh0xl9jRFiEU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/privatedns v1.0.1145 h1:K5N0Uxqm9kM7KU6DFBekCTKbldlXq6UD1ekOyXn4zEc=
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/go.mod h1:8RnM56yZTR7mYyUIvrDgXzdRaEyFIzqdEi7+um26Sv8=
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/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/vektah/gqlparser/v2 v2.5.20 h1:kPaWbhBntxoZPaNdBaIPT1Kh0i1b/onb5kXgEdP5JCo=
github.com/vektah/gqlparser/v2 v2.5.20/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM=
github.com/vektah/gqlparser/v2 v2.5.25 h1:FmWtFEa+invTIzWlWK6Vk7BVEZU/97QBzeI8Z1JjGt8=
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/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
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.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/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
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.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.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
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/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/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
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/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.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
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-20180904163835-0709b304e793/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.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.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
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-20180724234803-3673e40ba225/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.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.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
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-20191011141410-1b5146add898/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.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.228.0 h1:X2DJ/uoWGnY5obVjewbp8icSL5U4FzuCfy9OjbLSnLs=
google.golang.org/api v0.228.0/go.mod h1:wNvRS1Pbe8r4+IfBIniV8fwCpGwTrYa+kMUDiC5z5a4=
google.golang.org/api v0.231.0 h1:LbUD5FUl0C4qwia2bjXhCMH65yz1MLPzA/0OYEsYY7Q=
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.2.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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
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-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 h1:29cjnHVylHwTzH66WfFZqgSQgnxzvWE+jvBwpZCLRxY=
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 v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
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.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
istio.io/api v1.25.1 h1:l3JS6Rh4bH+6ox84hRvtsJsr7gMJLsoJG2BQZGbXRyc=
istio.io/api v1.25.1/go.mod h1:QFzEXv/IT582T0FHZVp1QoolvE4ws0zz/vVO55blmlE=
istio.io/client-go v1.25.1 h1:1Asibz5v2NBA6w4Sqa85NS7TkpEolZcPKAT5oUAXWi8=
istio.io/client-go v1.25.1/go.mod h1:Vap9OyHJMvvDegYoZczcNybS4wbPaTk+4bZcWMb8+vE=
istio.io/api v1.25.2 h1:FCRQy7iaTreKJdLemlQ1vRJEsf1soCHoTAuSf68w5I8=
istio.io/api v1.25.2/go.mod h1:QFzEXv/IT582T0FHZVp1QoolvE4ws0zz/vVO55blmlE=
istio.io/client-go v1.25.2 h1:faupTqeMD0PkuNTZdFlpxxT35jBSQapK5k+MNAkXvqA=
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.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4=
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
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.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio=
k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
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.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
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.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.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
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.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
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/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-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
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.4/go.mod h1:EzB+nfeUWk6fm6giXQ8P4Fayw3dsN+M7Wjy23mTRtB0=
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=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A=
sigs.k8s.io/controller-runtime v0.18.7 h1:WDnx8LTRY8Fn1j/7B+S/R9MeDjWNAzpDBoaSvMSrQME=
sigs.k8s.io/controller-runtime v0.18.7/go.mod h1:L9r3fUZhID7Q9eK9mseNskpaTg2n11f/tlb8odyzJ4Y=
sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=
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/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM=
sigs.k8s.io/gateway-api v1.2.1/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0=
sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M=
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/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
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/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.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
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.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=

View File

@ -17,33 +17,100 @@ limitations under the License.
package testutils
import (
"bytes"
"flag"
"strings"
"testing"
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:
//
// buf := LogsToBuffer(log.DebugLevel, t)
// hook := testutils.LogsUnderTestWithLogLevel(log.DebugLevel, t)
// ... 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()
buf := new(bytes.Buffer)
log.SetOutput(buf)
logger, hook := test.NewNullLogger()
log.AddHook(hook)
log.SetOutput(logger.Out)
log.SetLevel(level)
klog.SetOutput(buf)
flags := &flag.FlagSet{}
klog.InitFlags(flags)
// make sure klog doesn't write to stderr by default in tests
_ = flags.Set("logtostderr", "false")
_ = flags.Set("alsologtostderr", "false")
_ = flags.Set("stderrthreshold", "4")
return buf
return hook
}
// TestHelperLogContains verifies that a specific log message 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`.
//
// 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
- Monitoring: docs/monitoring/*
- MultiTarget: docs/proposal/multi-target.md
- NAT64: docs/nat64.md
- Rate Limits: docs/rate-limits.md
- TTL: docs/ttl.md
- NAT64: docs/advanced/nat64.md
- Rate Limits: docs/advanced/rate-limits.md
- TTL: docs/advanced/ttl.md
- FQDN Templating: docs/advanced/fqdn-templating.md
- Contributing:
- Kubernetes Contributions: CONTRIBUTING.md
- Release: docs/release.

View File

@ -217,167 +217,168 @@ type Config struct {
}
var defaultConfig = &Config{
APIServerURL: "",
KubeConfig: "",
RequestTimeout: time.Second * 30,
DefaultTargets: []string{},
GlooNamespaces: []string{"gloo-system"},
SkipperRouteGroupVersion: "zalando.org/v1",
Sources: nil,
Namespace: "",
AnnotationFilter: "",
LabelFilter: labels.Everything().String(),
IngressClassNames: nil,
FQDNTemplate: "",
CombineFQDNAndAnnotation: false,
IgnoreHostnameAnnotation: false,
IgnoreIngressTLSSpec: false,
IgnoreIngressRulesSpec: false,
GatewayName: "",
GatewayNamespace: "",
GatewayLabelFilter: "",
Compatibility: "",
PublishInternal: false,
PublishHostIP: false,
ExposeInternalIPV6: true,
ConnectorSourceServer: "localhost:8080",
Provider: "",
ProviderCacheTime: 0,
GoogleProject: "",
GoogleBatchChangeSize: 1000,
GoogleBatchChangeInterval: time.Second,
GoogleZoneVisibility: "",
DomainFilter: []string{},
ZoneIDFilter: []string{},
ExcludeDomains: []string{},
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",
AkamaiAccessToken: "",
AkamaiClientSecret: "",
AkamaiClientToken: "",
AkamaiEdgercPath: "",
AkamaiEdgercSection: "",
AkamaiServiceConsumerDomain: "",
AlibabaCloudConfigFile: "/etc/kubernetes/alibaba-cloud.json",
AnnotationFilter: "",
APIServerURL: "",
AWSAPIRetries: 3,
AWSAssumeRole: "",
AWSAssumeRoleExternalID: "",
AWSBatchChangeInterval: time.Second,
AWSBatchChangeSize: 1000,
AWSBatchChangeSizeBytes: 32000,
AWSBatchChangeSizeValues: 1000,
AWSDynamoDBRegion: "",
AWSDynamoDBTable: "external-dns",
AWSEvaluateTargetHealth: true,
AWSPreferCNAME: false,
AWSSDCreateTag: map[string]string{},
AWSSDServiceCleanup: false,
AWSZoneCacheDuration: 0 * time.Second,
AWSZoneMatchParent: false,
AWSZoneTagFilter: []string{},
AWSZoneType: "",
AzureConfigFile: "/etc/kubernetes/azure.json",
AzureResourceGroup: "",
AzureSubscriptionID: "",
AzureZonesCacheDuration: 0 * time.Second,
CFAPIEndpoint: "",
CFPassword: "",
CFUsername: "",
CloudflareCustomHostnamesCertificateAuthority: "google",
CloudflareCustomHostnames: false,
CloudflareCustomHostnamesMinTLSVersion: "1.0",
CloudflareDNSRecordsPerPage: 100,
CloudflareProxied: false,
CloudflareRegionKey: "earth",
CoreDNSPrefix: "/skydns/",
AkamaiServiceConsumerDomain: "",
AkamaiClientToken: "",
AkamaiClientSecret: "",
AkamaiAccessToken: "",
AkamaiEdgercSection: "",
AkamaiEdgercPath: "",
OCIConfigFile: "/etc/kubernetes/oci.yaml",
OCIZoneScope: "GLOBAL",
OCIZoneCacheDuration: 0 * time.Second,
InMemoryZones: []string{},
OVHEndpoint: "ovh-eu",
OVHApiRateLimit: 20,
OVHEnableCNAMERelative: false,
PDNSServer: "http://localhost:8081",
PDNSServerID: "localhost",
PDNSAPIKey: "",
PDNSSkipTLSVerify: false,
PodSourceDomain: "",
TLSCA: "",
TLSClientCert: "",
TLSClientCertKey: "",
Policy: "sync",
Registry: "txt",
TXTOwnerID: "default",
TXTPrefix: "",
TXTSuffix: "",
TXTCacheInterval: 0,
TXTWildcardReplacement: "",
MinEventSyncInterval: 5 * time.Second,
TXTEncryptEnabled: false,
TXTEncryptAESKey: "",
TXTNewFormatOnly: false,
Interval: time.Minute,
Once: false,
DryRun: false,
UpdateEvents: false,
LogFormat: "text",
MetricsAddress: ":7979",
LogLevel: logrus.InfoLevel.String(),
ExoscaleAPIEnvironment: "api",
ExoscaleAPIZone: "ch-gva-2",
ExoscaleAPIKey: "",
ExoscaleAPISecret: "",
CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1",
CRDSourceKind: "DNSEndpoint",
ServiceTypeFilter: []string{},
CFAPIEndpoint: "",
CFUsername: "",
CFPassword: "",
RFC2136Host: []string{""},
RFC2136Port: 0,
RFC2136Zone: []string{},
RFC2136Insecure: false,
RFC2136GSSTSIG: false,
RFC2136KerberosRealm: "",
RFC2136KerberosUsername: "",
RFC2136KerberosPassword: "",
RFC2136TSIGKeyName: "",
RFC2136TSIGSecret: "",
RFC2136TSIGSecretAlg: "",
RFC2136TAXFR: true,
RFC2136MinTTL: 0,
RFC2136BatchChangeSize: 50,
RFC2136UseTLS: false,
RFC2136LoadBalancingStrategy: "disabled",
RFC2136SkipTLSVerify: false,
NS1Endpoint: "",
NS1IgnoreSSL: false,
TransIPAccountName: "",
TransIPPrivateKeyFile: "",
DigitalOceanAPIPageSize: 50,
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
ExcludeDNSRecordTypes: []string{},
GoDaddyAPIKey: "",
GoDaddySecretKey: "",
GoDaddyTTL: 600,
GoDaddyOTE: false,
IBMCloudProxied: false,
IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json",
TencentCloudConfigFile: "/etc/kubernetes/tencent-cloud.json",
TencentCloudZoneType: "",
PiholeServer: "",
PiholePassword: "",
PiholeTLSInsecureSkipVerify: false,
PiholeApiVersion: "5",
PluralCluster: "",
PluralProvider: "",
WebhookProviderURL: "http://localhost:8888",
WebhookProviderReadTimeout: 5 * time.Second,
WebhookProviderWriteTimeout: 10 * time.Second,
WebhookServer: false,
TraefikDisableLegacy: false,
TraefikDisableNew: false,
NAT64Networks: []string{},
ExcludeUnschedulable: true,
CombineFQDNAndAnnotation: false,
Compatibility: "",
ConnectorSourceServer: "localhost:8080",
CoreDNSPrefix: "/skydns/",
CRDSourceAPIVersion: "externaldns.k8s.io/v1alpha1",
CRDSourceKind: "DNSEndpoint",
DefaultTargets: []string{},
DigitalOceanAPIPageSize: 50,
DomainFilter: []string{},
DryRun: false,
ExcludeDNSRecordTypes: []string{},
ExcludeDomains: []string{},
ExcludeTargetNets: []string{},
ExcludeUnschedulable: true,
ExoscaleAPIEnvironment: "api",
ExoscaleAPIKey: "",
ExoscaleAPISecret: "",
ExoscaleAPIZone: "ch-gva-2",
ExposeInternalIPV6: true,
FQDNTemplate: "",
GatewayLabelFilter: "",
GatewayName: "",
GatewayNamespace: "",
GlooNamespaces: []string{"gloo-system"},
GoDaddyAPIKey: "",
GoDaddyOTE: false,
GoDaddySecretKey: "",
GoDaddyTTL: 600,
GoogleBatchChangeInterval: time.Second,
GoogleBatchChangeSize: 1000,
GoogleProject: "",
GoogleZoneVisibility: "",
IBMCloudConfigFile: "/etc/kubernetes/ibmcloud.json",
IBMCloudProxied: false,
IgnoreHostnameAnnotation: false,
IgnoreIngressRulesSpec: false,
IgnoreIngressTLSSpec: false,
IngressClassNames: nil,
InMemoryZones: []string{},
Interval: time.Minute,
KubeConfig: "",
LabelFilter: labels.Everything().String(),
LogFormat: "text",
LogLevel: logrus.InfoLevel.String(),
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeAAAA, endpoint.RecordTypeCNAME},
MetricsAddress: ":7979",
MinEventSyncInterval: 5 * time.Second,
Namespace: "",
NAT64Networks: []string{},
NS1Endpoint: "",
NS1IgnoreSSL: false,
OCIConfigFile: "/etc/kubernetes/oci.yaml",
OCIZoneCacheDuration: 0 * time.Second,
OCIZoneScope: "GLOBAL",
Once: false,
OVHApiRateLimit: 20,
OVHEnableCNAMERelative: false,
OVHEndpoint: "ovh-eu",
PDNSAPIKey: "",
PDNSServer: "http://localhost:8081",
PDNSServerID: "localhost",
PDNSSkipTLSVerify: false,
PiholeApiVersion: "5",
PiholePassword: "",
PiholeServer: "",
PiholeTLSInsecureSkipVerify: false,
PluralCluster: "",
PluralProvider: "",
PodSourceDomain: "",
Policy: "sync",
Provider: "",
ProviderCacheTime: 0,
PublishHostIP: false,
PublishInternal: false,
RegexDomainExclusion: regexp.MustCompile(""),
RegexDomainFilter: regexp.MustCompile(""),
Registry: "txt",
RequestTimeout: time.Second * 30,
RFC2136BatchChangeSize: 50,
RFC2136GSSTSIG: false,
RFC2136Host: []string{""},
RFC2136Insecure: false,
RFC2136KerberosPassword: "",
RFC2136KerberosRealm: "",
RFC2136KerberosUsername: "",
RFC2136LoadBalancingStrategy: "disabled",
RFC2136MinTTL: 0,
RFC2136Port: 0,
RFC2136SkipTLSVerify: false,
RFC2136TAXFR: true,
RFC2136TSIGKeyName: "",
RFC2136TSIGSecret: "",
RFC2136TSIGSecretAlg: "",
RFC2136UseTLS: false,
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
@ -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)
// 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("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("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("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("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("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("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
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
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-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-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)

View File

@ -84,13 +84,13 @@ func TestMustRegister(t *testing.T) {
}
func TestUnsupportedMetricWarning(t *testing.T) {
buf := testutils.LogsToBuffer(log.WarnLevel, t)
hook := testutils.LogsUnderTestWithLogLevel(log.WarnLevel, t)
registry := NewMetricsRegister()
mockUnsupported := &MockMetric{FQDN: "unsupported_metric"}
registry.MustRegister(mockUnsupported)
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) {

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
// are trying to acquire same DNS name
// are trying to acquire the same DNS name
type ConflictResolver interface {
ResolveCreate(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 takes "minimal" (string comparison of Target) endpoint to acquire the DNS record
func (s PerResource) ResolveCreate(candidates []*endpoint.Endpoint) *endpoint.Endpoint {
var min *endpoint.Endpoint
var minE *endpoint.Endpoint
for _, ep := range candidates {
if min == nil || s.less(ep, min) {
min = ep
if minE == nil || s.less(ep, minE) {
minE = ep
}
}
return min
return minE
}
// 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
type Changes struct {
// Records that need to be created
Create []*endpoint.Endpoint
Create []*endpoint.Endpoint `json:"create,omitempty"`
// 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)
UpdateNew []*endpoint.Endpoint
UpdateNew []*endpoint.Endpoint `json:"updateNew,omitempty"`
// Records that need to be deleted
Delete []*endpoint.Endpoint
Delete []*endpoint.Endpoint `json:"delete,omitempty"`
}
// planKey is a key for a row in `planTable`.
@ -262,9 +262,11 @@ func (p *Plan) Calculate() *Plan {
}
plan := &Plan{
Current: p.Current,
Desired: p.Desired,
Changes: changes,
Current: p.Current,
Desired: p.Desired,
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},
}

View File

@ -17,6 +17,9 @@ limitations under the License.
package plan
import (
"bytes"
"encoding/json"
"strings"
"testing"
"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() {
current := []*endpoint.Endpoint{}
desired := []*endpoint.Endpoint{suite.fooV1Cname, suite.fooV2Cname, suite.bar127A}
@ -367,9 +412,22 @@ func (suite *PlanTestSuite) TestSyncSecondRoundWithProviderSpecificNoChange() {
}
changes := p.Calculate().Changes
if changes.HasChanges() {
suite.T().Fatal("test should not have changes")
suite.Assert().False(changes.HasChanges())
}
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() {

View File

@ -34,9 +34,9 @@ import (
const (
// Default Record TTL
edgeDNSRecordTTL = 600
maxUint = ^uint(0)
maxInt = int(maxUint >> 1)
defaultTTL = 600
maxUint = ^uint(0)
maxInt = int(maxUint >> 1)
)
// 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) {
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
if akamaiConfig.ServiceConsumerDomain == "" || akamaiConfig.ClientToken == "" || akamaiConfig.ClientSecret == "" || akamaiConfig.AccessToken == "" {
// 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
if err != nil {
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")
} else {
@ -360,7 +352,7 @@ func trimTxtRdata(rdata []string, rtype string) []string {
func ttlAsInt(src endpoint.TTL) int {
var temp interface{} = int64(src)
temp64 := temp.(int64)
var ttl = edgeDNSRecordTTL
var ttl = defaultTTL
if temp64 > 0 && temp64 <= int64(maxInt) {
ttl = int(temp64)
}

View File

@ -38,7 +38,7 @@ import (
)
const (
defaultAlibabaCloudRecordTTL = 600
defaultTTL = 600
defaultAlibabaCloudPrivateZoneRecordTTL = 60
defaultAlibabaCloudPageSize = 50
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 {
ttl1 := record.TTL
if ttl1 == defaultAlibabaCloudRecordTTL {
if ttl1 == defaultTTL {
ttl1 = 0
}
ttl2 := int64(endpoint.RecordTTL)
if ttl2 == defaultAlibabaCloudRecordTTL {
if ttl2 == defaultTTL {
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/pvtz"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
@ -223,18 +224,7 @@ func newTestAlibabaCloudProvider(private bool) *AlibabaCloudProvider {
cfg := alibabaCloudConfig{
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"})
return &AlibabaCloudProvider{
@ -256,8 +246,8 @@ func TestAlibabaCloudPrivateProvider_Records(t *testing.T) {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %++v", *endpoint)
for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *ep)
}
}
}
@ -271,8 +261,8 @@ func TestAlibabaCloudProvider_Records(t *testing.T) {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %++v", *endpoint)
for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *ep)
}
}
}
@ -282,7 +272,7 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
defaultTtlPlan := &endpoint.Endpoint{
DNSName: "ttl.container-service.top",
RecordType: "A",
RecordTTL: defaultAlibabaCloudRecordTTL,
RecordTTL: defaultTTL,
Targets: endpoint.NewTargets("4.3.2.1"),
}
changes := plan.Changes{
@ -313,7 +303,8 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
},
}
ctx := context.Background()
p.ApplyChanges(ctx, &changes)
err := p.ApplyChanges(ctx, &changes)
assert.NoError(t, err)
endpoints, err := p.Records(ctx)
if err != nil {
t.Errorf("Failed to get records: %v", err)
@ -321,8 +312,8 @@ func TestAlibabaCloudProvider_ApplyChanges(t *testing.T) {
if len(endpoints) != 3 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %++v", *endpoint)
for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *ep)
}
}
for _, ep := range endpoints {
@ -343,8 +334,8 @@ func TestAlibabaCloudProvider_Records_PrivateZone(t *testing.T) {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %++v", *endpoint)
for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *ep)
}
}
}
@ -378,7 +369,8 @@ func TestAlibabaCloudProvider_ApplyChanges_PrivateZone(t *testing.T) {
},
}
ctx := context.Background()
p.ApplyChanges(ctx, &changes)
err := p.ApplyChanges(ctx, &changes)
assert.NoError(t, err)
endpoints, err := p.Records(ctx)
if err != nil {
t.Errorf("Failed to get records: %v", err)
@ -386,8 +378,8 @@ func TestAlibabaCloudProvider_ApplyChanges_PrivateZone(t *testing.T) {
if len(endpoints) != 2 {
t.Errorf("Incorrect number of records: %d", len(endpoints))
}
for _, endpoint := range endpoints {
t.Logf("Endpoint for %++v", *endpoint)
for _, ep := range endpoints {
t.Logf("Endpoint for %++v", *ep)
}
}
}

View File

@ -39,7 +39,7 @@ import (
const (
defaultAWSProfile = "default"
recordTTL = 300
defaultTTL = 300
// 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.
// After a discussion with AWS representatives, clients should accept
@ -232,7 +232,7 @@ type profiledZone struct {
}
func (cs Route53Changes) Route53Changes() []route53types.Change {
ret := []route53types.Change{}
var ret []route53types.Change
for _, c := range cs {
ret = append(ret, c.Change)
}
@ -313,7 +313,7 @@ type AWSConfig struct {
// NewAWSProvider initializes a new AWS Route53 based Provider.
func NewAWSProvider(awsConfig AWSConfig, clients map[string]Route53API) (*AWSProvider, error) {
provider := &AWSProvider{
pr := &AWSProvider{
clients: clients,
domainFilter: awsConfig.DomainFilter,
zoneIDFilter: awsConfig.ZoneIDFilter,
@ -331,7 +331,7 @@ func NewAWSProvider(awsConfig AWSConfig, clients map[string]Route53API) (*AWSPro
failedChangesQueue: make(map[string]Route53Changes),
}
return provider, nil
return pr, nil
}
// 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 {
// Alias records don't have TTLs so provide the default to match the TXT generation
if ttl == 0 {
ttl = recordTTL
ttl = defaultTTL
}
ep := endpoint.
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.
func (p *AWSProvider) requiresDeleteCreate(old *endpoint.Endpoint, new *endpoint.Endpoint) bool {
// a change of record type
if old.RecordType != new.RecordType {
func (p *AWSProvider) requiresDeleteCreate(old *endpoint.Endpoint, newE *endpoint.Endpoint) bool {
// a change of a record type
if old.RecordType != newE.RecordType {
return true
}
// an ALIAS record change to/from an A
if old.RecordType == endpoint.RecordTypeA {
oldAlias, _ := old.GetProviderSpecificProperty(providerSpecificAlias)
newAlias, _ := new.GetProviderSpecificProperty(providerSpecificAlias)
newAlias, _ := newE.GetProviderSpecificProperty(providerSpecificAlias)
if oldAlias != newAlias {
return true
}
}
// a set identifier change
if old.SetIdentifier != new.SetIdentifier {
if old.SetIdentifier != newE.SetIdentifier {
return true
}
// 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,
providerSpecificFailover, providerSpecificGeolocationContinentCode, providerSpecificGeolocationCountryCode,
providerSpecificGeolocationSubdivisionCode} {
_, oldPolicy := old.GetProviderSpecificProperty(propType)
_, newPolicy := new.GetProviderSpecificProperty(propType)
_, newPolicy := newE.GetProviderSpecificProperty(propType)
if oldPolicy != newPolicy {
return true
}
@ -601,14 +601,14 @@ func (p *AWSProvider) createUpdateChanges(newEndpoints, oldEndpoints []*endpoint
var creates []*endpoint.Endpoint
var updates []*endpoint.Endpoint
for i, new := range newEndpoints {
old := oldEndpoints[i]
if p.requiresDeleteCreate(old, new) {
deletes = append(deletes, old)
creates = append(creates, new)
for i, newE := range newEndpoints {
oldE := oldEndpoints[i]
if p.requiresDeleteCreate(oldE, newE) {
deletes = append(deletes, oldE)
creates = append(creates, newE)
} else {
// 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 {
changes := make(Route53Changes, 0, len(endpoints))
for _, endpoint := range endpoints {
change := p.newChange(action, endpoint)
for _, ep := range endpoints {
change := p.newChange(action, ep)
changes = append(changes, change)
}
@ -804,8 +804,8 @@ func (p *AWSProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoi
if alias {
if ep.RecordTTL.IsConfigured() {
log.Debugf("Modifying endpoint: %v, setting ttl=%v", ep, recordTTL)
ep.RecordTTL = recordTTL
log.Debugf("Modifying endpoint: %v, setting ttl=%v", ep, defaultTTL)
ep.RecordTTL = defaultTTL
}
if prop, ok := ep.GetProviderSpecificProperty(providerSpecificEvaluateTargetHealth); ok {
if prop != "true" && prop != "false" {
@ -866,7 +866,7 @@ func (p *AWSProvider) newChange(action route53types.ChangeAction, ep *endpoint.E
change.sizeValues += 1
} else {
if !ep.RecordTTL.IsConfigured() {
change.ResourceRecordSet.TTL = aws.Int64(recordTTL)
change.ResourceRecordSet.TTL = aws.Int64(defaultTTL)
} else {
change.ResourceRecordSet.TTL = aws.Int64(int64(ep.RecordTTL))
}

View File

@ -101,7 +101,8 @@ func TestAWSZonesSecondRequestHitsTheCache(t *testing.T) {
ctx := context.Background()
_, err := provider.Zones(ctx)
assert.NoError(t, err)
b := testutils.LogsToBuffer(log.DebugLevel, t)
hook := testutils.LogsUnderTestWithLogLevel(log.DebugLevel, t)
_, _ = 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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
},
{
Name: aws.String("list-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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.")),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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.")),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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.")),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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.")),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
AliasTarget: &route53types.AliasTarget{
DNSName: aws.String("escape-codes.eu-central-1.elb.amazonaws.com."),
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.")),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
AliasTarget: &route53types.AliasTarget{
DNSName: aws.String("escape-codes.eu-central-1.elb.amazonaws.com."),
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."),
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")}},
},
{
Name: aws.String("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeTxt,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("random")}},
},
{
Name: aws.String("weight-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set-1"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}},
SetIdentifier: aws.String("test-set-2"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set-1"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}},
SetIdentifier: aws.String("test-set-2"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("test-set-1"),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("foo.example.com")}},
SetIdentifier: aws.String("test-set-1"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("4.3.2.1")}},
SetIdentifier: aws.String("test-set-2"),
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."),
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")}},
},
})
@ -613,32 +613,32 @@ func TestAWSRecords(t *testing.T) {
require.NoError(t, err)
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-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(recordTTL), "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-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-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.RecordTypeAAAA, endpoint.TTL(recordTTL), "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.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.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.RecordTypeAAAA, endpoint.TTL(recordTTL), "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.RecordTypeAAAA, endpoint.TTL(recordTTL), "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("prefix-*.wildcard.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeTXT, endpoint.TTL(recordTTL), "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(recordTTL), "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("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("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("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(recordTTL), "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("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.RecordTypeA, endpoint.TTL(recordTTL), "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("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(defaultTTL), "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(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(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(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(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(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(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(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(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(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(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(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(defaultTTL), "random"),
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(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(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(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(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(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(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(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(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(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(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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}},
},
{
Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}},
},
{
Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}},
},
{
Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
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")}},
},
{
Name: aws.String("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
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")}},
},
{
Name: aws.String("delete-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
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")}},
},
{
Name: aws.String("weighted-to-simple.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("weighted-to-simple"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
},
{
Name: aws.String("policy-change.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("policy-change"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("before"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"),
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."),
Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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.")),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1111")}},
},
{
Name: aws.String("update-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("simple-to-weighted"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("policy-change"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("after"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"),
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."),
Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("1.2.3.4")}},
SetIdentifier: aws.String("no-change"),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("2606:4700:4700::1001")}},
},
{
Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeAaaa,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
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")}},
},
{
Name: aws.String("create-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
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")}},
},
{
Name: aws.String("update-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
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")}},
},
{
Name: aws.String("update-test-multiple-aaaa.zone-2.ext-dns-test-2.teapot.zalan.do."),
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")}},
},
{
Name: aws.String("update-test-mx.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
},
{
Name: aws.String("delete-test.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.8.8")}},
},
{
Name: aws.String("update-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
ResourceRecords: []route53types.ResourceRecord{{Value: aws.String("8.8.4.4")}},
},
{
Name: aws.String("delete-test.zone-2.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeA,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeCname,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
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")}},
},
{
Name: aws.String("delete-test-multiple.zone-2.ext-dns-test-2.teapot.zalan.do."),
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")}},
},
{
Name: aws.String("update-test-mx.zone-1.ext-dns-test-2.teapot.zalan.do."),
Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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."),
Type: route53types.RRTypeMx,
TTL: aws.Int64(recordTTL),
TTL: aws.Int64(defaultTTL),
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++ {
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)
ep := endpoint.NewEndpointWithTTL(hostname, endpoint.RecordTypeA, endpoint.TTL(recordTTL), ip)
ep := endpoint.NewEndpointWithTTL(hostname, endpoint.RecordTypeA, endpoint.TTL(defaultTTL), ip)
endpoints = append(endpoints, ep)
}
}
@ -1491,7 +1491,7 @@ func TestAWSsubmitChangesError(t *testing.T) {
zones, err := provider.zones(ctx)
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})
require.Error(t, provider.submitChanges(ctx, cs, zones))
@ -1504,11 +1504,11 @@ func TestAWSsubmitChangesRetryOnError(t *testing.T) {
zones, err := provider.zones(ctx)
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")
ep2 := endpoint.NewEndpointWithTTL("fail.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "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")
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(defaultTTL), "1.0.0.2")
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{
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) {
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")
newRecordType := endpoint.NewEndpointWithTTL("recordType", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "bar").WithProviderSpecific(providerSpecificAlias, "false")
oldRecordType := endpoint.NewEndpointWithTTL("recordType", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8")
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.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")
newAtoAlias := endpoint.NewEndpointWithTTL("AtoAlias", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "bar.us-east-1.elb.amazonaws.com").WithProviderSpecific(providerSpecificAlias, "true")
oldAtoAlias := endpoint.NewEndpointWithTTL("AtoAlias", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "1.1.1.1")
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.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")
newPolicy := endpoint.NewEndpointWithTTL("policy", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8").WithSetIdentifier("nochange").WithProviderSpecific(providerSpecificWeight, "10")
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(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.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")
newSetIdentifier := endpoint.NewEndpointWithTTL("setIdentifier", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "8.8.8.8").WithSetIdentifier("new")
oldSetIdentifier := endpoint.NewEndpointWithTTL("setIdentifier", endpoint.RecordTypeA, endpoint.TTL(defaultTTL), "8.8.8.8").WithSetIdentifier("old")
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.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"
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"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/provider"

View File

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

View File

@ -35,7 +35,7 @@ import (
)
const (
azureRecordTTL = 300
defaultTTL = 300
)
// 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) {
var ttl int64 = azureRecordTTL
var ttl int64 = defaultTTL
if endpoint.RecordTTL.IsConfigured() {
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) {
var ttl int64 = azureRecordTTL
var ttl int64 = defaultTTL
if endpoint.RecordTTL.IsConfigured() {
ttl = int64(endpoint.RecordTTL)
}

View File

@ -50,7 +50,7 @@ func TestGetCloudConfiguration(t *testing.T) {
func TestOverrideConfiguration(t *testing.T) {
_, 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")
if err != nil {
t.Errorf("got unexpected err %v", err)

View File

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

View File

@ -44,20 +44,20 @@ const (
cloudFlareDelete = "DELETE"
// cloudFlareUpdate is a ChangeAction enum value
cloudFlareUpdate = "UPDATE"
// defaultCloudFlareRecordTTL 1 = automatic
defaultCloudFlareRecordTTL = 1
// defaultTTL 1 = automatic
defaultTTL = 1
)
// 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
// proxyEnabled is a pointer to a bool true showing the record should be proxied through cloudflare
var proxyEnabled *bool = boolPtr(true)
var (
// 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 {
Name string
Type string
@ -279,8 +279,8 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov
if err != nil {
return nil, fmt.Errorf("failed to initialize cloudflare provider: %w", err)
}
provider := &CloudFlareProvider{
// Client: config,
return &CloudFlareProvider{
Client: zoneService{config},
domainFilter: domainFilter,
zoneIDFilter: zoneIDFilter,
@ -289,13 +289,12 @@ func NewCloudFlareProvider(domainFilter endpoint.DomainFilter, zoneIDFilter prov
DryRun: dryRun,
DNSRecordsPerPage: dnsRecordsPerPage,
RegionKey: regionKey,
}
return provider, nil
}, nil
}
// Zones returns the list of hosted zones.
func (p *CloudFlareProvider) Zones(ctx context.Context) ([]cloudflare.Zone, error) {
result := []cloudflare.Zone{}
var result []cloudflare.Zone
// if there is a zoneIDfilter configured
// && 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
}
endpoints := []*endpoint.Endpoint{}
var endpoints []*endpoint.Endpoint
for _, zone := range zones {
records, err := p.listDNSRecordsWithAutoPagination(ctx, zone.ID)
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.
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 p.CustomHostnamesConfig.Enabled {
@ -425,7 +424,7 @@ func (p *CloudFlareProvider) submitCustomHostnameChanges(ctx context.Context, zo
failedChange := false
// return early if disabled
if !p.CustomHostnamesConfig.Enabled {
return !failedChange
return true
}
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
func (p *CloudFlareProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
adjustedEndpoints := []*endpoint.Endpoint{}
var adjustedEndpoints []*endpoint.Endpoint
for _, e := range endpoints {
proxied := shouldBeProxied(e, p.proxiedByDefault)
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 {
ttl := defaultCloudFlareRecordTTL
ttl := defaultTTL
proxied := shouldBeProxied(ep, p.proxiedByDefault)
if ep.RecordTTL.IsConfigured() {
@ -969,7 +968,7 @@ func getEndpointCustomHostnames(ep *endpoint.Endpoint) []string {
}
func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHostnamesMap) []*endpoint.Endpoint {
endpoints := []*endpoint.Endpoint{}
var endpoints []*endpoint.Endpoint
// group supported records by name and type
groups := map[string][]cloudflare.DNSRecord{}
@ -994,7 +993,7 @@ func groupByNameAndTypeWithCustomHostnames(records DNSRecordsMap, chs CustomHost
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 {
if len(records) == 0 {
return endpoints

View File

@ -51,6 +51,7 @@ type mockCloudFlareClient struct {
Records map[string]map[string]cloudflare.DNSRecord
Actions []MockAction
listZonesError error
zoneDetailsError error
listZonesContextError error
dnsRecordsError error
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) {
if m.zoneDetailsError != nil {
return cloudflare.Zone{}, m.zoneDetailsError
}
for id, zoneName := range m.Zones {
if zoneID == id {
return cloudflare.Zone{
@ -891,6 +896,24 @@ func TestCloudflareZones(t *testing.T) {
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) {
client := NewMockCloudFlareClient()
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) {
_ = os.Setenv("CF_API_TOKEN", "abc123def")
_, err := NewCloudFlareProvider(
endpoint.NewDomainFilter([]string{"bar.com"}),
provider.NewZoneIDFilter([]string{""}),
false,
true,
5000,
"",
CustomHostnamesConfig{Enabled: false})
if err != nil {
t.Errorf("should not fail, %s", err)
var err error
type EnvVar struct {
Key string
Value string
}
_ = os.Unsetenv("CF_API_TOKEN")
tokenFile := "/tmp/cf_api_token"
if err := os.WriteFile(tokenFile, []byte("abc123def"), 0o644); err != nil {
t.Errorf("failed to write token file, %s", err)
}
_ = os.Setenv("CF_API_TOKEN", tokenFile)
_, err = NewCloudFlareProvider(
endpoint.NewDomainFilter([]string{"bar.com"}),
provider.NewZoneIDFilter([]string{""}),
false,
true,
5000,
"",
CustomHostnamesConfig{Enabled: false})
if err != nil {
t.Errorf("should not fail, %s", err)
testCases := []struct {
Name string
Environment []EnvVar
ShouldFail bool
}{
{
Name: "use_api_token",
Environment: []EnvVar{
{Key: "CF_API_TOKEN", Value: "abc123def"},
},
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")
_ = os.Setenv("CF_API_KEY", "xxxxxxxxxxxxxxxxx")
_ = os.Setenv("CF_API_EMAIL", "test@test.com")
_, err = NewCloudFlareProvider(
endpoint.NewDomainFilter([]string{"bar.com"}),
provider.NewZoneIDFilter([]string{""}),
false,
true,
5000,
"",
CustomHostnamesConfig{Enabled: false})
if err != nil {
t.Errorf("should not fail, %s", err)
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
for _, env := range tc.Environment {
t.Setenv(env.Key, env.Value)
}
_, err = NewCloudFlareProvider(
endpoint.NewDomainFilter([]string{"bar.com"}),
provider.NewZoneIDFilter([]string{""}),
false,
true,
5000,
"",
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) {
changes := &plan.Changes{}
client := NewMockCloudFlareClient()
@ -1217,7 +1288,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
},
@ -1226,7 +1297,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -1244,14 +1315,14 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
{
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
},
@ -1260,7 +1331,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -1278,28 +1349,28 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
{
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
{
Name: "bar.de",
Type: endpoint.RecordTypeA,
Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
{
Name: "bar.de",
Type: endpoint.RecordTypeA,
Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
},
@ -1308,7 +1379,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -1321,7 +1392,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "bar.de",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -1339,21 +1410,21 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
{
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
{
Name: "bar.de",
Type: endpoint.RecordTypeA,
Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
},
@ -1362,7 +1433,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -1375,7 +1446,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "bar.de",
Targets: endpoint.Targets{"10.10.10.1"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -1393,21 +1464,21 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
{
Name: "foo.com",
Type: endpoint.RecordTypeA,
Content: "10.10.10.2",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
{
Name: "bar.de",
Type: "NOT SUPPORTED",
Content: "10.10.10.1",
TTL: defaultCloudFlareRecordTTL,
TTL: defaultTTL,
Proxied: proxyDisabled,
},
},
@ -1416,7 +1487,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
DNSName: "foo.com",
Targets: endpoint.Targets{"10.10.10.1", "10.10.10.2"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -1568,7 +1639,7 @@ func TestCloudflareComplexUpdate(t *testing.T) {
DNSName: "foobar.bar.com",
Targets: endpoint.Targets{"1.2.3.4", "2.3.4.5"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
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{
Client: client,
CustomHostnamesConfig: CustomHostnamesConfig{Enabled: true},
@ -1899,7 +1970,7 @@ func TestCloudflareLongRecordsErrorLog(t *testing.T) {
if err != nil {
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
@ -1935,7 +2006,7 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
DNSName: "newerror.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
},
},
@ -1948,7 +2019,7 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
DNSName: "newerror-list-1.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
},
},
@ -1966,7 +2037,7 @@ func TestCloudflareDNSRecordsOperationsFail(t *testing.T) {
DNSName: "newerror-update-1.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
},
},
@ -2050,7 +2121,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "create.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2069,7 +2140,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "origin.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4", "2.3.4.5"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2082,7 +2153,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "another-origin.foo.bar.com",
Targets: endpoint.Targets{"3.4.5.6"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2101,7 +2172,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "c.foo.bar.com",
Targets: endpoint.Targets{"c.cname.foo.bar.com"},
RecordType: endpoint.RecordTypeCNAME,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
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",
},
RecordType: endpoint.RecordTypeTXT,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2141,7 +2212,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "fail.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2160,7 +2231,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "fail.list.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2184,7 +2255,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2203,7 +2274,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2222,7 +2293,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2241,7 +2312,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
},
},
@ -2254,7 +2325,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2285,7 +2356,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "nocustomhostname.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
},
},
@ -2298,7 +2369,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2311,7 +2382,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "txt.foo.bar.com",
Targets: endpoint.Targets{"value"},
RecordType: endpoint.RecordTypeTXT,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2332,7 +2403,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2353,7 +2424,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2376,7 +2447,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2399,7 +2470,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2420,7 +2491,7 @@ func TestCloudflareCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
},
},
@ -2509,7 +2580,7 @@ func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.11"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2522,14 +2593,14 @@ func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.12"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
},
{
DNSName: "c.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.13"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2548,14 +2619,14 @@ func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.11"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
},
{
DNSName: "b.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.12"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2568,7 +2639,7 @@ func TestCloudflareDisabledCustomHostnameOperations(t *testing.T) {
DNSName: "c.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.13"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2632,7 +2703,7 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
DNSName: "create.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2649,7 +2720,7 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
Name: "remove DNS record with unexpectedly missing custom hostname",
Endpoints: []*endpoint.Endpoint{},
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",
@ -2664,7 +2735,7 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
DNSName: "a.foo.bar.com",
Targets: endpoint.Targets{"1.2.3.4"},
RecordType: endpoint.RecordTypeA,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{
@ -2675,12 +2746,12 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
},
},
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 {
b := testutils.LogsToBuffer(log.InfoLevel, t)
hook := testutils.LogsUnderTestWithLogLevel(log.InfoLevel, t)
records, err := provider.Records(ctx)
if err != nil {
@ -2730,7 +2801,8 @@ func TestCloudflareCustomHostnameNotFoundOnRecordDeletion(t *testing.T) {
if e := checkFailed(tc.Name, err, false); !errors.Is(e, nil) {
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),
Targets: endpoint.Targets{fmt.Sprintf("cname-%d.foo.bar.com", i)},
RecordType: endpoint.RecordTypeCNAME,
RecordTTL: endpoint.TTL(defaultCloudFlareRecordTTL),
RecordTTL: endpoint.TTL(defaultTTL),
Labels: endpoint.Labels{},
ProviderSpecific: endpoint.ProviderSpecific{
{

View File

@ -92,7 +92,7 @@ type etcdClient struct {
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) {
ctx, cancel := context.WithTimeout(c.ctx, etcdTimeout)
defer cancel()

View File

@ -34,8 +34,8 @@ import (
)
const (
// digitalOceanRecordTTL is the default TTL value
digitalOceanRecordTTL = 300
// defaultTTL is the default TTL value
defaultTTL = 300
)
// 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() {
return int(ep.RecordTTL)
}
return digitalOceanRecordTTL
return defaultTTL
}
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) {
// Ensure that records at the root of the zone get `@` as the name.
r1 := makeDomainEditRequest("example.com", "example.com", endpoint.RecordTypeA,
"1.2.3.4", digitalOceanRecordTTL)
"1.2.3.4", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeA,
Name: "@",
Data: "1.2.3.4",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
}, r1)
// Ensure the CNAME records have a `.` appended.
r2 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeCNAME,
"bar.example.com", digitalOceanRecordTTL)
"bar.example.com", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeCNAME,
Name: "foo",
Data: "bar.example.com.",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
}, r2)
// Ensure that CNAME records do not have an extra `.` appended if they already have a `.`
r3 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeCNAME,
"bar.example.com.", digitalOceanRecordTTL)
"bar.example.com.", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeCNAME,
Name: "foo",
Data: "bar.example.com.",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
}, r3)
// Ensure that custom TTLs can be set
@ -350,24 +350,24 @@ func TestDigitalOceanMakeDomainEditRequest(t *testing.T) {
// Ensure that MX records have `.` appended.
r5 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX,
"10 mx.example.com", digitalOceanRecordTTL)
"10 mx.example.com", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeMX,
Name: "foo",
Data: "mx.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
}, r5)
// Ensure that MX records do not have an extra `.` appended if they already have a `.`
r6 := makeDomainEditRequest("example.com", "foo.example.com", endpoint.RecordTypeMX,
"10 mx.example.com.", digitalOceanRecordTTL)
"10 mx.example.com.", defaultTTL)
assert.Equal(t, &godo.DomainRecordEditRequest{
Type: endpoint.RecordTypeMX,
Name: "foo",
Data: "mx.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
}, r6)
}
@ -420,7 +420,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "1.2.3.4",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
{
@ -429,7 +429,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
Name: "@",
Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
{
@ -439,7 +439,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
Type: endpoint.RecordTypeMX,
Priority: 10,
Data: "mx.example.com.",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
{
@ -448,7 +448,7 @@ func TestDigitalOceanProcessCreateActions(t *testing.T) {
Name: "@",
Type: endpoint.RecordTypeTXT,
Data: "SOME-TXT-TEXT",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
}
@ -466,21 +466,21 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "1.2.3.4",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
{
ID: 2,
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "5.6.7.8",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
{
ID: 3,
Name: "@",
Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
{
ID: 4,
@ -488,7 +488,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Type: endpoint.RecordTypeMX,
Data: "mx1.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
{
ID: 5,
@ -496,14 +496,14 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Type: endpoint.RecordTypeMX,
Data: "mx2.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
{
ID: 6,
Name: "@",
Type: endpoint.RecordTypeTXT,
Data: "SOME_TXTX_TEXT",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
}
@ -532,7 +532,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "10.11.12.13",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
{
@ -541,7 +541,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Name: "@",
Type: endpoint.RecordTypeCNAME,
Data: "bar.example.com.",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
{
@ -551,7 +551,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Type: endpoint.RecordTypeMX,
Data: "mx3.example.com.",
Priority: 10,
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
{
@ -560,7 +560,7 @@ func TestDigitalOceanProcessUpdateActions(t *testing.T) {
Name: "@",
Type: endpoint.RecordTypeTXT,
Data: "ANOTHER-TXT",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
}
@ -609,7 +609,7 @@ func TestDigitalOceanProcessDeleteActions(t *testing.T) {
Name: "foo",
Type: endpoint.RecordTypeA,
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.
{
@ -617,14 +617,14 @@ func TestDigitalOceanProcessDeleteActions(t *testing.T) {
Name: "foo",
Type: endpoint.RecordTypeA,
Data: "5.6.7.8",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
{
ID: 3,
Name: "@",
Type: endpoint.RecordTypeCNAME,
Data: "foo.example.com.",
TTL: digitalOceanRecordTTL,
TTL: defaultTTL,
},
},
}

View File

@ -33,7 +33,13 @@ import (
"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 {
service *dnsimple.IdentityService
@ -91,12 +97,6 @@ type dnsimpleChange struct {
ResourceRecordSet dnsimple.ZoneRecord
}
const (
dnsimpleCreate = "CREATE"
dnsimpleDelete = "DELETE"
dnsimpleUpdate = "UPDATE"
)
// NewDnsimpleProvider initializes a new Dnsimple based provider
func NewDnsimpleProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool) (provider.Provider, error) {
oauthToken := os.Getenv("DNSIMPLE_OAUTH")
@ -149,7 +149,7 @@ func ZonesFromZoneString(zonestring string) map[string]dnsimple.Zone {
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) {
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
func newDnsimpleChange(action string, e *endpoint.Endpoint) *dnsimpleChange {
ttl := dnsimpleRecordTTL
ttl := defaultTTL
if e.RecordTTL.IsConfigured() {
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 {
// Since Exoscale "Patches", we ignore UpdateOld
// Since Exoscale "Patches", we've ignored UpdateOld
// We leave this logging here for information
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 {
findMatch := func(template *endpoint.Endpoint) *endpoint.Endpoint {
for _, new := range updateNew {
if template.DNSName == new.DNSName &&
template.RecordType == new.RecordType {
return new
for _, record := range updateNew {
if template.DNSName == record.DNSName &&
template.RecordType == record.RecordType {
return record
}
}
return nil
@ -323,7 +323,7 @@ func merge(updateOld, updateNew []*endpoint.Endpoint) []*endpoint.Endpoint {
for _, old := range updateOld {
matchingNew := findMatch(old)
if matchingNew == nil {
// no match, shouldn't happen
// no match shouldn't happen
continue
}

View File

@ -33,7 +33,7 @@ const (
gandiCreate = "CREATE"
gandiDelete = "DELETE"
gandiUpdate = "UPDATE"
gandiTTL = 600
defaultTTL = 600
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 {
changes := make([]*GandiChanges, 0, len(endpoints))
ttl := gandiTTL
ttl := defaultTTL
for _, e := range endpoints {
if e.RecordTTL.IsConfigured() {
ttl = int(e.RecordTTL)

View File

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

View File

@ -19,8 +19,8 @@ package godaddy
import (
"context"
"encoding/json"
"errors"
"fmt"
"slices"
"strings"
log "github.com/sirupsen/logrus"
@ -32,10 +32,12 @@ import (
)
const (
gdMinimalTTL = 600
gdCreate = 0
gdReplace = 1
gdDelete = 2
defaultTTL = 600
gdCreate = 0
gdReplace = 1
gdDelete = 2
domainsURI = "/v1/domains?statuses=ACTIVE,PENDING_DNS_ACTIVE"
)
var actionNames = []string{
@ -44,11 +46,6 @@ var actionNames = []string{
"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 {
Patch(string, interface{}, interface{}) error
Post(string, interface{}, interface{}) error
@ -147,7 +144,7 @@ func NewGoDaddyProvider(ctx context.Context, domainFilter endpoint.DomainFilter,
return &GDProvider{
client: client,
domainFilter: domainFilter,
ttl: maxOf(gdMinimalTTL, ttl),
ttl: maxOf(defaultTTL, ttl),
DryRun: dryRun,
}, nil
}
@ -294,14 +291,14 @@ func (p *GDProvider) groupByNameAndType(zoneRecords []gdRecords) []*endpoint.End
recordName = strings.TrimPrefix(fmt.Sprintf("%s.%s", records[0].Name, zoneName), ".")
}
endpoint := endpoint.NewEndpointWithTTL(
ep := endpoint.NewEndpointWithTTL(
recordName,
records[0].Type,
endpoint.TTL(records[0].TTL),
targets...,
)
endpoints = append(endpoints, endpoint)
endpoints = append(endpoints, ep)
}
}
@ -356,7 +353,7 @@ func (p *GDProvider) changeAllRecords(endpoints []gdEndpoint, zoneRecords []*gdR
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 {
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
for _, endpoints := range changes {
for _, endpoint := range endpoints {
count += len(endpoint.Targets)
for _, ep := range endpoints {
count += len(ep.Targets)
}
}
@ -593,15 +590,7 @@ func countTargets(p *plan.Changes) int {
}
func maxOf(vars ...int64) int64 {
max := vars[0]
for _, i := range vars {
if max < i {
max = i
}
}
return max
return slices.Max(vars)
}
func toString(obj interface{}) string {

View File

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

View File

@ -37,7 +37,7 @@ import (
)
const (
googleRecordTTL = 300
defaultTTL = 300
)
type managedZonesCreateCallInterface interface {
@ -154,7 +154,7 @@ func NewGoogleProvider(ctx context.Context, project string, domainFilter endpoin
zoneTypeFilter := provider.NewZoneTypeFilter(zoneVisibility)
provider := &GoogleProvider{
return &GoogleProvider{
project: project,
dryRun: dryRun,
batchChangeSize: batchChangeSize,
@ -166,9 +166,7 @@ func NewGoogleProvider(ctx context.Context, project string, domainFilter endpoin
managedZonesClient: managedZonesService{dnsClient.ManagedZones},
changesClient: changesService{dnsClient.Changes},
ctx: ctx,
}
return provider, nil
}, nil
}
// 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.
func (p *GoogleProvider) newFilteredRecords(endpoints []*endpoint.Endpoint) []*dns.ResourceRecordSet {
records := []*dns.ResourceRecordSet{}
var records []*dns.ResourceRecordSet
for _, endpoint := range endpoints {
if p.domainFilter.Match(endpoint.DNSName) {
records = append(records, newRecord(endpoint))
for _, ep := range endpoints {
if p.domainFilter.Match(ep.DNSName) {
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.
func batchChange(change *dns.Change, batchSize int) []*dns.Change {
changes := []*dns.Change{}
var changes []*dns.Change
if batchSize == 0 {
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
var ttl int64 = googleRecordTTL
var ttl int64 = defaultTTL
if ep.RecordTTL.IsConfigured() {
ttl = int64(ep.RecordTTL)
}

View File

@ -279,12 +279,12 @@ func TestGoogleRecords(t *testing.T) {
func TestGoogleRecordsFilter(t *testing.T) {
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("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "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("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "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("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"),
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, defaultTTL, "8.8.8.8"),
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, defaultTTL, "8.8.4.4"),
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, defaultTTL, "qux.elb.amazonaws.com"),
}
provider := newGoogleProvider(
@ -339,12 +339,12 @@ func TestGoogleApplyChanges(t *testing.T) {
provider.NewZoneIDFilter([]string{""}),
false,
[]*endpoint.Endpoint{
endpoint.NewEndpointWithTTL("update-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, 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, 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("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "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("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"),
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, defaultTTL, "bar.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,
@ -393,23 +393,23 @@ func TestGoogleApplyChanges(t *testing.T) {
require.NoError(t, err)
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("update-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "1.2.3.4"),
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, 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("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("update-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "baz.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, defaultTTL, "baz.elb.amazonaws.com"),
})
}
func TestGoogleApplyChangesDryRun(t *testing.T) {
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("delete-test.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "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("delete-test.zone-2.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeA, googleRecordTTL, "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("delete-test-cname.zone-1.ext-dns-test-2.gcp.zalan.do", endpoint.RecordTypeCNAME, googleRecordTTL, "qux.elb.amazonaws.com"),
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, defaultTTL, "8.8.8.8"),
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, defaultTTL, "8.8.4.4"),
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, 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)

View File

@ -61,8 +61,8 @@ const (
recordDelete = "DELETE"
// recordUpdate is a ChangeAction enum value
recordUpdate = "UPDATE"
// defaultPublicRecordTTL 1 = automatic
defaultPublicRecordTTL = 1
// defaultTTL 1 = automatic
defaultTTL = 1
proxyFilter = "ibmcloud-proxied"
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)
}
if c.Endpoint != "" {
service.publicZonesService.SetServiceURL(c.Endpoint)
_ = service.publicZonesService.SetServiceURL(c.Endpoint)
}
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)
}
if c.Endpoint != "" {
service.publicRecordsService.SetServiceURL(c.Endpoint)
_ = service.publicRecordsService.SetServiceURL(c.Endpoint)
}
case strings.Contains(crn.ServiceName, "dns-svcs"):
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)
}
if c.Endpoint != "" {
service.privateDNSService.SetServiceURL(c.Endpoint)
_ = service.privateDNSService.SetServiceURL(c.Endpoint)
}
default:
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
}
provider := &IBMCloudProvider{
return &IBMCloudProvider{
Client: client,
source: source,
domainFilter: domainFilter,
@ -331,8 +331,7 @@ func NewIBMCloudProvider(configFile string, domainFilter endpoint.DomainFilter,
privateZone: isPrivate,
proxiedByDefault: proxiedByDefault,
DryRun: dryRun,
}
return provider, nil
}, nil
}
// 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 {
log.Debugln("applying change...")
ibmcloudChanges := []*ibmcloudChange{}
for _, endpoint := range changes.Create {
for _, target := range endpoint.Targets {
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordCreate, endpoint, target))
for _, et := range changes.Create {
for _, target := range et.Targets {
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 _, target := range endpoint.Targets {
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordDelete, endpoint, target))
for _, et := range changes.Delete {
for _, target := range et.Targets {
ibmcloudChanges = append(ibmcloudChanges, p.newIBMCloudChange(recordDelete, et, target))
}
}
@ -680,15 +679,15 @@ func (p *IBMCloudProvider) privateRecords(ctx context.Context) ([]*endpoint.Endp
return nil, err
}
// Filter VPC annoation for private zone active
for _, source := range sources {
vpc = checkVPCAnnotation(source)
for _, src := range sources {
vpc = checkVPCAnnotation(src)
if len(vpc) > 0 {
log.Debugf("VPC found: %s", vpc)
break
}
}
endpoints := []*endpoint.Endpoint{}
var endpoints []*endpoint.Endpoint
for _, zone := range zones {
if len(vpc) > 0 && *zone.State == zoneStatePendingNetwork {
log.Debugf("active zone: %s", *zone.ID)
@ -730,7 +729,7 @@ GETRECORDS:
}
func (p *IBMCloudProvider) groupPrivateRecords(records []dnssvcsv1.ResourceRecord) []*endpoint.Endpoint {
endpoints := []*endpoint.Endpoint{}
var endpoints []*endpoint.Endpoint
// group supported records by name and type
groups := map[string][]dnssvcsv1.ResourceRecord{}
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 {
ttl := defaultPublicRecordTTL
ttl := defaultTTL
proxied := shouldBeProxied(endpoint, p.proxiedByDefault)
if endpoint.RecordTTL.IsConfigured() {
@ -984,7 +983,7 @@ func checkVPCAnnotation(endpoint *endpoint.Endpoint) string {
for _, v := range endpoint.ProviderSpecific {
if v.Name == vpcFilter {
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)
} else {
vpc = v.Value
@ -995,6 +994,7 @@ func checkVPCAnnotation(endpoint *endpoint.Endpoint) string {
return vpc
}
// TODO: could be shared function
func isNil(i interface{}) bool {
if i == nil {
return true
@ -1002,6 +1002,7 @@ func isNil(i interface{}) bool {
switch reflect.TypeOf(i).Kind() {
case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice:
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.SetUserAgent(fmt.Sprintf("%s linodego/%s", externaldns.UserAgent(), linodego.Version))
provider := &LinodeProvider{
return &LinodeProvider{
Client: &linodeClient,
domainFilter: domainFilter,
DryRun: dryRun,
}
return provider, nil
}, 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) {
zones, err := p.fetchZones(ctx)
if err != nil {

View File

@ -40,8 +40,8 @@ const (
ns1Delete = "DELETE"
// ns1Update is a ChangeAction enum value
ns1Update = "UPDATE"
// ns1DefaultTTL is the default ttl for ttls that are not set
ns1DefaultTTL = 10
// defaultTTL is the default ttl for ttls that are not set
defaultTTL = 10
)
// 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...)
provider := &NS1Provider{
return &NS1Provider{
client: NS1DomainService{apiClient},
domainFilter: config.DomainFilter,
zoneIDFilter: config.ZoneIDFilter,
minTTLSeconds: config.MinTTLSeconds,
}
return provider, nil
}, nil
}
// 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, " ")))
}
// set default ttl, but respect minTTLSeconds
ttl := ns1DefaultTTL
ttl := defaultTTL
if p.minTTLSeconds > ttl {
ttl = p.minTTLSeconds
}
@ -257,7 +256,7 @@ func (p *NS1Provider) zonesFiltered() ([]*dns.Zone, error) {
return nil, err
}
toReturn := []*dns.Zone{}
var toReturn []*dns.Zone
for _, z := range zones {
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 {
changes := make([]*ns1Change, 0, len(endpoints))
for _, endpoint := range endpoints {
for _, ep := range endpoints {
changes = append(changes, &ns1Change{
Action: action,
Endpoint: endpoint,
Endpoint: ep,
},
)
}

View File

@ -18,16 +18,16 @@ package oci
import (
"context"
"errors"
"fmt"
"os"
"strings"
"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/auth"
"github.com/oracle/oci-go-sdk/v65/dns"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"sigs.k8s.io/external-dns/endpoint"
@ -35,7 +35,7 @@ import (
"sigs.k8s.io/external-dns/provider"
)
const ociRecordTTL = 300
const defaultTTL = 300
// OCIAuthConfig holds connection parameters for the OCI API.
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)
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 == "" {
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.
var mergedEndpoints []*endpoint.Endpoint
for _, endpoints := range endpointsByNameType {
dnsName := endpoints[0].DNSName
recordType := endpoints[0].RecordType
recordTTL := endpoints[0].RecordTTL
for _, ep := range endpointsByNameType {
dnsName := ep[0].DNSName
recordType := ep[0].RecordType
recordTTL := ep[0].RecordTTL
targets := make([]string, len(endpoints))
for i, e := range endpoints {
targets := make([]string, len(ep))
for i, e := range ep {
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 {
ops := []dns.RecordOperation{}
var ops []dns.RecordOperation
for _, ep := range endpoints {
if ep == nil {
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))
}
endpoints := []*endpoint.Endpoint{}
var endpoints []*endpoint.Endpoint
for _, zone := range zones {
var page *string
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 {
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.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
func (p *OCIProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
adjustedEndpoints := []*endpoint.Endpoint{}
var adjustedEndpoints []*endpoint.Endpoint
for _, e := range endpoints {
// OCI DNS does not support the set-identifier attribute, so we remove it to avoid plan failure
if e.SetIdentifier != "" {
@ -370,7 +370,7 @@ func newRecordOperation(ep *endpoint.Endpoint, opType dns.RecordOperationOperati
}
rdata := strings.Join(targets, " ")
ttl := ociRecordTTL
ttl := defaultTTL
if ep.RecordTTL.IsConfigured() {
ttl = int(ep.RecordTTL)
}

View File

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

View File

@ -41,8 +41,8 @@ import (
)
const (
ovhDefaultTTL = 0
ovhCreate = iota
defaultTTL = 0
ovhCreate = iota
ovhDelete
ovhUpdate
)
@ -324,9 +324,9 @@ func (p *OVHProvider) change(ctx context.Context, change ovhChange) error {
return 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) {
@ -357,8 +357,8 @@ func (p *OVHProvider) zonesRecords(ctx context.Context) ([]string, []ovhRecord,
}
func (p *OVHProvider) zones(ctx context.Context) ([]string, error) {
zones := []string{}
filteredZones := []string{}
var zones []string
var filteredZones []string
p.apiRateLimiter.Take()
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
for _, records := range groups {
targets := []string{}
var targets []string
for _, record := range records {
targets = append(targets, record.Target)
}
endpoint := endpoint.NewEndpointWithTTL(
ep := endpoint.NewEndpointWithTTL(
strings.TrimPrefix(records[0].SubDomain+"."+records[0].Zone, "."),
records[0].FieldType,
endpoint.TTL(records[0].TTL),
targets...,
)
endpoints = append(endpoints, endpoint)
endpoints = append(endpoints, ep)
}
return endpoints
}
func (p OVHProvider) newOvhChangeCreateDelete(action int, endpoints []*endpoint.Endpoint, zone string, existingRecords []ovhRecord) ([]ovhChange, []ovhRecord) {
ovhChanges := []ovhChange{}
toDeleteIds := []int{}
var ovhChanges []ovhChange
var toDeleteIds []int
for _, e := range endpoints {
for _, target := range e.Targets {
@ -506,7 +506,7 @@ func (p OVHProvider) newOvhChangeCreateDelete(action int, endpoints []*endpoint.
FieldType: e.RecordType,
ovhRecordFieldUpdate: ovhRecordFieldUpdate{
SubDomain: convertDNSNameIntoSubDomain(e.DNSName, zone),
TTL: ovhDefaultTTL,
TTL: defaultTTL,
Target: target,
},
},
@ -579,13 +579,13 @@ func (p OVHProvider) newOvhChangeUpdate(endpointsOld []*endpoint.Endpoint, endpo
}
}
changes := []ovhChange{}
var changes []ovhChange
for id := range oldEndpointByTypeAndName {
oldRecords := slices.Clone(oldRecordsInZone[id])
endpointsNew := newEndpointByTypeAndName[id]
toInsertTarget := []string{}
var toInsertTarget []string
for _, target := range endpointsNew.Targets {
var toDelete = -1
@ -617,7 +617,7 @@ func (p OVHProvider) newOvhChangeUpdate(endpointsOld []*endpoint.Endpoint, endpo
if endpointsNew.RecordTTL.IsConfigured() {
record.TTL = int64(endpointsNew.RecordTTL)
} else {
record.TTL = ovhDefaultTTL
record.TTL = defaultTTL
}
change := ovhChange{
@ -634,7 +634,7 @@ func (p OVHProvider) newOvhChangeUpdate(endpointsOld []*endpoint.Endpoint, endpo
if len(toInsertTarget) > 0 {
for _, target := range toInsertTarget {
recordTTL := int64(ovhDefaultTTL)
recordTTL := int64(defaultTTL)
if endpointsNew.RecordTTL.IsConfigured() {
recordTTL = int64(endpointsNew.RecordTTL)
}
@ -680,6 +680,8 @@ func (c *ovhChange) String() string {
action = "update"
case ovhDelete:
action = "delete"
default:
action = "unknown"
}
if c.ID != 0 {

View File

@ -371,8 +371,8 @@ func TestOvhNewChange(t *testing.T) {
changes, _ := provider.newOvhChangeCreateDelete(ovhCreate, endpoints, "example.net", []ovhRecord{})
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: "ovh", TTL: ovhDefaultTTL, 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: "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: defaultTTL, Target: "ovh.example.net."}}}},
})
// Delete change
@ -386,9 +386,9 @@ func TestOvhNewChange(t *testing.T) {
}
changes, _ = provider.newOvhChangeCreateDelete(ovhDelete, endpoints, "example.net", records)
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: 43, 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: 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: defaultTTL, 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
@ -403,8 +403,8 @@ func TestOvhNewChange(t *testing.T) {
changes, _ = provider.newOvhChangeCreateDelete(ovhCreate, endpoints, "example.net", []ovhRecord{})
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: "ovh", TTL: ovhDefaultTTL, 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: "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: defaultTTL, Target: "ovh"}}}},
})
// 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{})
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: "ovh", TTL: ovhDefaultTTL, 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: "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: defaultTTL, Target: "ovh.example.com."}}}},
})
}

View File

@ -25,6 +25,7 @@ import (
"fmt"
"io"
"net/http"
"net/netip"
"net/url"
"strconv"
"strings"
@ -63,6 +64,7 @@ func newPiholeClientV6(cfg PiholeConfig) (piholeAPI, error) {
},
},
}
cl := instrumented_http.NewClient(httpClient, &instrumented_http.Callbacks{})
p := &piholeClientV6{
@ -114,6 +116,32 @@ func (p *piholeClientV6) getConfigValue(ctx context.Context, rtype string) ([]st
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) {
out := make([]*endpoint.Endpoint, 0)
results, err := p.getConfigValue(ctx, rtype)
@ -126,42 +154,39 @@ func (p *piholeClientV6) listRecords(ctx context.Context, rtype string) ([]*endp
return r == ' ' || r == ','
})
if len(recs) < 2 {
log.Warnf("skipping record %s: invalid format", rec)
log.Warnf("skipping record %s: invalid format received from PiHole", rec)
continue
}
var DNSName, Target string
var Ttl endpoint.TTL = 0
var Ttl = endpoint.TTL(0)
// A/AAAA record format is target(IP) DNSName
DNSName, Target = recs[1], recs[0]
switch rtype {
case endpoint.RecordTypeA:
if strings.Contains(Target, ":") {
//PiHole return A and AAAA records. Filter to only keep the A records
if !isValidIPv4(Target) {
continue
}
case endpoint.RecordTypeAAAA:
if strings.Contains(Target, ".") {
//PiHole return A and AAAA records. Filter to only keep the AAAA records
if !isValidIPv6(Target) {
continue
}
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]
if len(recs) == 3 { // TTL is present
// Parse string to int64 first
if ttlInt, err := strconv.ParseInt(recs[2], 10, 64); err == nil {
Ttl = endpoint.TTL(ttlInt)
} 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{
DNSName: DNSName,
Targets: []string{Target},
RecordTTL: Ttl,
RecordType: rtype,
})
out = append(out, endpoint.NewEndpointWithTTL(DNSName, rtype, Ttl, Target))
}
return out, nil
}
@ -375,7 +400,13 @@ func (p *piholeClientV6) do(req *http.Request) ([]byte, error) {
if err := json.Unmarshal(jRes, &apiError); err != nil {
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 != "" {
tryCount := 1
maxRetries := 3

View File

@ -29,6 +29,62 @@ import (
"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 {
t.Helper()
svr := httptest.NewServer(hdlr)
@ -137,7 +193,9 @@ func TestListRecordsV6(t *testing.T) {
"192.168.178.34 service3.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: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)
}
// 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
arecs, err := cl.listRecords(context.Background(), endpoint.RecordTypeA)
if err != nil {
t.Fatal(err)
}
if len(arecs) != 3 {
t.Fatal("Expected 3 A records returned, got:", 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"},
if len(arecs) != len(expected) {
t.Fatalf("Expected %d A records returned, got: %d", len(expected), len(arecs))
}
for idx, rec := range arecs {
if rec.DNSName != 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
arecs, err = cl.listRecords(context.Background(), endpoint.RecordTypeAAAA)
if err != nil {
t.Fatal(err)
}
if len(arecs) != 3 {
t.Fatal("Expected 3 AAAA records returned, got:", 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"},
if len(arecs) != len(expected) {
t.Fatalf("Expected %d AAAA records returned, got: %d", len(expected), len(arecs))
}
for idx, rec := range arecs {
if rec.DNSName != 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
cnamerecs, err := cl.listRecords(context.Background(), endpoint.RecordTypeCNAME)
if err != nil {
t.Fatal(err)
}
if len(cnamerecs) != 3 {
t.Fatal("Expected 3 CAME records returned, got:", 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"},
if len(cnamerecs) != len(expected) {
t.Fatalf("Expected %d CAME records returned, got: %d", len(expected), len(cnamerecs))
}
for idx, rec := range cnamerecs {
if rec.DNSName != 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")
}
}
func TestErrorsV6(t *testing.T) {
//Error test cases

View File

@ -45,29 +45,25 @@ type RecordChange struct {
func NewPluralProvider(cluster, provider string) (*PluralProvider, error) {
token := os.Getenv("PLURAL_ACCESS_TOKEN")
endpoint := os.Getenv("PLURAL_ENDPOINT")
if token == "" {
return nil, fmt.Errorf("no plural access token provided, you must set the PLURAL_ACCESS_TOKEN env var")
}
config := &Config{
Token: token,
Endpoint: endpoint,
Endpoint: os.Getenv("PLURAL_ENDPOINT"),
Cluster: cluster,
Provider: provider,
}
client, err := NewClient(config)
cl, err := NewClient(config)
if err != nil {
return nil, err
}
prov := &PluralProvider{
Client: client,
}
return prov, nil
return &PluralProvider{
Client: cl,
}, nil
}
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 {
var changes []*RecordChange
for _, endpoint := range diffs.Create {
changes = append(changes, makeChange(CreateAction, endpoint.Targets, endpoint))
for _, ep := range diffs.Create {
changes = append(changes, makeChange(CreateAction, ep.Targets, ep))
}
for _, desired := range diffs.UpdateNew {

View File

@ -19,6 +19,7 @@ package rfc2136
import (
"context"
"crypto/tls"
"errors"
"fmt"
"math/rand"
"net"
@ -32,7 +33,6 @@ import (
"github.com/bodgit/tsig/gss"
"github.com/miekg/dns"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"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) {
secretAlgChecked, ok := tsigAlgs[secretAlg]
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
@ -307,7 +307,7 @@ func (r *rfc2136Provider) List() ([]dns.RR, error) {
for e := range env {
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")
} else {
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 {
// Find the zone for the PTR record
// zone := findMsgZone(&endpoint.Endpoint{DNSName: ip}, p.ptrZoneNames)
// Generate PTR notation record starting from the IP address
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 {
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) {
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 err := r.actions.SendMessage(z); err != nil {
log.Errorf("RFC2136 create record failed: %v", err)
errors = append(errors, err)
errs = append(errs, err)
continue
}
}
@ -436,7 +434,7 @@ func (r *rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Change
if len(z.Ns) > 0 {
if err := r.actions.SendMessage(z); err != nil {
log.Errorf("RFC2136 update record failed: %v", err)
errors = append(errors, err)
errs = append(errs, err)
continue
}
}
@ -473,15 +471,15 @@ func (r *rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Change
if len(z.Ns) > 0 {
if err := r.actions.SendMessage(z); err != nil {
log.Errorf("RFC2136 delete record failed: %v", err)
errors = append(errors, err)
errs = append(errs, err)
continue
}
}
}
}
if len(errors) > 0 {
return fmt.Errorf("RFC2136 had errors in one or more of its batches: %v", errors)
if len(errs) > 0 {
return fmt.Errorf("RFC2136 had errors in one or more of its batches: %v", errs)
}
return nil

View File

@ -34,7 +34,7 @@ import (
)
const (
scalewyRecordTTL uint32 = 300
defaultTTL uint32 = 300
scalewayDefaultPriority uint32 = 0
scalewayPriorityKey string = "scw/priority"
)
@ -110,7 +110,7 @@ func (p *ScalewayProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*e
for i := range endpoints {
eps[i] = endpoints[i]
if !eps[i].RecordTTL.IsConfigured() {
eps[i].RecordTTL = endpoint.TTL(scalewyRecordTTL)
eps[i].RecordTTL = endpoint.TTL(defaultTTL)
}
if _, ok := eps[i].GetProviderSpecificProperty(scalewayPriorityKey); !ok {
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 {
// no annotation results in a TTL of 0, default to 300 for consistency with other providers
ttl := scalewyRecordTTL
ttl := defaultTTL
if ep.RecordTTL.IsConfigured() {
ttl = uint32(ep.RecordTTL)
}

View File

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

View File

@ -34,7 +34,7 @@ import (
const (
// 60 seconds is the current minimal TTL for TransIP and will replace unconfigured
// TTL's for Endpoints
transipMinimalValidTTL = 60
defaultTTL = 60
)
// 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
// transipMinimalValidTTL
// defaultTTL
func getMinimalValidTTL(ep *endpoint.Endpoint) int {
// TTL cannot be lower than transipMinimalValidTTL
if ep.RecordTTL < transipMinimalValidTTL {
return transipMinimalValidTTL
// TTL cannot be lower than defaultTTL
if ep.RecordTTL < defaultTTL {
return defaultTTL
}
return int(ep.RecordTTL)

View File

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

View File

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

View File

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

View File

@ -24,9 +24,11 @@ import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
@ -35,8 +37,9 @@ import (
var records []*endpoint.Endpoint
type FakeWebhookProvider struct {
err error
domainFilter endpoint.DomainFilter
err error
domainFilter endpoint.DomainFilter
assertChanges func(*plan.Changes)
}
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
}
records = append(records, changes.Create...)
if p.assertChanges != nil {
p.assertChanges(changes)
}
return nil
}
@ -77,7 +83,7 @@ func TestMain(m *testing.M) {
}
func TestRecordsHandlerRecords(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/records", nil)
req := httptest.NewRequest(http.MethodGet, UrlRecords, nil)
w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{
@ -99,7 +105,7 @@ func TestRecordsHandlerRecords(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()
providerAPIServer := &WebhookServer{
@ -139,7 +145,7 @@ func TestRecordsHandlerApplyChangesWithValidRequest(t *testing.T) {
reader := bytes.NewReader(j)
req := httptest.NewRequest(http.MethodPost, "/applychanges", reader)
req := httptest.NewRequest(http.MethodPost, UrlApplyChanges, reader)
w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{
@ -165,7 +171,7 @@ func TestRecordsHandlerApplyChangesWithErrors(t *testing.T) {
reader := bytes.NewReader(j)
req := httptest.NewRequest(http.MethodPost, "/applychanges", reader)
req := httptest.NewRequest(http.MethodPost, UrlApplyChanges, reader)
w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{
@ -179,7 +185,7 @@ func TestRecordsHandlerApplyChangesWithErrors(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()
providerAPIServer := &WebhookServer{
@ -190,8 +196,43 @@ func TestRecordsHandlerWithWrongHTTPMethod(t *testing.T) {
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) {
req := httptest.NewRequest(http.MethodPost, "/adjustendpoints", nil)
req := httptest.NewRequest(http.MethodPost, UrlAdjustEndpoints, nil)
w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{
@ -201,7 +242,7 @@ func TestAdjustEndpointsHandlerWithInvalidRequest(t *testing.T) {
res := w.Result()
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)
res = w.Result()
@ -222,7 +263,7 @@ func TestAdjustEndpointsHandlerWithValidRequest(t *testing.T) {
require.NoError(t, err)
reader := bytes.NewReader(j)
req := httptest.NewRequest(http.MethodPost, "/adjustendpoints", reader)
req := httptest.NewRequest(http.MethodPost, UrlAdjustEndpoints, reader)
w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{
@ -248,7 +289,7 @@ func TestAdjustEndpointsHandlerWithError(t *testing.T) {
require.NoError(t, err)
reader := bytes.NewReader(j)
req := httptest.NewRequest(http.MethodPost, "/adjustendpoints", reader)
req := httptest.NewRequest(http.MethodPost, UrlAdjustEndpoints, reader)
w := httptest.NewRecorder()
providerAPIServer := &WebhookServer{

View File

@ -30,7 +30,7 @@ import (
"sigs.k8s.io/external-dns/provider"
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"
log "github.com/sirupsen/logrus"
)
@ -113,45 +113,30 @@ func NewWebhookProvider(u string) (*WebhookProvider, error) {
}
// negotiate API information
req, err := http.NewRequest("GET", u, nil)
req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil {
return nil, err
}
req.Header.Set(acceptHeader, webhookapi.MediaTypeFormatAndVersion)
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 {
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
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{}
if err := json.NewDecoder(resp.Body).Decode(&df); err != nil {
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{
client: client,
remoteServerURL: parsedURL,
@ -159,12 +144,28 @@ func NewWebhookProvider(u string) (*WebhookProvider, error) {
}, 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
func (p WebhookProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
recordsRequestsGauge.Gauge.Inc()
u := p.remoteServerURL.JoinPath("records").String()
req, err := http.NewRequest("GET", u, nil)
req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil {
recordsErrorsGauge.Gauge.Inc()
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
}
endpoints := []*endpoint.Endpoint{}
var endpoints []*endpoint.Endpoint
if err := json.NewDecoder(resp.Body).Decode(&endpoints); err != nil {
recordsErrorsGauge.Gauge.Inc()
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
func (p WebhookProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
func (p WebhookProvider) ApplyChanges(_ context.Context, changes *plan.Changes) error {
applyChangesRequestsGauge.Gauge.Inc()
u := p.remoteServerURL.JoinPath("records").String()
u := p.remoteServerURL.JoinPath(webhookapi.UrlRecords).String()
b := new(bytes.Buffer)
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
}
req, err := http.NewRequest("POST", u, b)
req, err := http.NewRequest(http.MethodPost, u, b)
if err != nil {
applyChangesErrorsGauge.Gauge.Inc()
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())
return err
}
defer resp.Body.Close()
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
// 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.
func (p WebhookProvider) AdjustEndpoints(e []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
adjustEndpointsRequestsGauge.Gauge.Inc()
endpoints := []*endpoint.Endpoint{}
u, err := url.JoinPath(p.remoteServerURL.String(), "adjustendpoints")
var endpoints []*endpoint.Endpoint
u, err := url.JoinPath(p.remoteServerURL.String(), webhookapi.UrlAdjustEndpoints)
if err != nil {
adjustEndpointsErrorsGauge.Gauge.Inc()
log.Debugf("Failed to join path, %s", err)
@ -259,7 +261,7 @@ func (p WebhookProvider) AdjustEndpoints(e []*endpoint.Endpoint) ([]*endpoint.En
return nil, err
}
req, err := http.NewRequest("POST", u, b)
req, err := http.NewRequest(http.MethodPost, u, b)
if err != nil {
adjustEndpointsErrorsGauge.Gauge.Inc()
log.Debugf("Failed to create new HTTP request, %s", err)

View File

@ -22,8 +22,11 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
@ -31,6 +34,55 @@ import (
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) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
@ -108,6 +160,68 @@ func TestRecordsWithErrors(t *testing.T) {
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) {
successfulApplyChanges := true
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)
}
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) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
@ -144,7 +301,7 @@ func TestAdjustEndpoints(t *testing.T) {
w.Write([]byte(`{}`))
return
}
require.Equal(t, "/adjustendpoints", r.URL.Path)
require.Equal(t, webhookapi.UrlAdjustEndpoints, r.URL.Path)
var endpoints []*endpoint.Endpoint
defer r.Body.Close()
@ -162,7 +319,6 @@ func TestAdjustEndpoints(t *testing.T) {
}
j, _ := json.Marshal(endpoints)
w.Write(j)
}))
defer svr.Close()
@ -197,7 +353,7 @@ func TestAdjustendpointsWithError(t *testing.T) {
w.Write([]byte(`{}`))
return
}
require.Equal(t, "/adjustendpoints", r.URL.Path)
require.Equal(t, webhookapi.UrlAdjustEndpoints, r.URL.Path)
w.WriteHeader(http.StatusInternalServerError)
}))
defer svr.Close()
@ -269,3 +425,108 @@ func TestApplyChangesWithProviderSpecificProperty(t *testing.T) {
})
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
}
// 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) {
name, err := idna.Lookup.ToUnicode(hostname)
if err != nil {
log.Warnf("Failed to convert hostname '%s' to its Unicode form: %v", hostname, err)
name = hostname
var name string
domain_labels := strings.Split(hostname, ".")
for i, label := range domain_labels {
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 {
if name == zoneName || strings.HasSuffix(name, "."+zoneName) {
if suitableZoneName == "" || len(zoneName) > len(suitableZoneName) {

View File

@ -30,11 +30,15 @@ func TestZoneIDName(t *testing.T) {
z.Add("123456", "qux.baz")
z.Add("654321", "foo.qux.baz")
z.Add("987654", "エイミー.みんな")
z.Add("123123", "_metadata.example.com")
z.Add("456456", "_metadata.エイミー.みんな")
assert.Equal(t, ZoneIDName{
"123456": "qux.baz",
"654321": "foo.qux.baz",
"987654": "エイミー.みんな",
"123123": "_metadata.example.com",
"456456": "_metadata.エイミー.みんな",
}, z)
// simple entry in a domain
@ -72,7 +76,8 @@ func TestZoneIDName(t *testing.T) {
assert.Equal(t, "エイミー.みんな", zoneName)
assert.Equal(t, "987654", zoneID)
b := testutils.LogsToBuffer(log.WarnLevel, t)
zoneID, zoneName = z.FindZone("???")
assert.Contains(t, b.String(), "level=warning msg=\"Failed to convert hostname '???' to its Unicode form: idna: disallowed rune U+003F\"")
hook := testutils.LogsUnderTestWithLogLevel(log.WarnLevel, t)
_, _ = z.FindZone("???")
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 {
return err
}
for i, endpoint := range filteredChanges.Create {
if endpoint.Key() == key {
log.Infof("Skipping endpoint %v because owner does not match", endpoint)
for i, ep := range filteredChanges.Create {
if ep.Key() == key {
log.Infof("Skipping endpoint %v because owner does not match", ep)
filteredChanges.Create = append(filteredChanges.Create[:i], filteredChanges.Create[i+1:]...)
// The dynamodb insertion failed; remove from our cache.
im.removeFromCache(endpoint)
im.removeFromCache(ep)
delete(im.labels, key)
return nil
}
@ -466,7 +466,7 @@ func toDynamoLabels(labels endpoint.Labels) dynamodbtypes.AttributeValue {
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{
Statement: aws.String(fmt.Sprintf("INSERT INTO %q VALUE {'k':?, 'o':?, 'l':?}", im.table)),
ConsistentRead: aws.Bool(true),
@ -475,16 +475,16 @@ func (im *DynamoDBRegistry) appendInsert(statements []dynamodbtypes.BatchStateme
&dynamodbtypes.AttributeValueMemberS{
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 {
if len(old) == len(new) {
func (im *DynamoDBRegistry) appendUpdate(statements []dynamodbtypes.BatchStatementRequest, key endpoint.EndpointKey, old endpoint.Labels, newE endpoint.Labels) []dynamodbtypes.BatchStatementRequest {
if len(old) == len(newE) {
equal := true
for k, v := range old {
if newV, exists := new[k]; !exists || v != newV {
if newV, exists := newE[k]; !exists || v != newV {
equal = false
break
}
@ -497,7 +497,7 @@ func (im *DynamoDBRegistry) appendUpdate(statements []dynamodbtypes.BatchStateme
return append(statements, dynamodbtypes.BatchStatementRequest{
Statement: aws.String(fmt.Sprintf("UPDATE %q SET \"l\"=? WHERE \"k\"=?", im.table)),
Parameters: []dynamodbtypes.AttributeValue{
toDynamoLabels(new),
toDynamoLabels(newE),
toDynamoKey(key),
},
})

View File

@ -1821,7 +1821,7 @@ func TestTXTRegistryRecordsWithEmptyTargets(t *testing.T) {
})
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)
require.NoError(t, err)
@ -1835,5 +1835,6 @@ func TestTXTRegistryRecordsWithEmptyTargets(t *testing.T) {
}
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-template)
helm_unittest
helm_template
;;
-d|--diff)
diff_schema

View File

@ -15,9 +15,9 @@
# limitations under the License.
# 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
GOLANG_CI_LINTER_VERSION=v2.0.2
GOLANG_CI_LINTER_VERSION=v2.1.6
# Execute
# scripts/install-tools.sh

View File

@ -18,12 +18,12 @@ package source
import (
"context"
"errors"
"fmt"
"sort"
"strings"
ambassador "github.com/datawire/ambassador/pkg/api/getambassador.io/v2"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -97,7 +97,7 @@ func NewAmbassadorHostSource(
uc, err := newUnstructuredConverter()
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{
@ -138,7 +138,7 @@ func (sc *ambassadorHostSource) Endpoints(ctx context.Context) ([]*endpoint.Endp
// Filter Ambassador Hosts
ambassadorHosts, err = sc.filterByAnnotations(ambassadorHosts)
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
@ -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.
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()) {
@ -311,9 +311,8 @@ func (sc *ambassadorHostSource) filterByAnnotations(ambassadorHosts []*ambassado
// Return a filtered list of Ambassador Hosts
filteredList := []*ambassador.Host{}
for _, host := range ambassadorHosts {
annotations := labels.Set(host.Annotations)
// 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)
}
}

View File

@ -69,6 +69,11 @@ func ProviderSpecificAnnotations(annotations map[string]string) (endpoint.Provid
Name: CloudflareProxiedKey,
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 {
title string
annotations map[string]string

View File

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

View File

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

View File

@ -148,7 +148,7 @@ func (cs *crdSource) AddEventHandler(ctx context.Context, handler func()) {
AddFunc: func(obj interface{}) {
handler()
},
UpdateFunc: func(old interface{}, new interface{}) {
UpdateFunc: func(old interface{}, newI interface{}) {
handler()
},
DeleteFunc: func(obj interface{}) {
@ -287,11 +287,8 @@ func (cs *crdSource) filterByAnnotations(dnsendpoints *endpoint.DNSEndpointList)
filteredList := endpoint.DNSEndpointList{}
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
if selector.Matches(annotations) {
if selector.Matches(labels.Set(dnsendpoint.Annotations)) {
filteredList.Items = append(filteredList.Items, dnsendpoint)
}
}

View File

@ -22,7 +22,8 @@ import (
"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 (
endpoints []*endpoint.Endpoint
aTargets endpoint.Targets

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